使用 ScrollableTabRow 指示器显示选定的选项卡文本 - 意外行为

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

您可能已经知道,ScrollableTabRow 组件有一个名为“Indicator”的输入参数

当我使用此参数在所选项目周围绘制一个框时,输出与我的预期不同。出现这个问题是因为ScrollableTabRow在最后一步计算并绘制了指示器,导致所选选项卡的文本值无法显示。

为了解决这个问题,我可以轻松复制整个 ScrollableTabRow 并修改其行为。不过,似乎应该有更好的方法来处理这种情况。有人可以提供实现所需行为的指导吗?

我的期望:

我所看到的:

代码:



enum class TrackScreenTab(val text: UiText, val index: Int) {
    MUSIC(
        index = 0,
        text = UiText.StringResource(R.string.music))
    ,
    COMMENTS(
        index = 1,
        text = UiText.StringResource(R.string.comments))
    ,
    SIMILAR_MUSICS(
        index = 2,
        text = UiText.StringResource(R.string.simular_musics)
    )
}



@Composable
fun TrackTabRow(
    modifier: Modifier = Modifier,
    tabs: List<TrackScreenTab>,
    selectedTab: TrackScreenTab = TrackScreenTab.MUSIC,
    onTabSelected: (selectedTab: TrackScreenTab) -> Unit
) {
    ScrollableTabRow(
        selectedTabIndex = selectedTab.index,
        modifier = modifier,
        edgePadding = 16.dp,
        tabs = {
            tabs.forEachIndexed { index, tab ->
                Tab(
                    selected = selectedTab.index == index,
                    onClick = remember { { onTabSelected(tab) } },
                    text = {
                        Text(
                            text = tab.text.asString(),
                            style = MaterialTheme.typography.bodyLarge.copy(fontWeight = FontWeight.Bold),
                        )
                    },
                    modifier = Modifier
                        .padding(end = 8.dp)
                        .height(height = 36.dp)
                )
            }
        },
        divider = {},
        indicator = { tabPositions: List<TabPosition> ->
            Box(
                Modifier
                    .tabIndicatorOffset(tabPositions[selectedTab.index])
                    .fillMaxWidth()
                    .height(36.dp)
                    .background(
                        brush = Brush.verticalGradient(
                            colors = listOf(
                                Color(0XFF00B2FF),
                                Color(0XFF00F0FF)
                            )
                        ),
                        shape = RoundedCornerShape(16.dp)
                    )
            )
        },
        containerColor = Color.Transparent,
        contentColor = Color(0XFF9E9FB4)
    )
}
android android-jetpack-compose material-design android-jetpack jetpack
1个回答
0
投票

出现这个问题是因为 ScrollableTabRow 计算并绘制了 最后一步中的指示器,导致所选的文本值 选项卡不显示。

这是正确的,但由于它们都是兄弟姐妹,放置在同一个

layout()
中,使用
Modifier.zIndex(1f)
可以将选项卡放置在指示器上方。

tabs = {
    tabs.forEachIndexed { index, tab ->
        Tab(
            selected = selectedTab.index == index,
            onClick = remember { { onTabSelected(tab) } },
            text = {
                Text(
                    text = tab.text,
                    color = if (selectedTab.index == index) Color.Blue else Color.Gray,
                    style = MaterialTheme.typography.bodyLarge.copy(fontWeight = FontWeight.Bold),
                )
            },
            modifier = Modifier
                .zIndex(1f)
                .padding(end = 8.dp)
                .height(height = 36.dp)
        )
    }
},

试用演示

@Preview
@Composable
private fun Test() {
    val tabs = remember {
        listOf(
            TrackScreenTab.MUSIC,
            TrackScreenTab.COMMENTS,
            TrackScreenTab.SIMILAR_MUSICS
        )
    }

    var selectedTab by remember {
        mutableStateOf(
            tabs[0]
        )
    }

    TrackTabRow(
        tabs = tabs,
        selectedTab = selectedTab
    ) {
        selectedTab = it
    }
}

enum class TrackScreenTab(val text: String, val index: Int) {
    MUSIC(
        index = 0,
        text = "Music"
    ),
    COMMENTS(
        index = 1,
        text = "Comments"
    ),
    SIMILAR_MUSICS(
        index = 2,
        text = "Similar Music"
    )
}


@Composable
fun TrackTabRow(
    modifier: Modifier = Modifier,
    tabs: List<TrackScreenTab>,
    selectedTab: TrackScreenTab = TrackScreenTab.MUSIC,
    onTabSelected: (selectedTab: TrackScreenTab) -> Unit
) {
    ScrollableTabRow(
        selectedTabIndex = selectedTab.index,
        modifier = modifier,
        edgePadding = 16.dp,
tabs = {
    tabs.forEachIndexed { index, tab ->
        Tab(
            selected = selectedTab.index == index,
            onClick = remember { { onTabSelected(tab) } },
            text = {
                Text(
                    text = tab.text,
                    color = if (selectedTab.index == index) Color.Blue else Color.Gray,
                    style = MaterialTheme.typography.bodyLarge.copy(fontWeight = FontWeight.Bold),
                )
            },
            modifier = Modifier
                .zIndex(1f)
                .padding(end = 8.dp)
                .height(height = 36.dp)
        )
    }
},
        divider = {},
        indicator = { tabPositions: List<TabPosition> ->
            Box(
                Modifier
                    .tabIndicatorOffset(tabPositions[selectedTab.index])
                    .fillMaxWidth()
                    .height(36.dp)
                    .background(
                        brush = Brush.verticalGradient(
                            colors = listOf(
                                Color(0XFF00B2FF),
                                Color(0XFF00F0FF)
                            )
                        ),
                        shape = RoundedCornerShape(16.dp)
                    )
            )
        },
        containerColor = Color.Transparent,
        contentColor = Color(0XFF9E9FB4)
    )
}

对于任何感兴趣的人来说,它们都是这样放置的。

        // Position the children.
        layout(layoutWidth, layoutHeight) {
            // Place the tabs
            val tabPositions = mutableListOf<TabPosition>()
            var left = padding
            tabPlaceables.forEach {
                it.placeRelative(left, 0)
                tabPositions.add(TabPosition(left = left.toDp(), width = it.width.toDp()))
                left += it.width
            }

            // The divider is measured with its own height, and width equal to the total width
            // of the tab row, and then placed on top of the tabs.
            subcompose(TabSlots.Divider, divider).forEach {
                val placeable = it.measure(
                    constraints.copy(
                        minHeight = 0,
                        minWidth = layoutWidth,
                        maxWidth = layoutWidth
                    )
                )
                placeable.placeRelative(0, layoutHeight - placeable.height)
            }

            // The indicator container is measured to fill the entire space occupied by the tab
            // row, and then placed on top of the divider.
            subcompose(TabSlots.Indicator) {
                indicator(tabPositions)
            }.forEach {
                it.measure(Constraints.fixed(layoutWidth, layoutHeight)).placeRelative(0, 0)
            }

            scrollableTabData.onLaidOut(
                density = this@SubcomposeLayout,
                edgeOffset = padding,
                tabPositions = tabPositions,
                selectedTab = selectedTabIndex
            )
        }
© www.soinside.com 2019 - 2024. All rights reserved.