You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

如何在Collapsing Toolbar+Horizontal Pager子Composable中居中Empty State

问题:Collapsing Toolbar展开时Horizontal Pager子项Empty State无法居中

当前Horizontal Pager的子Composable设置了fillMaxHeight,导致Empty State底部区域被截断,无法在Toolbar展开状态下居中显示。考虑两种思路:

  • 获取子Composable可见高度计算后设置居中
  • 利用滚动行为API处理

解决方案

核心问题在于外层布局的高度设置错误,导致Pager区域实际高度超出屏幕可见范围,Empty State的居中是基于这个超出高度计算,而非可见区域。通过以下调整修复:

  • 将包含Tab和Pager的Column的height(screenHeight)替换为weight(1f),让它占据Header下方的剩余屏幕空间,而非强制设置为屏幕高度。
  • 给HorizontalPager添加fillMaxHeight()修饰符,确保它填满父布局的可用空间,同时保留wrapContentHeight()实现内容自适应。
  • 保持Empty State的父容器使用fillMaxSize()结合contentAlignment = Alignment.Center,此时父容器高度为可见区域高度,Empty State就能正确居中。

修改后的代码示例

import androidx.compose.foundation.ExperimentalFoundationApi
import androidx.compose.foundation.Image
import androidx.compose.foundation.background
import androidx.compose.foundation.layout.Box
import androidx.compose.foundation.layout.BoxWithConstraints
import androidx.compose.foundation.layout.Column
import androidx.compose.foundation.layout.ExperimentalLayoutApi
import androidx.compose.foundation.layout.FlowRow
import androidx.compose.foundation.layout.fillMaxSize
import androidx.compose.foundation.layout.fillMaxWidth
import androidx.compose.foundation.layout.height
import androidx.compose.foundation.layout.size
import androidx.compose.foundation.layout.wrapContentHeight
import androidx.compose.foundation.lazy.LazyColumn
import androidx.compose.foundation.pager.HorizontalPager
import androidx.compose.foundation.pager.rememberPagerState
import androidx.compose.foundation.rememberScrollState
import androidx.compose.foundation.verticalScroll
import androidx.compose.material.Button
import androidx.compose.material.Tab
import androidx.compose.material.TabRow
import androidx.compose.material.TabRowDefaults
import androidx.compose.material3.Text
import androidx.compose.runtime.Composable
import androidx.compose.runtime.remember
import androidx.compose.runtime.rememberCoroutineScope
import androidx.compose.ui.Alignment
import androidx.compose.ui.Modifier
import androidx.compose.ui.geometry.Offset
import androidx.compose.ui.graphics.Color
import androidx.compose.ui.input.nestedscroll.NestedScrollConnection
import androidx.compose.ui.input.nestedscroll.NestedScrollSource
import androidx.compose.ui.input.nestedscroll.nestedscroll
import androidx.compose.ui.res.painterResource
import androidx.compose.ui.text.TextStyle
import androidx.compose.ui.unit.dp
import androidx.compose.ui.unit.sp
import com.google.accompanist.pager.ExperimentalPagerApi
import com.google.accompanist.pager.pagerTabIndicatorOffset
import kotlinx.coroutines.launch

@OptIn(ExperimentalFoundationApi::class, ExperimentalPagerApi::class)
@Composable
fun CenterViewScreen() {
    BoxWithConstraints {
        val scrollState = rememberScrollState()

        Column(
            modifier = Modifier
                .fillMaxSize()
                .verticalScroll(state = scrollState),
        ) {
            Box(
                modifier = Modifier
                    .height(200.dp)
                    .fillMaxWidth()
                    .background(Color.LightGray),
                contentAlignment = Alignment.Center,
            ) {
                Text(text = "HEADER")
            }

            // 替换height(screenHeight)为weight(1f),占据剩余空间
            Column(modifier = Modifier.weight(1f)) {
                val tabList = listOf("Tab1", "Tab2")
                val pagerState = rememberPagerState(initialPage = 0)
                val coroutineScope = rememberCoroutineScope()

                TabRow(
                    modifier = Modifier.fillMaxWidth(),
                    backgroundColor = Color.White,
                    contentColor = Color.Black,
                    selectedTabIndex = pagerState.currentPage,
                    indicator = { tabPositions ->
                        TabRowDefaults.Indicator(
                            Modifier.pagerTabIndicatorOffset(pagerState, tabPositions),
                        )
                    },
                ) {
                    tabList.forEachIndexed { index, title ->
                        Tab(
                            text = { Text(title) },
                            selected = pagerState.currentPage == index,
                            onClick = {
                                coroutineScope.launch {
                                    pagerState.animateScrollToPage(index)
                                }
                            },
                        )
                    }
                }

                HorizontalPager(
                    state = pagerState,
                    pageCount = tabList.size,
                    modifier = Modifier.fillMaxWidth()
                        .fillMaxHeight() // 添加fillMaxHeight确保填满可用空间
                        .wrapContentHeight()
                        .nestedScroll(
                            remember {
                                object : NestedScrollConnection {
                                    override fun onPreScroll(
                                        available: Offset,
                                        source: NestedScrollSource,
                                    ): Offset {
                                        return if (available.y > 0) {
                                            Offset.Zero
                                        } else {
                                            Offset(
                                                x = 0f,
                                                y = -scrollState.dispatchRawDelta(-available.y),
                                            )
                                        }
                                    }
                                }
                            },
                        ),
                ) { page: Int ->
                    when (page) {
                        0 -> ListLazyColumn(0)
                        1 -> ListFlowRow(5)
                    }
                }
            }
        }
    }
}

@Composable
fun ListLazyColumn(itemsCount: Int) {
    Box(
        modifier = Modifier.fillMaxSize(),
        contentAlignment = Alignment.Center,
    ) {
        if (itemsCount > 0) {
            LazyColumn(modifier = Modifier.fillMaxSize()) {
                items(itemsCount) { index ->
                    Button(
                        onClick = { /*TODO*/ },
                        modifier = Modifier.fillMaxWidth(),
                    ) {
                        Text(text = "Button $index")
                    }
                }
            }
        } else {
            Column(
                Modifier.align(Alignment.Center),
                horizontalAlignment = Alignment.CenterHorizontally,
            ) {
                Image(
                    painter = painterResource(id = R.drawable.e_commerce_01),
                    contentDescription = "empty state",
                    modifier = Modifier.size(120.dp),
                )
                Text(
                    text = "Centering Empty State",
                    style = TextStyle(
                        fontSize = 18.sp,
                    ),
                )
            }
        }
    }
}

@OptIn(ExperimentalLayoutApi::class)
@Composable
fun ListFlowRow(items: Int) {
    FlowRow(modifier = Modifier.fillMaxSize()) {
        repeat(items) { index ->
            Button(
                onClick = { /*TODO*/ },
                modifier = Modifier.fillMaxWidth(),
            ) {
                Text(text = "Button $index")
            }
        }
    }
}

内容的提问来源于stack exchange,提问作者Htoo Aung Hlaing

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.07.08 09:17:37