英文:
CollapsingToolbar in Compose with Button Sticky
问题
I'm here to provide translations for the text you provided. Here's the translation:
我正在尝试在Compose中实现CollapsingToolbar,但我在互联网上找到的示例不符合我的需求。
我以前使用xml,相同的布局如下:
- AppBarLayout
- CollapsingToolbarLayout
- ConstraintLayout
- ImageView(视差,centerCrop)
- Toolbar
- NestedScrollView
- FloatingButton(anchorGravity="bottom|end")
- Button(sticky)
我尝试了很多方法,但困难的部分是要有FloatingButton(可以是任何其他视图,不一定是FAB)。
我实现了视差效果如下:
Box(
modifier = Modifier.graphicsLayer {
translationy = -scroll.value.toFloat() / 2f
alpha = (-1f / headerHeightPx) * scroll.value + 1
}
) { Image... }
我还找到了Compose Collapsing Toolbar库,但如果可能的话,我更喜欢不使用任何库。
我注意到Material3中有一个TopAppBar,也许它会起作用。
更新
我还尝试了Material3库,但效果不如预期,因为我的行为不符合预期。
我考虑创建一个Box,或者因为我想要阴影效果,可以创建一个Surface,在其中包含Icon和Image,以及中心的Box。
我创建了一个简单的图像来描述我的想法,也许这是一个好的开始。
在这里,我需要将列表的滚动链接到.value,以创建动画或更改Image的Alpha(或视差效果),然后一旦折叠,就向Surface添加阴影。
在第二张图中,中心的框没有以相同的方式对齐,但我不想更改它,这是一个绘制错误。
所以总结一下:
创建这个Composable并“伪造”TopAppBar,以便在高度达到56.dp时停止“折叠”。
滚动时,Image应该执行动画或更改其Alpha(或视差效果),然后将背景转换为白色作为过渡。
然后我看到的问题是,我需要添加一些计算来检测exitUntilCollapsed的行为,比如说我想要在列表的第一个项目可见时“取消折叠”。
英文:
I'm trying to implement the CollapsingToolbar in Compose
but the examples I've found through the internet don't accomplish my needs.
I used to use xml and the same layout was :
-AppBarLayout
-CollapsingToolbarLayout
-ConstraintLayout
-ImageView(parallax, centerCrop)
-Toolbar
-NestedScrollView
-FloatingButton (anchorGravity="bottom|end")
-Button(sticky)
I've tried many things, but the hard thing is to have the FloatingButton
(can be any other view not specially a FAB
).
Demo :
The parallax
effect I did it as follows :
Box(
modifier = Modifier.graphicsLayer {
translationy = -scroll.value.toFloat() / 2f
alpha = (-1f / headerHeightPx) * scroll.value + 1
}
) { Image... }
I also found the Compose Collapsing Toolbar library
but I'd prefer to not use any library if possible.
I've seen that there's a Material3 there's a TopAppBar
and perhaps it would work.
Update
I've also tried the Material3
library but is not working as expected since my behaviour is breaking the how it should be.
What I'm thinking is to create a Box
or since I want elevation a Surface
and on it, contain inside the Icon
and the Image
and also the center Box
.
I've created a simple image to describe what I've thought and perhaps is a good way to start.
Here I'd need to link the scroll of the List
and with the .value
create the animation or change the Alpha
of the Image
and then once is collapsed add the elevation
to the Surface
.
In the second image the center box is not aligned the same way, but I don't want to change it, it was a miss paint.
So the recap should be :
Create this Composable
and fake the TopAppBar
so it should stop collapsing when the height is 56.dp
as the min size of the TopAppBar
.
When scrolling the Image
should animate or change it's alpha (or parallax effect) and then convert the background to white as a transition.
And then the problem I'm seeing is that I'd need to add some calculations to detect the behaviour of exitUntilCollapsed
let's say I want to un-collapse once the first item of the list is visible.
答案1
得分: 1
根据您的绘图建议,我尝试了一下:
import androidx.compose.animation.core.animateDpAsState
import androidx.compose.foundation.Image
import androidx.compose.foundation.background
import androidx.compose.foundation.layout.Arrangement
import androidx.compose.foundation.layout.Box
import androidx.compose.foundation.layout.Column
import androidx.compose.foundation.layout.Row
import androidx.compose.foundation.layout.Spacer
import androidx.compose.foundation.layout.fillMaxSize
import androidx.compose.foundation.layout.fillMaxWidth
import androidx.compose.foundation.layout.height
import androidx.compose.foundation.layout.padding
import androidx.compose.foundation.layout.size
import androidx.compose.foundation.layout.wrapContentHeight
import androidx.compose.foundation.lazy.LazyColumn
import androidx.compose.foundation.lazy.items
import androidx.compose.foundation.lazy.rememberLazyListState
import androidx.compose.foundation.shape.RoundedCornerShape
import androidx.compose.material.Button
import androidx.compose.material.ButtonDefaults
import androidx.compose.material.Divider
import androidx.compose.material.Icon
import androidx.compose.material.IconButton
import androidx.compose.material.Text
import androidx.compose.material.icons.Icons
import androidx.compose.material.icons.filled.ArrowBack
import androidx.compose.runtime.Composable
import androidx.compose.runtime.getValue
import androidx.compose.ui.Alignment
import androidx.compose.ui.Alignment.Companion.BottomCenter
import androidx.compose.ui.Alignment.Companion.Center
import androidx.compose.ui.Alignment.Companion.CenterVertically
import androidx.compose.ui.Alignment.Companion.TopStart
import androidx.compose.ui.Modifier
import androidx.compose.ui.graphics.Color
import androidx.compose.ui.layout.ContentScale
import androidx.compose.ui.res.painterResource
import androidx.compose.ui.text.style.TextAlign
import androidx.compose.ui.unit.dp
import androidx.compose.ui.unit.max
import com.canopas.base.ui.AppTheme
import com.canopas.base.ui.ColorPrimary
import com.canopas.base.ui.White
import com.canopas.ui.R
import kotlin.math.min
Column(modifier = Modifier.fillMaxSize()) {
val items = (1..100).map { "Item $it" }
val lazyListState = rememberLazyListState()
val scrollOffset: Float = min(
1f,
1 - (lazyListState.firstVisibleItemScrollOffset / 200f + lazyListState.firstVisibleItemIndex)
)
val imageSize by animateDpAsState(targetValue = max(0.dp, 300.dp * scrollOffset))
Box(contentAlignment = Center) {
Image(
painterResource(id = R.drawable.ic_intro_screen_image1),
modifier = Modifier.size(imageSize),
contentScale = ContentScale.FillWidth,
contentDescription = "Food Category thumbnail"
)
Row(
Modifier
.height(56.dp)
.background(Color.White).align(BottomCenter),
horizontalArrangement = Arrangement.Center, verticalAlignment = CenterVertically
) {
Text(text = "Title", modifier = Modifier.padding(vertical = 12.dp).fillMaxSize(), textAlign = TextAlign.Center)
}
IconButton(onClick = { /*TODO*/ }, modifier = Modifier.align(TopStart)) {
Icon(imageVector = Icons.Default.ArrowBack, contentDescription = "")
}
}
LazyColumn(
Modifier
.fillMaxWidth()
.weight(1f),
lazyListState,
) {
items(items) {
Text(
text = it,
Modifier
.background(Color.White)
.fillMaxWidth()
.padding(8.dp)
)
}
}
Column(
modifier = Modifier.fillMaxWidth(),
horizontalAlignment = Alignment.CenterHorizontally
) {
Divider(modifier = Modifier.fillMaxWidth(), color = ColorPrimary)
Spacer(modifier = Modifier.height(10.dp))
Button(
onClick = {
},
shape = RoundedCornerShape(25.dp),
colors = ButtonDefaults.buttonColors(
backgroundColor = ColorPrimary,
),
elevation = ButtonDefaults.elevation(
defaultElevation = 0.dp,
pressedElevation = 0.dp,
disabledElevation = 0.dp,
hoveredElevation = 0.dp,
focusedElevation = 0.dp
),
modifier = Modifier
.wrapContentHeight()
) {
Text(
text = "Button",
color = White,
style = AppTheme.typography.buttonStyle,
modifier = Modifier.padding(vertical = 6.dp)
)
}
Spacer(modifier = Modifier.height(10.dp))
}
}
[1]: https://i.stack.imgur.com/zpjhC.gif
<details>
<summary>英文:</summary>
Just gave it a try according to your drawing suggestions:
import androidx.compose.animation.core.animateDpAsState
import androidx.compose.foundation.Image
import androidx.compose.foundation.background
import androidx.compose.foundation.layout.Arrangement
import androidx.compose.foundation.layout.Box
import androidx.compose.foundation.layout.Column
import androidx.compose.foundation.layout.Row
import androidx.compose.foundation.layout.Spacer
import androidx.compose.foundation.layout.fillMaxSize
import androidx.compose.foundation.layout.fillMaxWidth
import androidx.compose.foundation.layout.height
import androidx.compose.foundation.layout.padding
import androidx.compose.foundation.layout.size
import androidx.compose.foundation.layout.wrapContentHeight
import androidx.compose.foundation.lazy.LazyColumn
import androidx.compose.foundation.lazy.items
import androidx.compose.foundation.lazy.rememberLazyListState
import androidx.compose.foundation.shape.RoundedCornerShape
import androidx.compose.material.Button
import androidx.compose.material.ButtonDefaults
import androidx.compose.material.Divider
import androidx.compose.material.Icon
import androidx.compose.material.IconButton
import androidx.compose.material.Text
import androidx.compose.material.icons.Icons
import androidx.compose.material.icons.filled.ArrowBack
import androidx.compose.runtime.Composable
import androidx.compose.runtime.getValue
import androidx.compose.ui.Alignment
import androidx.compose.ui.Alignment.Companion.BottomCenter
import androidx.compose.ui.Alignment.Companion.Center
import androidx.compose.ui.Alignment.Companion.CenterVertically
import androidx.compose.ui.Alignment.Companion.TopStart
import androidx.compose.ui.Modifier
import androidx.compose.ui.graphics.Color
import androidx.compose.ui.layout.ContentScale
import androidx.compose.ui.res.painterResource
import androidx.compose.ui.text.style.TextAlign
import androidx.compose.ui.unit.dp
import androidx.compose.ui.unit.max
import com.canopas.base.ui.AppTheme
import com.canopas.base.ui.ColorPrimary
import com.canopas.base.ui.White
import com.canopas.ui.R
import kotlin.math.min
Column(modifier = Modifier.fillMaxSize()) {
val items = (1..100).map { "Item $it" }
val lazyListState = rememberLazyListState()
val scrollOffset: Float = min(
1f,
1 - (lazyListState.firstVisibleItemScrollOffset / 200f + lazyListState.firstVisibleItemIndex)
)
val imageSize by animateDpAsState(targetValue = max(0.dp, 300.dp * scrollOffset))
Box(contentAlignment = Center) {
Image(
painterResource(id = R.drawable.ic_intro_screen_image1),
modifier = Modifier.size(imageSize),
contentScale = ContentScale.FillWidth,
contentDescription = "Food Category thumbnail"
)
Row(
Modifier
.height(56.dp)
.background(Color.White).align(BottomCenter),
horizontalArrangement = Arrangement.Center, verticalAlignment = CenterVertically
) {
Text(text = "Title", modifier = Modifier.padding(vertical = 12.dp).fillMaxSize(), textAlign = TextAlign.Center)
}
IconButton(onClick = { /TODO/ }, modifier = Modifier.align(TopStart)) {
Icon(imageVector = Icons.Default.ArrowBack, contentDescription = "")
}
}
LazyColumn(
Modifier
.fillMaxWidth()
.weight(1f),
lazyListState,
) {
items(items) {
Text(
text = it,
Modifier
.background(Color.White)
.fillMaxWidth()
.padding(8.dp)
)
}
}
Column(
modifier = Modifier.fillMaxWidth(),
horizontalAlignment = Alignment.CenterHorizontally
) {
Divider(modifier = Modifier.fillMaxWidth(), color = ColorPrimary)
Spacer(modifier = Modifier.height(10.dp))
Button(
onClick = {
},
shape = RoundedCornerShape(25.dp),
colors = ButtonDefaults.buttonColors(
backgroundColor = ColorPrimary,
),
elevation = ButtonDefaults.elevation(
defaultElevation = 0.dp,
pressedElevation = 0.dp,
disabledElevation = 0.dp,
hoveredElevation = 0.dp,
focusedElevation = 0.dp
),
modifier = Modifier
.wrapContentHeight()
) {
Text(
text = "Button",
color = White,
style = AppTheme.typography.buttonStyle,
modifier = Modifier.padding(vertical = 6.dp)
)
}
Spacer(modifier = Modifier.height(10.dp))
}
}
[![enter image description here][1]][1]
[1]: https://i.stack.imgur.com/zpjhC.gif
</details>
通过集体智慧和协作来改善编程学习和解决问题的方式。致力于成为全球开发者共同参与的知识库,让每个人都能够通过互相帮助和分享经验来进步。
评论