如何使 TabRow 指示器与 HorizontalPager 同步滚动?

问题描述 投票:0回答:2

我正在尝试实现一件简单的事情:我有一个 ScrollableTabRow 和一个 HorizontalPager ,我想让 ScrollableTabRow 的指示器在我向左或向右拖动 HorizontalPager 时移动。

现在,只有当我更改页面时它才会移动。

它可以用 XML 开箱即用,但现在我觉得我必须这样做,但我不知道做什么。

代码:

      ScrollableTabRow(selectedTabIndex = selectedTabIndex.value,
            indicator = { positions ->
                TabRowDefaults.SecondaryIndicator(
                    Modifier.tabIndicatorOffset(positions[pagerState.currentPage])
                )
            },
            modifier = Modifier.fillMaxWidth(),
            containerColor = getTopBarColor()) {
            data.forEachIndexed { index, _ ->
                Tab(
                    text = { Text(tabTitle, maxLines = 1) },
                    selected = selectedTabIndex.value == index,
                    onClick = {
                        scope.launch {
                            pagerState.animateScrollToPage(index)
                        }
                    }
                )
            }
        }
        HorizontalPager(state = pagerState, modifier = Modifier
            .fillMaxWidth()
            .weight(1f)) { page ->
            // Show the page
        }
    ....
android android-jetpack-compose horizontal-pager android-scrollable-tabs
2个回答
0
投票

试试这个

@OptIn(ExperimentalMaterial3Api::class)
@Composable
fun TabIndicatorScope.SynchronizedIndicator(
    selectedTabIndex: Int,
    pagerOffset: Float,
    modifier: Modifier = Modifier,
    width: Dp = 24.dp,
    height: Dp = 3.dp,
    color: Color = Color.Unspecified,
    shape: Shape = RectangleShape
) {
    Spacer(
        modifier.tabIndicatorLayout { measurable: Measurable,
                                  constraints: Constraints,
                                  tabPositions: List<TabPosition> ->
                val floatingIndex = selectedTabIndex + pagerOffset

                val startTab =
                    tabPositions[floatingIndex
                        .toInt()
                        .coerceIn(0, tabPositions.lastIndex)]
                val endTab =
                    tabPositions[(floatingIndex.toInt() + 1).coerceIn(0, tabPositions.lastIndex)]

                val startCenter = (startTab.left + startTab.right) / 2
                val endCenter = (endTab.left + endTab.right) / 2
                val startIndicatorPosition = startCenter - startTab.contentWidth / 2
                val endIndicatorPosition = endCenter - endTab.contentWidth / 2
                val startIndicatorEndPosition = startCenter + startTab.contentWidth / 2
                val endIndicatorEndPosition = endCenter + endTab.contentWidth / 2

                val progress = floatingIndex - floatingIndex.toInt()

                val indicatorStart =
                    startIndicatorPosition + (endIndicatorPosition - startIndicatorPosition) * progress
                val indicatorEnd =
                    startIndicatorEndPosition + (endIndicatorEndPosition - startIndicatorEndPosition) * progress

                val placeable = measurable.measure(
                    constraints.copy(
                        maxWidth = (indicatorEnd - indicatorStart).roundToPx(),
                        minWidth = (indicatorEnd - indicatorStart).roundToPx()
                    )
                )

                layout(constraints.maxWidth, placeable.height) {
                    placeable.place(indicatorStart.roundToPx(), 0)
                }
            }
            .requiredHeight(height)
            .requiredWidth(width)
            .background(color = color, shape = shape)
    )
}

然后在你的代码中

val pager = rememberPagerState(initialPage = 0) { 4 }

PrimaryTabRow(
   selectedTabIndex = pager.currentPage,
   indicator = {
     SynchronizedIndicator(
        selectedTabIndex = pager.currentPage,
        pagerOffset = pager.currentPageOffsetFraction,
        width = Dp.Unspecified,
        height = 5.dp,
        color = Color.White,
        shape = RoundedCornerShape(topStart = 3.dp, topEnd = 3.dp)
     )
   }
) {
//Your tabs
}
//HorizontalPager

有关指标的更多信息位于此处


0
投票

您可以使用以下代码:

@Composable
fun TabbedPager() {

    val tabs = listOf("Videos", "Shorts", "Podcasts", "Courses", "Playlists", "Community")

    val coroutineScope = rememberCoroutineScope()
    val pagerState = rememberPagerState(
        initialPage = 0,
        pageCount = { tabs.size }
    )
    val selectedTabIndex by remember { derivedStateOf { pagerState.currentPage }}
    var previousTabIndex by remember { mutableIntStateOf(0) }
    var targetTabIndex by remember { mutableIntStateOf(0) }

    LaunchedEffect(pagerState.currentPageOffsetFraction) {
        val scrollFraction = pagerState.currentPageOffsetFraction
        if (scrollFraction > 0) {
            previousTabIndex = pagerState.currentPage
            targetTabIndex = previousTabIndex + 1
        }
        if (scrollFraction < 0) {
            previousTabIndex = pagerState.currentPage
            targetTabIndex = previousTabIndex - 1
        }
    }

    Scaffold(
        modifier = Modifier.fillMaxSize(),
        topBar = {
            ScrollableTabRow(
                selectedTabIndex = selectedTabIndex,
                edgePadding = 0.dp,
                indicator = { tabPositions ->
                    TabRowDefaults.SecondaryIndicator(
                        Modifier.smoothTabIndicatorOffset(
                            previousTabPosition = tabPositions[previousTabIndex],
                            newTabPosition = tabPositions[targetTabIndex],
                            swipeProgress = pagerState.currentPageOffsetFraction
                        )
                    )
                }
            ) {
                tabs.forEachIndexed { index, title ->
                    Tab(
                        text = { Text(title) },
                        selected = selectedTabIndex == index,
                        onClick = {
                            coroutineScope.launch {
                                pagerState.animateScrollToPage(index)
                            }
                        },
                    )
                }
            }
        }
    ) { innerPadding ->
        HorizontalPager(
            modifier = Modifier
                .padding(innerPadding)
                .fillMaxSize(),
            state = pagerState
        ) { pageIndex ->
            Column(
                modifier = Modifier.fillMaxSize(),
                verticalArrangement = Arrangement.Center,
                horizontalAlignment = Alignment.CenterHorizontally
            ) {
                Text("Page $pageIndex")
            }
        }
    }
}

fun Modifier.smoothTabIndicatorOffset(previousTabPosition: TabPosition, newTabPosition: TabPosition, swipeProgress: Float): Modifier =
    composed {
        val currentTabWidth = previousTabPosition.width + (newTabPosition.width - previousTabPosition.width) * abs(swipeProgress)
        val indicatorOffset = previousTabPosition.left + (newTabPosition.left - previousTabPosition.left) * abs(swipeProgress)
        fillMaxWidth()
            .wrapContentSize(Alignment.BottomStart)
            .offset { IntOffset(x = indicatorOffset.roundToPx(), y = 0) }
            .width(currentTabWidth)
    }

输出:

Screen Recording

© www.soinside.com 2019 - 2024. All rights reserved.