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

如何在Jetpack Compose Material 3中实现折叠式TopAppBar

在Jetpack Compose Material 3中实现折叠式TopAppBar

核心思路是利用Material3的ExitUntilCollapsedScrollBehavior获取折叠进度(collapsedFraction),通过这个进度值动态过渡UI元素的颜色、尺寸属性,完全匹配你需要的折叠效果:

import androidx.compose.animation.core.animateColorAsState
import androidx.compose.animation.core.animateFloatAsState
import androidx.compose.animation.core.tween
import androidx.compose.foundation.layout.Row
import androidx.compose.foundation.layout.Spacer
import androidx.compose.foundation.layout.fillMaxWidth
import androidx.compose.foundation.layout.padding
import androidx.compose.foundation.layout.size
import androidx.compose.foundation.lazy.LazyColumn
import androidx.compose.material.icons.Icons
import androidx.compose.material.icons.filled.Search
import androidx.compose.material3.ExperimentalMaterial3Api
import androidx.compose.material3.Icon
import androidx.compose.material3.MaterialTheme
import androidx.compose.material3.Scaffold
import androidx.compose.material3.Text
import androidx.compose.material3.TopAppBar
import androidx.compose.material3.TopAppBarDefaults
import androidx.compose.material3.rememberTopAppBarState
import androidx.compose.runtime.Composable
import androidx.compose.runtime.getValue
import androidx.compose.ui.Alignment
import androidx.compose.ui.Modifier
import androidx.compose.ui.graphics.Color
import androidx.compose.ui.text.font.FontWeight
import androidx.compose.ui.unit.dp
import androidx.compose.ui.unit.sp

@OptIn(ExperimentalMaterial3Api::class)
@Composable
fun CollapsibleWeatherTopAppBar() {
    // 初始化折叠滚动行为
    val scrollBehavior = TopAppBarDefaults.exitUntilCollapsedScrollBehavior(
        rememberTopAppBarState(),
        canScroll = { true }
    )
    // 获取折叠进度:0=完全展开,1=完全收起
    val collapsedFraction = scrollBehavior.state.collapsedFraction

    // 标题/搜索图标颜色过渡:展开时用OnSurface,收起时切换为Primary
    val titleAndIconColor by animateColorAsState(
        targetValue = if (collapsedFraction >= 1f) MaterialTheme.colorScheme.primary else MaterialTheme.colorScheme.onSurface,
        animationSpec = tween(300)
    )
    // 温度文本尺寸过渡:展开48sp,收起24sp
    val temperatureTextSize by animateFloatAsState(
        targetValue = if (collapsedFraction >= 1f) 24f else 48f,
        animationSpec = tween(300)
    )
    // 温度颜色过渡:展开时Primary,收起时OnSurface
    val temperatureColor by animateColorAsState(
        targetValue = if (collapsedFraction >= 1f) MaterialTheme.colorScheme.onSurface else MaterialTheme.colorScheme.primary,
        animationSpec = tween(300)
    )
    // Feels like文本颜色过渡:展开时OnSurfaceVariant,收起时OnSurface
    val feelsLikeColor by animateColorAsState(
        targetValue = if (collapsedFraction >= 1f) MaterialTheme.colorScheme.onSurface else MaterialTheme.colorScheme.onSurfaceVariant,
        animationSpec = tween(300)
    )

    Scaffold(
        topBar = {
            TopAppBar(
                title = {
                    Row(
                        modifier = Modifier.fillMaxWidth(),
                        verticalAlignment = Alignment.CenterVertically
                    ) {
                        // 城市标题区域
                        Row(
                            modifier = Modifier.weight(1f),
                            verticalAlignment = Alignment.CenterVertically
                        ) {
                            Column {
                                Text(
                                    text = "Kharkive, Ukraine",
                                    color = titleAndIconColor,
                                    style = MaterialTheme.typography.titleLarge.copy(
                                        fontWeight = FontWeight.Medium
                                    )
                                )
                                Text(
                                    text = "Feels like -2°",
                                    color = feelsLikeColor,
                                    style = MaterialTheme.typography.bodyMedium,
                                    modifier = Modifier.padding(top = 4.dp)
                                )
                            }
                        }
                        // 温度与搜索图标区域
                        Row(verticalAlignment = Alignment.CenterVertically) {
                            Text(
                                text = "3°",
                                color = temperatureColor,
                                fontSize = temperatureTextSize.sp,
                                fontWeight = FontWeight.Bold
                            )
                            Spacer(modifier = Modifier.size(16.dp))
                            Icon(
                                imageVector = Icons.Default.Search,
                                contentDescription = "Search",
                                tint = titleAndIconColor,
                                modifier = Modifier.size(24.dp)
                            )
                        }
                    }
                },
                scrollBehavior = scrollBehavior,
                colors = TopAppBarDefaults.topAppBarColors(
                    containerColor = Color.Transparent,
                    scrolledContainerColor = MaterialTheme.colorScheme.surface
                )
            )
        }
    ) { paddingValues ->
        // 可滚动内容,用于触发TopAppBar的折叠逻辑
        LazyColumn(modifier = Modifier.padding(paddingValues)) {
            items(50) { index ->
                Text(
                    text = "List item $index",
                    modifier = Modifier
                        .fillMaxWidth()
                        .padding(16.dp)
                )
            }
        }
    }
}

关键实现说明

  • 折叠进度控制:collapsedFraction是核心变量,它反映了TopAppBar的折叠程度,所有UI变化都基于这个值触发
  • 平滑过渡动画:使用animateColorAsState和animateFloatAsState实现颜色、尺寸的无缝切换,避免生硬的视觉跳变
  • 布局稳定性:标题和搜索图标的位置全程保持不变,仅通过颜色、尺寸的变化体现折叠状态,完全符合你的需求

内容的提问来源于stack exchange,提问作者Muhammad Ibrahim

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.06.17 11:33:19