作るもの
収まる場合は等幅
収まらない場合は横スクロールさせる。なおかつ等幅
使用するもの
Jetpack Composeは1度しかmeasureを呼ぶ事ができない制約がありますが、レイアウトにとある高さを与えた場合、無限の横幅があると仮定してレイアウトが変わらない最小の横幅を計測してくれる maxIntrinsicWidth
を使います。
Intrinsic measurements in Compose layouts | Jetpack Compose | Android Developers
コード
@Composable fun TabLayout( modifier: Modifier, size: Int, content: @Composable (index: Int) -> Unit ) { var enableScroll by rememberSaveable { mutableStateOf(false) } BoxWithConstraints( modifier = modifier ) { val containerWidthPx = with(LocalDensity.current) { maxWidth.toPx() } Layout( modifier = Modifier .fillMaxSize() .horizontalScroll(state = rememberScrollState(), enabled = enableScroll), content = { (0 until size).forEach { index -> content(index) } }) layout@{ measurables, constraints -> // 最大の横幅のアイテムを計測する val contentMaxOfMinIntrinsicWidth = measurables.map { it.maxIntrinsicWidth(constraints.maxHeight) } .maxOfOrNull { it } ?: return@layout layout(0, 0) {} val contentWidth: Int if (contentMaxOfMinIntrinsicWidth * measurables.size > containerWidthPx) { enableScroll = true contentWidth = contentMaxOfMinIntrinsicWidth } else { enableScroll = false contentWidth = (containerWidthPx / measurables.size).toInt() } val placeables = measurables.map { it.measure(Constraints.fixed(width = contentWidth, height = constraints.maxHeight)) } layout(contentWidth * measurables.size, constraints.maxHeight) { placeables.forEachIndexed { index, placeable -> placeable.place(x = contentWidth * index, y = 0) } } } } }
プレビュー
@Composable @Preview(showBackground = true) private fun ShortPreview() { val items = remember { listOf( "000000000000", "1", ) } PreviewTab(items = items) } @Composable @Preview(showBackground = true) private fun LongPreview() { val items = remember { listOf( "000000000000", "1", "2", "3", "400000", ) } PreviewTab(items = items) } @Composable private fun PreviewTab( items: List<String> ) { TabLayout( modifier = Modifier .fillMaxWidth() .height(80.dp), size = items.size, content = { index -> val item = items[index] Box( modifier = Modifier .fillMaxSize(), contentAlignment = Alignment.Center, ) { Text(text = item) } } ) }