我找不到任何关于这个问题的文档,在Compose中有类似于CollapsingToolbar的东西吗?所有我发现的是提到它here,但没有关于如何设置它
CollapsingToolbar
pexxcrt21#
Material Design 3的Jetpack合成实施包括4种类型的顶部应用栏(https://m3.material.io/components/top-app-bar/implementation):
CenterAlignedTopAppBar
SmallTopAppBar
MediumTopAppBar
LargeTopAppBar
https://developer.android.com/reference/kotlin/androidx/compose/material3/package-summary它们都有一个scrollBehavior参数,可以用来折叠工具栏。
scrollBehavior
TopAppBarDefaults.pinnedScrollBehavior
TopAppBarDefaults.enterAlwaysScrollBehavior
TopAppBarDefaults.exitUntilCollapsedScrollBehavior
https://developer.android.com/reference/kotlin/androidx/compose/material3/TopAppBarDefaults
**注:**此API目前被注解为实验性。
@OptIn(ExperimentalMaterial3Api::class) @Composable fun Test() { val scrollBehavior = TopAppBarDefaults.enterAlwaysScrollBehavior(rememberTopAppBarState()) Scaffold( modifier = Modifier.nestedScroll(scrollBehavior.nestedScrollConnection), topBar = { MediumTopAppBar( title = { Text(text = "Scroll Behavior Test") }, navigationIcon = { IconButton(onClick = { /*TODO*/ }) { Icon(imageVector = Icons.Default.Menu, contentDescription = "") } }, scrollBehavior = scrollBehavior ) } ) { LazyColumn(modifier = Modifier.fillMaxWidth()) { items((1..50).toList()) { item -> Text(modifier = Modifier.padding(8.dp), text = "Item $item") } } } }
hujrc8aj2#
我找到了一个由Samir Basnet(来自KotlinSlack频道)创建的解决方案,这对我很有用,我希望它能帮助其他人...
@Composable fun CollapsingEffectScreen() { val items = (1..100).map { "Item $it" } val lazyListState = rememberLazyListState() var scrolledY = 0f var previousOffset = 0 LazyColumn( Modifier.fillMaxSize(), lazyListState, ) { item { Image( painter = painterResource(id = R.drawable.recife), contentDescription = null, contentScale = ContentScale.FillWidth, modifier = Modifier .graphicsLayer { scrolledY += lazyListState.firstVisibleItemScrollOffset - previousOffset translationY = scrolledY * 0.5f previousOffset = lazyListState.firstVisibleItemScrollOffset } .height(240.dp) .fillMaxWidth() ) } items(items) { Text( text = it, Modifier .background(Color.White) .fillMaxWidth() .padding(8.dp) ) } } }
结果如下:
5sxhfpxr3#
我在Android文档中发现了这个问题,我认为您在问题中链接的文档是在讨论如何使用嵌套滚动来实现这一点。
val toolbarHeight = 48.dp val toolbarHeightPx = with(LocalDensity.current) { toolbarHeight.roundToPx().toFloat() } val toolbarOffsetHeightPx = remember { mutableStateOf(0f) } val nestedScrollConnection = remember { object : NestedScrollConnection { override fun onPreScroll(available: Offset, source: NestedScrollSource): Offset { val delta = available.y val newOffset = toolbarOffsetHeightPx.value + delta toolbarOffsetHeightPx.value = newOffset.coerceIn(-toolbarHeightPx, 0f) return Offset.Zero } } } Box( Modifier .fillMaxSize() .nestedScroll(nestedScrollConnection) ) { LazyColumn(contentPadding = PaddingValues(top = toolbarHeight)) { items(100) { index -> Text("I'm item $index", modifier = Modifier .fillMaxWidth() .padding(16.dp)) } } TopAppBar( modifier = Modifier .height(toolbarHeight) .offset { IntOffset(x = 0, y = toolbarOffsetHeightPx.value.roundToInt()) }, title = { Text("toolbar offset is ${toolbarOffsetHeightPx.value}") } ) }
tez616oj4#
您可以使用compose-collapsing-toolbar库。
安装:implementation "me.onebone:toolbar-compose:2.1.0"
implementation "me.onebone:toolbar-compose:2.1.0"
用法-Exemple
以下是来自该库的www.example.com的一些gif图像Readme.md:第一次
lhcgjxsq5#
您可以按照文档中的示例创建一个工具栏,该工具栏在每次向上/向下滚动时都会展开/折叠。要创建一个仅在列表滚动到顶部时才展开的工具栏,可以对原始示例稍作修改:
val toolbarHeight = 48.dp val toolbarHeightPx = with(LocalDensity.current) { toolbarHeight.roundToPx().toFloat() } var toolbarOffsetHeightPx by remember { mutableStateOf(0f) } var totalScrollOffsetPx = remember { 0f } val nestedScrollConnection = remember { object : NestedScrollConnection { override fun onPreScroll(available: Offset, source: NestedScrollSource): Offset { val delta = available.y totalScrollOffsetPx += delta if (totalScrollOffsetPx in -toolbarHeightPx..0f) { toolbarOffsetHeightPx = totalScrollOffsetPx } return Offset.Zero } } }
通过这样做,您可以灵活地创建自己的CollapsibleScaffold,它可以接受scrollBehaviour、appBarLayout和list等参数。例如,通过这种方式,你还可以通过编程计算应用栏的高度,摆脱大量的样板文件,使屏幕上使用的代码整洁干净。
CollapsibleScaffold
scrollBehaviour
appBarLayout
list
qcuzuvrc6#
合成-折叠-工具栏Jetpack合成的折叠工具栏布局的简单实现https://github.com/onebone/compose-collapsing-toolbar
jecbmhm37#
我有一些特定的需求,所以我创建了一个简单的impl,它测量navigationIcons和Trainling图标,并试图适应它们之间的内容。忽略重载和测试代码,它不到200行,应该很容易定制为您的特定需求。https://gist.github.com/fabriciovergara/5de1e8b114fb484bf5f6808a0a107b24
@Composable fun CollapsibleScaffold( state: LazyListState, modifier: Modifier = Modifier, topBar: @Composable () -> Unit = {}, content: @Composable (insets: PaddingValues) -> Unit ) { CollapsibleScaffoldInternal( offsetState = rememberOffsetScrollState(state), modifier = modifier, topBar = topBar, content = content ) } @Composable private fun CollapsibleScaffoldInternal( offsetState: State<Int>, modifier: Modifier = Modifier, topBar: @Composable () -> Unit = {}, content: @Composable (insets: PaddingValues) -> Unit ) { Scaffold(modifier = modifier, backgroundColor = Color.Transparent) { insets -> Box { content( PaddingValues( top = CollapsibleTopAppBarDefaults.maxHeight + 8.dp, bottom = 16.dp ) ) CompositionLocalProvider( LocalScrollOffset provides offsetState, LocalInsets provides insets ) { topBar() } } } } @Composable fun CollapsibleTopAppBar( modifier: Modifier = Modifier, actions: (@Composable RowScope.() -> Unit)? = null, navigationIcon: (@Composable () -> Unit)? = null, content: (@Composable CollapsibleTopAppBarScope.() -> Unit) = { } ) { CollapsibleTopAppBarInternal( scrollOffset = LocalScrollOffset.current.value, insets = LocalInsets.current, modifier = modifier.background(Color.Transparent), navigationIcon = navigationIcon, actions = actions, content = content ) } @Composable private fun CollapsibleTopAppBarInternal( scrollOffset: Int, insets: PaddingValues, modifier: Modifier = Modifier, navigationIcon: (@Composable () -> Unit)? = null, actions: (@Composable RowScope.() -> Unit)? = null, content: @Composable CollapsibleTopAppBarScope.() -> Unit ) { val density = LocalDensity.current val actionsSize = remember { mutableStateOf(IntSize.Zero) } val navIconSize = remember { mutableStateOf(IntSize.Zero) } val actionWidth = with(density) { actionsSize.value.width.toDp() } val backWidth = with(density) { navIconSize.value.width.toDp() } val bodyHeight = CollapsibleTopAppBarDefaults.maxHeight - CollapsibleTopAppBarDefaults.minHeight val maxOffset = with(density) { bodyHeight.roundToPx() - insets.calculateTopPadding().roundToPx() } val offset = min(scrollOffset, maxOffset) val fraction = 1f - kotlin.math.max(0f, offset.toFloat()) / maxOffset val currentMaxHeight = bodyHeight * fraction BoxWithConstraints(modifier = modifier) { val maxWidth = maxWidth Row( modifier = Modifier .height(CollapsibleTopAppBarDefaults.minHeight) .fillMaxWidth(), verticalAlignment = Alignment.CenterVertically ) { Box( modifier = Modifier.onGloballyPositioned { navIconSize.value = it.size } ) { if (navigationIcon != null) { navigationIcon() } } Spacer(modifier = Modifier.weight(1f)) Row( modifier = Modifier .widthIn(0.dp, maxWidth / 3) .onGloballyPositioned { actionsSize.value = it.size } ) { if (actions != null) { actions() } } } val scaleFraction = (fraction / CollapsibleTopAppBarDefaults.startScalingFraction).coerceIn(0f, 1f) val paddingStart = if (fraction > CollapsibleTopAppBarDefaults.startScalingFraction) { 0.dp } else { lerp(backWidth, 0.dp, scaleFraction) } val paddingEnd = if (fraction > CollapsibleTopAppBarDefaults.startScalingFraction) { 0.dp } else { lerp(actionWidth, 0.dp, scaleFraction) } /** * When content height reach minimum size, we start translating it to fit the toolbar */ val startTranslateFraction = CollapsibleTopAppBarDefaults.minHeight / CollapsibleTopAppBarDefaults.maxHeight val translateFraction = (fraction / startTranslateFraction).coerceIn(0f, 1f) val paddingTop = if (fraction > startTranslateFraction) { CollapsibleTopAppBarDefaults.minHeight } else { lerp(0.dp, CollapsibleTopAppBarDefaults.minHeight, translateFraction) } BoxWithConstraints( modifier = Modifier .padding(top = paddingTop, start = paddingStart, end = paddingEnd) .height(max(CollapsibleTopAppBarDefaults.minHeight, currentMaxHeight)) .fillMaxWidth() .align(Alignment.BottomStart) ) { val scope = remember(fraction, this) { CollapsibleTopAppBarScope(fraction = fraction, scope = this) } content(scope) } } } @Composable private fun rememberOffsetScrollState(state: LazyListState): MutableState<Int> { val offsetState = rememberSaveable() { mutableStateOf(0) } LaunchedEffect(key1 = state.layoutInfo.visibleItemsInfo) { val fistItem = state.layoutInfo.visibleItemsInfo.firstOrNull { it.index == 0 } val offset = fistItem?.offset?.absoluteValue ?: Int.MAX_VALUE offsetState.value = offset } return offsetState } object CollapsibleTopAppBarDefaults { // Replicating the value in androidx.compose.material.AppBar.AppBarHeight which is private val minHeight = 56.dp val maxHeight = 320.dp /** * When content height reach this point we start applying padding start and end */ const val startScalingFraction = 0.5f }
zbdgwd5y8#
这是我在作曲时用来制作折叠效果的
Constraint layout - compose
Motion Layout
结果+ Source Code**
添加此依赖项。implementation("androidx.constraintlayout:constraintlayout-compose:1.1.0-alpha03")
implementation("androidx.constraintlayout:constraintlayout-compose:1.1.0-alpha03")
**STEP 1:**在原始资源文件夹中创建collapse_toolbar.json5文件折叠工具栏.json5
STEP 1:
collapse_toolbar.json5
{ ConstraintSets: { start: { box: { width: 'spread', height: 230, start: ['parent', 'start'], end: ['parent', 'end'], top: ['parent', 'top'], custom: { background: '#FF74d680' } }, help_image:{ width: 80, height: 120, end: ['box', 'end', 16], top: ['box', 'top', 16], bottom: ['box', 'bottom',8] }, close_button:{ start: ['parent', 'start',8], bottom: ['box', 'bottom',8] }, title: { start: ['close_button', 'end', 16], bottom: ['close_button', 'bottom'], top: ['close_button', 'top'] } }, end: { help_image:{ width: 10, height: 10, bottom: ['box', 'bottom'], end: ['box', 'end'] }, box: { width: 'spread', height: 56, start: ['parent', 'start'], end: ['parent', 'end'], top: ['parent', 'top'], custom: { background: '#FF378b29' } }, close_button:{ start: ['box', 'start', 16], bottom: ['box', 'bottom', 16], top: ['box', 'top', 16] }, title: { start: ['close_button', 'end', 8], bottom: ['close_button', 'bottom'], top: ['close_button', 'top'] } } }, Transitions: { default: { from: 'start', to: 'end', pathMotionArc: 'startVertical', // key here must be Key with capital K KeyFrames: { KeyAttributes: [ { target: ['box'], frames: [0, 20, 50, 80, 100] // rotationZ: [0, 360] }, { target: ['close_button'], frames: [0, 20, 60, 80, 100], // translationY: [20, 40, 65, 85, 100] // alpha: [1, 0.5, 0.5, 0.7, 1] }, { target: ['title'], frames: [0, 100], // translationY: [20,100] // alpha: [1, 0.5, 0.5, 0.7, 1] }, { target: ['help_image'], frames: [0, 30, 50, 80, 100], scaleX: [1, 0.8, 0.6, 0.3, 0], scaleY: [1, 0.8, 0.6, 0.3, 0], alpha: [1, 0.8, 0.6, 0.3, 0] } ] } } } }
**STEP 2:**创建可组合函数并添加Motion Layout主要活动.kt
STEP 2:
@ExperimentalComposeUiApi class MainActivity : ComponentActivity() { override fun onCreate(savedInstanceState: Bundle?) { super.onCreate(savedInstanceState) setContent { val lazyScrollState = rememberLazyListState() Scaffold( modifier = Modifier .fillMaxSize(), topBar = { CollapsingToolbar(lazyScrollState) }, ) { paddingValues -> Column(modifier = Modifier.padding(paddingValues)) { LazyColumn( modifier = Modifier .fillMaxSize() .background(color = Color.White) .animateContentSize(), state = lazyScrollState ) { items(100) { index -> Text(modifier = Modifier.padding(36.dp), text = "Item: $index") Divider(color = Color.Black, thickness = 1.dp) } } } } } } } @OptIn(ExperimentalMotionApi::class) @Composable fun CollapsingToolbar(lazyScrollState: LazyListState) { val context = LocalContext.current val motionScene = remember { context.resources.openRawResource(R.raw.collapse_toolbar).readBytes().decodeToString() } val progress by animateFloatAsState( targetValue = if (lazyScrollState.firstVisibleItemIndex in 0..1) 0f else 1f, tween(500) ) val motionHeight by animateDpAsState( targetValue = if (lazyScrollState.firstVisibleItemIndex in 0..1) 230.dp else 56.dp, tween(500) ) MotionLayout( motionScene = MotionScene(content = motionScene), progress = progress, modifier = Modifier .fillMaxWidth() .background(backgroundColor) .height(motionHeight) ) { val boxProperties = motionProperties(id = "box") // val startColor = Color(boxProperties.value.color("custome")) Box( modifier = Modifier .layoutId("box") .background(boxProperties.value.color("background")) ) Image( modifier = Modifier .layoutId("help_image"), painter = painterResource(id = R.drawable.help), contentDescription = "" ) Icon( modifier = Modifier.layoutId("close_button"), imageVector = Icons.Filled.Close, contentDescription = "", tint = Color.White ) Text( modifier = Modifier.layoutId("title"), text = "Help", color = Color.White, fontSize = 18.sp ) } }
8条答案
按热度按时间pexxcrt21#
Material Design 3的Jetpack合成实施包括4种类型的顶部应用栏(https://m3.material.io/components/top-app-bar/implementation):
CenterAlignedTopAppBar
SmallTopAppBar
MediumTopAppBar
LargeTopAppBar
https://developer.android.com/reference/kotlin/androidx/compose/material3/package-summary
它们都有一个
scrollBehavior
参数,可以用来折叠工具栏。TopAppBarDefaults.pinnedScrollBehavior
TopAppBarDefaults.enterAlwaysScrollBehavior
TopAppBarDefaults.exitUntilCollapsedScrollBehavior
https://developer.android.com/reference/kotlin/androidx/compose/material3/TopAppBarDefaults
**注:**此API目前被注解为实验性。
样品使用:
hujrc8aj2#
我找到了一个由Samir Basnet(来自KotlinSlack频道)创建的解决方案,这对我很有用,我希望它能帮助其他人...
结果如下:
5sxhfpxr3#
我在Android文档中发现了这个问题,我认为您在问题中链接的文档是在讨论如何使用嵌套滚动来实现这一点。
tez616oj4#
您可以使用compose-collapsing-toolbar库。
安装:
implementation "me.onebone:toolbar-compose:2.1.0"
用法-Exemple
预览
以下是来自该库的www.example.com的一些gif图像Readme.md:
第一次
lhcgjxsq5#
您可以按照文档中的示例创建一个工具栏,该工具栏在每次向上/向下滚动时都会展开/折叠。
要创建一个仅在列表滚动到顶部时才展开的工具栏,可以对原始示例稍作修改:
通过这样做,您可以灵活地创建自己的
CollapsibleScaffold
,它可以接受scrollBehaviour
、appBarLayout
和list
等参数。例如,通过这种方式,你还可以通过编程计算应用栏的高度,摆脱大量的样板文件,使屏幕上使用的代码整洁干净。
qcuzuvrc6#
合成-折叠-工具栏Jetpack合成的折叠工具栏布局的简单实现
https://github.com/onebone/compose-collapsing-toolbar
jecbmhm37#
我有一些特定的需求,所以我创建了一个简单的impl,它测量navigationIcons和Trainling图标,并试图适应它们之间的内容。忽略重载和测试代码,它不到200行,应该很容易定制为您的特定需求。
https://gist.github.com/fabriciovergara/5de1e8b114fb484bf5f6808a0a107b24
zbdgwd5y8#
这是我在作曲时用来制作折叠效果的
Constraint layout - compose
使用.json5文件创建约束集。创建开始、结束和过渡效果。Motion Layout
将所有小部件添加到合成功能中的运动布局。结果+ Source Code**
添加此依赖项。
implementation("androidx.constraintlayout:constraintlayout-compose:1.1.0-alpha03")
**
STEP 1:
**在原始资源文件夹中创建collapse_toolbar.json5
文件折叠工具栏.json5
**
STEP 2:
**创建可组合函数并添加Motion Layout
主要活动.kt