Jetpack Compose LazyColumn 以编程方式滚动到项目

Posted

技术标签:

【中文标题】Jetpack Compose LazyColumn 以编程方式滚动到项目【英文标题】:Jetpack Compose LazyColumn programmatically scroll to Item 【发布时间】:2021-03-11 21:16:01 【问题描述】:

有没有办法以编程方式将LazyColumn 滚动到列表中的某个项目?我认为可以通过提升LazyColumn 参数state: LazyListState = rememberLazyListState() 来完成,但我不知道如何改变这种状态,例如点击按钮。

【问题讨论】:

【参考方案1】:

1.0.x LazyListState支持滚动位置通过

scrollToItem() 函数,“立即”捕捉滚动位置, animateScrollToItem() 使用动画滚动

类似:

val listState = rememberLazyListState()
// Remember a CoroutineScope to be able to launch
val coroutineScope = rememberCoroutineScope()

LazyColumn(state = listState) 
    // ...


Button (
    onClick =  
        coroutineScope.launch 
            // Animate scroll to the 10th item
            listState.animateScrollToItem(index = 10)
        
    
)
    Text("Click")


  

【讨论】:

【参考方案2】:

在 Compose 1.0.0-alpha07 中,没有公共 API,但有一些内部 API 到 LazyListState#snapToItemIndex

/**
 * Instantly brings the item at [index] to the top of the viewport, offset by [scrollOffset]
 * pixels.
 *
 * Cancels the currently running scroll, if any, and suspends until the cancellation is
 * complete.
 *
 * @param index the data index to snap to
 * @param scrollOffset the number of pixels past the start of the item to snap to
 */
@OptIn(ExperimentalFoundationApi::class)
suspend fun snapToItemIndex(
    @IntRange(from = 0)
    index: Int,
    @IntRange(from = 0)
    scrollOffset: Int = 0
) = scrollableController.scroll 
    scrollPosition.update(
        index = DataIndex(index),
        scrollOffset = scrollOffset,
        // `true` will be replaced with the real value during the forceRemeasure() execution
        canScrollForward = true
    )
    remeasurement.forceRemeasure()

也许在即将发布的版本中,我们可以看到更新。

【讨论】:

【参考方案3】:

这是我制作粘性标题、列表和滚动的代码

@ExperimentalFoundationApi
@Composable
private fun ListView(data: YourClass)  

//this is to remember state, internal API also use the same
    val state = rememberLazyListState()

    LazyColumn(Modifier.fillMaxSize(), state) 
        itemsIndexed(items = data.list, itemContent =  index, dataItem ->
            ItemView(dataItem)// your row
        )
       // header after some data, according to your condition
            stickyHeader 
                ItemDecoration()// compose fun for sticky header 
            
// More items after header
            itemsIndexed(items = data.list2, itemContent =  index, dataItem ->
            ItemView(dataItem)// your row
        )
        

       // scroll to top
      // I am scrolling to top every time data changes, use accordingly
        CoroutineScope(Dispatchers.Main).launch 
            state.snapToItemIndex(0, 0)
        
    

【讨论】:

【参考方案4】:

试试

lazyListState.animateScrollToItem(lazyListState.firstVisibleItemIndex)


@Composable
fun CircularScrollList(
    value: Long,
    onValueChange: () -> Unit = 
) 
    val lazyListState = rememberLazyListState()
    val scope = rememberCoroutineScope()

    val items = CircularAdapter(listOf(0, 1, 2, 3, 4, 5, 6, 7, 8, 9))
    scope.launch  lazyListState.scrollToItem(items.midIndex()) 

    LazyColumn(
        modifier = Modifier
            .requiredHeight(height = 120.dp)
            .border(width = 1.dp, color = Color.Black),
        state = lazyListState,
    ) 
        items(items) 
            if (!lazyListState.isScrollInProgress) 
                scope.launch 
                    lazyListState.animateScrollToItem(lazyListState.firstVisibleItemIndex)
                
            
            Text(
                text = "$it",
                modifier = Modifier.requiredHeight(40.dp),
                style = TextStyle(fontSize = 30.sp)
            )
        
    


class CircularAdapter(
    private val content: List<Int>
) : List<Int> 
    fun midIndex(): Int = Int.MAX_VALUE / 2 + 6
    override val size: Int = Int.MAX_VALUE
    override fun contains(element: Int): Boolean = content.contains(element = element)
    override fun containsAll(elements: Collection<Int>): Boolean = content.containsAll(elements)
    override fun get(index: Int): Int = content[index % content.size]
    override fun indexOf(element: Int): Int = content.indexOf(element)
    override fun isEmpty(): Boolean = content.isEmpty()
    override fun iterator(): Iterator<Int> = content.iterator()
    override fun lastIndexOf(element: Int): Int = content.lastIndexOf(element)
    override fun listIterator(): ListIterator<Int> = content.listIterator()
    override fun listIterator(index: Int): ListIterator<Int> = content.listIterator(index)
    override fun subList(fromIndex: Int, toIndex: Int): List<Int> =
        content.subList(fromIndex, toIndex)

【讨论】:

【参考方案5】:

我已经解决了两种方法

一种方式:我选择了这种最好的方式

LaunchedEffect(true) 
  repeat(Int.MAX_VALUE) 
      delay(TIME_DELAY_BANNER)
      pagerState.animateScrollToPage(page = it % pagerState.pageCount)
  

两种方式:

var index = pagerState.currentPage
LaunchedEffect(true) 
            while (true) 
                delay(TIME_DELAY_BANNER)
                if (index == pagerState.pageCount) 
                    index = 0
                
      pagerState.animateScrollToPage(page = index++)
  

【讨论】:

以上是关于Jetpack Compose LazyColumn 以编程方式滚动到项目的主要内容,如果未能解决你的问题,请参考以下文章

Android Jetpack Compose学习—— Jetpack compose基础布局

Android Jetpack Compose学习—— Jetpack compose基础布局

Android Jetpack Compose学习—— Jetpack compose入门

Android Jetpack Compose学习—— Jetpack compose入门

jetpack compose 接收返回参数

什么是Jetpack Compose?带你走进Jetpack Compose~