如何在Android中制作垂直TabLayout

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

嗨,我正在尝试制作垂直选项卡布局。尝试使用旋转视图,但它无助于它不重新计算视图高度和视图。任何帮助将不胜感激,以实现以下结果。

android android-custom-view android-tablayout
3个回答
2
投票

这可能对你有帮助。

<FrameLayout
    android:layout_width="300dp"
    android:layout_height="300dp"
    android:rotation="-90"
    android:layout_gravity="center_vertical">

    <com.google.android.material.tabs.TabLayout
        android:id="@+id/tabLayout"
        android:layout_width="match_parent"
        android:layout_height="wrap_content"
        android:layout_gravity="center_horizontal"
        app:tabIndicatorHeight="4dp"
        app:tabTextAppearance="@style/CDTabLayoutTextAppearance"
        app:tabInlineLabel="true"
        app:tabIconTint="@color/tab_tint_color_selector"
        app:tabIndicatorFullWidth="false"
        app:tabIndicator="@drawable/ic_community_tab_selected"
        app:tabIndicatorColor="@color/colorColoringDesk"
        android:background="@color/white" >

        <com.google.android.material.tabs.TabItem
            android:id="@+id/tabItemContacts"
            android:layout_width="wrap_content"
            android:layout_height="wrap_content"
            android:text="Contacts" />

        <com.google.android.material.tabs.TabItem
            android:id="@+id/tabItemChat"
            android:layout_width="wrap_content"
            android:layout_height="wrap_content"
            android:text="Chat" />

    </com.google.android.material.tabs.TabLayout>
</FrameLayout>

将 TabLayout 与 ViewPager2 一起使用。


0
投票

我使用以下代码制作了垂直 tabLayout。你也可以尝试一下:-

<androidx.constraintlayout.widget.ConstraintLayout
    android:layout_width="match_parent"
    android:layout_height="wrap_content"
    android:rotation="-90"
    android:transformPivotX="0dp"
    android:layout_marginTop="@dimen/tab_width"
    android:layout_marginLeft="-24dp">
    <com.google.android.material.tabs.TabLayout
        android:layout_width="@dimen/tab_width"
        android:layout_height="wrap_content"
        android:background="@color/black"
        app:tabTextColor="@color/light_grey"
        app:tabSelectedTextColor="@color/white"
        app:tabIndicatorHeight="2dp"
        app:tabIndicatorColor="@color/white"
        app:tabPaddingBottom="-10dp">
        <com.google.android.material.tabs.TabItem
            android:layout_width="wrap_content"
            android:layout_height="wrap_content"
            android:text="ABC"/>
        <com.google.android.material.tabs.TabItem
            android:layout_width="wrap_content"
            android:layout_height="wrap_content"
            android:text="XYZ"/>
    </com.google.android.material.tabs.TabLayout>
</androidx.constraintlayout.widget.ConstraintLayout>

0
投票

这是 VerticalTabLayout 的代码! 我正在使用 Jetpack Compose。对于垂直选项卡,我使用 Modifier.onGloballyPositioned 来计算项目高度和指示器高度。我正在使用垂直滚动列作为项目列表。

@Composable
fun MDVerticalTabLayout(
    modifier: Modifier = Modifier,
    bottomPaddingDp: Dp = 0.dp,
    list: SnapshotStateList<CategoryItem>,
    gradientFirstColor: Color,
    gradientSecondColor: Color,
    indicatorColor: Color,
    indicatorHeightType: IndicatorHeightType = IndicatorHeightType.FIXED,
    indicatorHeight: Dp = 58.dp,
    textStyle: TextStyle = TextStyle.Default,
    onItemClick: (CategoryItem) -> Unit,
) {
    val context = LocalContext.current
    val globalOffsetYPosition = rememberSaveable {
        mutableIntStateOf(0)
    }
    val globalHeight = rememberSaveable {
        mutableIntStateOf(0)
    }

    val selectedPosition = rememberSaveable {
        mutableIntStateOf(0)
    }

    val movementThreshHold = remember {
        100
    }

    val offsetYPosition = rememberSaveable {
        mutableIntStateOf(0)
    }

    val heightOfItem = rememberSaveable {
        mutableIntStateOf(0)
    }
    val listState = rememberScrollState()
    val coroutineScope = rememberCoroutineScope()
    val map = remember {
        hashMapOf<Int, Int>()
    }

    val durationMillis = rememberSaveable {
        mutableStateOf(0)
    }

    // Define the animation spec
    val animationSpec by remember {
        derivedStateOf {
            tween<Offset>(
                durationMillis = durationMillis.value,
                easing = FastOutSlowInEasing,
            )
        }
    }

    val animationSpecForScroll by remember {
        derivedStateOf {
            tween<Float>(
                durationMillis = 600,
                easing = FastOutSlowInEasing,
            )
        }
    }

    // Create an animated offset
    val animatedOffset by animateOffsetAsState(
        targetValue = Offset(x = 0F, y = offsetYPosition.value.toFloat()),
        animationSpec = animationSpec,
        label = "",
    )

    val indicatorHeight by remember {
        derivedStateOf {
            when (indicatorHeightType) {
                IndicatorHeightType.FIXED -> {
                    indicatorHeight
                }
                IndicatorHeightType.FILL -> {
                    getDp(context, heightOfItem.intValue)
                }
            }
        }
    }

    Scaffold(
        modifier = modifier
            .background(Color.White)
            .fillMaxHeight()
            .width(78.dp)
            .onGloballyPositioned {
                globalOffsetYPosition.value = it.positionInRoot().y.toInt()
                globalHeight.value = it.size.height
            },
        content = { paddingValues ->
            Surface(modifier = Modifier.fillMaxHeight().padding(paddingValues), elevation = 10.dp) {
                Box(
                    contentAlignment = Alignment.TopEnd,
                ) {
                    Column(
                        modifier = Modifier
                            .verticalScroll(listState)
                            .width(78.dp),
                    ) {
                        list.forEachIndexed { position, item ->
                            map[position] = 0
                            var height: Int = 0

                            ItemSubCategory(
                                item = item,
                                onChecked = selectedPosition.value == position,
                                gradientFirstColor = gradientFirstColor,
                                gradientSecondColor = gradientSecondColor,
                                textStyle = textStyle,
                                modifier = Modifier
                                    .onGloballyPositioned {
                                        map[position] = it.positionInRoot().y.toInt()
                                        height = it.size.height
                                        if (selectedPosition.value == position) {
                                            height = it.size.height
                                            offsetYPosition.value =
                                                it.positionInRoot().y.toInt() - globalOffsetYPosition.value
                                            heightOfItem.value = height
                                        }
                                    }
                                    .fillMaxWidth(),
                            ) {
                                durationMillis.value = 600
                                offsetYPosition.intValue = map[position] ?: (0 - globalOffsetYPosition.intValue)
                                selectedPosition.intValue = position
                                heightOfItem.intValue = height
                                coroutineScope.launch {
                                    if (offsetYPosition.intValue <= height + movementThreshHold) {
                                        listState.animateScrollTo(animationSpec = animationSpecForScroll, value = listState.value - height / 2 - movementThreshHold)
                                    } else if (offsetYPosition.intValue >= globalHeight.intValue - height / 2 - movementThreshHold) {
                                        listState.animateScrollTo(animationSpec = animationSpecForScroll, value = (listState.value + height / 2 + movementThreshHold))
                                    }
                                    delay(600)
                                    durationMillis.value = 0
                                }
                                onItemClick(item)
                            }
                            if (position == list.size - 1) {
                                Spacer(
                                    modifier = Modifier
                                        .padding(bottom = bottomPaddingDp)
                                        .fillMaxWidth()
                                        .background(Color.White),
                                )
                            } else {
                                Spacer(
                                    modifier = Modifier
                                        .fillMaxWidth()
                                        .background(Color.White),
                                )
                            }
                        }
                    }

                    Box(
                        modifier = Modifier
                            .offset(
                                x = getDp(context, animatedOffset.x.toInt()),
                                y = getDp(context, animatedOffset.y.toInt()),
                            )
                            .padding(top = 24.dp)
                            .width(3.dp)
                            .height(indicatorHeight)
                            .background(
                                color = indicatorColor,
                                shape = RoundedCornerShape(2.dp),
                            ),

                    )
                }
            }
        },
    )
}


fun getDp(context: Context, offset: Int): Dp {
    val resources = context.resources
    val metrics = resources.displayMetrics
    val dpValue = offset / (metrics.densityDpi.toFloat() / DisplayMetrics.DENSITY_DEFAULT)
    return dpValue.dp
}

enum class IndicatorHeightType {
    FILL,
    FIXED,
}
© www.soinside.com 2019 - 2024. All rights reserved.