51 votes

Jetpack Compose LazyColumn fait défiler les éléments de manière programmatique

Existe-t-il un moyen de faire défiler par programme LazyColumn à un élément de la liste ? Je pensais que cela pouvait être fait en soulevant le LazyColumn argument state: LazyListState = rememberLazyListState() mais je n'ai aucune idée de la manière dont je peux changer cet état, par exemple en cas de clic sur un bouton.

94voto

Gabriele Mariotti Points 7243

Avec 1.0.0 (testé avec 1.0.0-beta08 ) LazyListState prend en charge la position de défilement via

Quelque chose comme :

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")
}

4voto

Dans Compose 1.0.0-alpha07 Il n'y a pas d'API publique, mais une API interne existe pour 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()
}

Peut-être que dans la prochaine version, nous pourrons voir les mises à jour.

2voto

Umesh Chhabra Points 250

Voici mon code qui rend les en-têtes, la liste et le défilement collants.

@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)
        }
    }
}

1voto

alekshandru Points 91

Essayer avec

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)
}

1voto

CHATAM_ Points 1

J'ai résolu le problème de 2 façons

dans un sens : J'ai choisi ce meilleur moyen

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

dans les deux sens :

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

Prograide.com

Prograide est une communauté de développeurs qui cherche à élargir la connaissance de la programmation au-delà de l'anglais.
Pour cela nous avons les plus grands doutes résolus en français et vous pouvez aussi poser vos propres questions ou résoudre celles des autres.

Powered by:

X