android Jetpack组成:模仿下拉菜单中的spinner.setSelection()

izj3ouym  于 2023-10-14  发布在  Android
关注(0)|答案(3)|浏览(141)

用例是,您在一个菜单中有10或100个项目,菜单选项有一定的顺序-如数字值或单词的字母列表-并且连续进行选择。
当用户重新打开菜单时,你希望它在与上次选择相同的区域打开,这样你就不会从“car”跳到“apple”,而是从“car”跳到“cat”。或者,如果他们只是选择查看订单号358,他们可以快速查看订单号359。
使用视图,您可以创建一个Spinner,并将所有项目放在ArrayAdapter中,然后调用spinner.setSelection()直接滚动到所需的索引。
DropdownMenu没有HorizontalPagerscrollToPage()那样的东西。那么,有什么解决方案可以实现这一目标呢?
到目前为止,我已经尝试将verticalScroll()添加到DropdownMenu的修饰符中,并尝试使用scrollState进行算术运算。但是它在运行时崩溃,并错误地说组件具有无限高度,如果您尝试使用verticalScroll将可滚动组件(如LazyColumn)嵌套在Column中,则会出现相同的错误。

zzlelutf

zzlelutf1#

known issue
DropdownMenuhas its own垂直滚动修饰符,并且没有API来使用它。
在通过提供合适的API解决这个问题之前,我能想到的唯一解决方法是创建自己的视图--您可以参考DropdownMenu的源代码。

ki1q1bka

ki1q1bka2#

我会在这里发布一个更详细的答案,因为我不想用我上面的评论误导任何人。
如果您在Android Studio中,请单击鼠标悬停快速文档框上的三个点,然后选择“编辑源代码”,以在AndroidMenu.android.kt中打开DropdownMenu的源代码。然后观察到它使用了一个名为DropdownMenuItemContent的可组合对象。再次编辑源代码,您将进入Menu.kt。
你会看到这个:

@Composable
internal fun DropdownMenuContent(
...
...
...
{
        Column(
            modifier = modifier
                .padding(vertical = DropdownMenuVerticalPadding)
                .width(IntrinsicSize.Max)
                .verticalScroll(rememberScrollState()),//<-We want this
            content = content
        )
    }

因此,在您的自定义组合中,只需将rememberScrollState()替换为您最喜欢的ScrollState变量名。
然后将该引用一直链接到原始视图。

获取ScrollState的访问权限

@Composable 
fun MyCustomDropdownMenu(
    expanded:Boolean,
    scrollStateProvidedByTopParent:ScrollState,
    ...
    ...
   )
   {...}

@Composable
fun MyCustomDropdownMenuContent(
    scrollStateProvidedByTopParent:ScrollState,
    ...
    ...
    )
    {...}

//And now for your actual content

@Composable
fun TopParent(){
    val scrollStateProvidedByTopParent=rememberScrollState()
    val spinnerExpanded by remember{mutableStateOf(false)}
    ...
    ...
    Box{
       Row(modifier=Modifier.clickable(onClick={spinnerExpanded=!spinnerExpanded}))//<-More about this line in the sequel
    {
      Text("Options")
      Icon(imageVector = Icons.Filled.ArrowDropDown, contentDescription = "")
      
      MyCustomDropdownMenu(
      expanded = spinnerExpanded,
      scrollStateProvidedByTopParent=scrollStateProvidedByTopParent,
      onDismissRequest = { spinnerExpanded = false }) 
    {//your spinner content}

       }
    }
}

以上仅指定如何访问DropdownMenuScrollState。但是,一旦你有了ScrollState,你就必须做一些算术,以获得正确的滚动位置时,它打开。这里有一个方法似乎不错。

计算滚动距离

即使在显式地设置了菜单项的内容之后,如果我依赖这些值,距离也不会完全正确。因此,我在菜单项的Text中使用了onTextLayout回调函数,以便在渲染时获得真正的Text高度。然后我用这个值来计算。它看起来像这样:

@Composable
fun TopParent(){
    val scrollStateProvidedByTopParent=rememberScrollState()
    val spinnerExpanded by remember{mutableStateOf(false)}
    val chosenText:String by remember{mutableStateOf(myListOfSpinnerOptions[0])
    val height by remember{mutableStateOf(0)}
    val heightHasBeenChecked by remember{mutableStateOf(false)}
    val coroutineScope=rememberCoroutineScope()

    ...
    ...
    Box{
       Row(modifier=Modifier.clickable(onClick={spinnerExpanded=!spinnerExpanded
 coroutineScope.launch{scrollStateProvidedByTopParent.scrollTo(height*myListOfSpinnerOptions.indexOf[chosenText])}}))//<-This gets some arithmetic for scrolling distance
    {
      Text("Options")
      Icon(imageVector = Icons.Filled.ArrowDropDown, contentDescription = "")
          
      MyCustomDropdownMenu(
      expanded = spinnerExpanded,
      scrollStateProvidedByTopParent=scrollStateProvidedByTopParent,
      onDismissRequest = { spinnerExpanded = false }) {
        myListOfSpinnerOptions.forEach{option->  
        DropdownMenuItem(onClick={
            chosenText=option    
            spinnerExpanded=false
            }){
              Text(option,onTextLayout={layoutResult->
              if (!heightHasBeenChecked){
                 height=layoutResults.size.height
                 heightHasBeenChecked=true
                 }
              }
              )

              }
          }
      }  
   }
}
p8h8hvxi

p8h8hvxi3#

在Material 3版本1.2.0-alpha 02中,将scrollState参数添加到DropdownMenu()。
关于this sample from android.com
他们有一个使用下拉菜单滚动状态的例子。要在微调器中模拟“setSelection”,可以调用scrollState.scrollTo(xxx)
参考LaunchedEffect中的部分

@Composable
fun MyUI() {
    var expanded by remember { mutableStateOf(false) }
    val contextForToast = LocalContext.current.applicationContext
    val scrollState = rememberScrollState()

    Box(
        modifier = Modifier
            .fillMaxSize()
            .wrapContentSize(Alignment.Center),
        contentAlignment = Alignment.Center
    ) {
        // vertical 3 dots button
        IconButton(
            onClick = {
                expanded = true
            }
        ) {
            Icon(Icons.Default.MoreVert, contentDescription = "Open Menu")
        }

        DropdownMenu(
            expanded = expanded,
            onDismissRequest = { expanded = false },
            scrollState = scrollState
        ) {
            repeat(30) {
                DropdownMenuItem(
                    text = {
                        Text("Item $it")
                    },
                    onClick = {
                        Toast.makeText(contextForToast, "Item $it", Toast.LENGTH_SHORT).show()       
                        expanded = false
                    }
                )
            }
        }
    }

    LaunchedEffect(expanded) {
        if (expanded) {
            // scroll to the last item
            scrollState.scrollTo(scrollState.maxValue)
        }
    }
}

相关问题