In https://developer.android.com/jetpack/compose/animation
If you are animating changes to content size:
Use Modifier.contentSize.
However, I cannot find how to access Modifier.contentSize and use it. Any guide?
I think you are talking about the animateContentSize function.
Here I have an example that might help
Column(
modifier = Modifier
.fillMaxWidth()
.padding(16.dp)
// This `Column` animates its size when its content changes.
.animateContentSize()
) {
Row {
Icon(
imageVector = Icons.Default.Info,
contentDescription = null
)
// ...
}
if (expanded) { // If the expanded value is changed then the animateContentSize will be triggered
Spacer(modifier = Modifier.height(8.dp))
Text(
text = stringResource(R.string.lorem_ipsum),
textAlign = TextAlign.Justify
)
}
}
From the Jetpack Compose Animation codelab:
Related
I'm developing an app for a very limited hardware and decided to use jetpack compose.
The problem arises when I need to display a list of cards and the lazyrow used for it gets extremely laggy. For comparison, I picked up a sample project with a recyclerView and used it to display roughly the same list of cards and the scrolling is as smooth as can be. Is jetpack compose inherently slower than xml view or am I doing something wrong?
Compose code (I can't exactly share my code, but the card composable is just a card with some images, icons and text):
#Composable
fun mainComposable(){
...
cardList = remember{ arrayListOf(...) }
lazyList(cardList)
...
}
#Composable
fun lazyList(
cardList: List<CardContent>,
){
LazyRow(
horizontalArrangement = Arrangement.spacedBy(16.dp),
contentPadding = PaddingValues(horizontal = 32.dp)
) {
items(
items = cardList,
key = { it.id }) { item ->
CardComposable(
content = item
)
}
}
}
I've already spent some time searching so I found a lot of optimizations, like running in release mode, setting minifyEnabled and shrinkResources to true on build.gradle and android.enableR8.fullMode to true in gradle.properties, using keys on the LazyRow, etc. They helped, but the scrolling is still fundamentally slower than an equivalent xml view app with recyclerView.
Edit: Added CardComposable code
#Composable
fun CardComposable(
content: Content,
) {
Card(
shape = RoundedCornerShape(8.dp),
elevation = 1.dp,
modifier = Modifier
.width(216.dp)
.height(308.dp)
) {
Column {
Box(contentAlignment = Alignment.TopEnd) {
Image(
painter = painterResource(id = content.image),
modifier = Modifier
.width(216.dp)
.height(164.dp)
)
Box(
contentAlignment = Alignment.Center,
modifier = Modifier.padding(8.dp)
) {
Image(
painter = painterResource(id = R.drawable.button_background),
modifier = Modifier
.width(40.dp)
.height(40.dp)
)
Icon(
painter = painterResource(id = R.drawable.button),
modifier = Modifier
.width(16.dp)
.height(16.dp),
tint = GenericRedColor
)
}
}
Column(
modifier = Modifier
.padding(start = 16.dp)
) {
Row(verticalAlignment = Alignment.CenterVertically) {
Text(
text = content.Name,
maxLines = 2,
color = GenericBlackColor,
fontSize = 16.sp,
fontWeight = FontWeight(500),
overflow = TextOverflow.Ellipsis,
modifier = Modifier
.width(134.dp)
.padding(top = 16.dp)
)
Box(
contentAlignment = Alignment.Center,
modifier = Modifier.padding(start = 16.dp, top = 8.dp)
) {
Icon(
painter = painterResource(id = R.drawable.square_button),
modifier = Modifier
.width(32.dp)
.height(32.dp),
tint = GenericRedColor
)
Icon(
painter = painterResource(id = R.drawable.button_icon),
modifier = Modifier
.width(16.dp)
.height(16.dp),
tint = GenericWhiteColor
)
}
}
Text(
text = content.contentType,
color = GenericLightGrayColor2,
fontSize = 14.sp,
modifier = Modifier.padding(top = 8.dp)
)
Row(
horizontalArrangement = Arrangement.spacedBy(26.dp),
modifier = Modifier.padding(top = 24.dp)
) {
val iconModifier = Modifier
.padding(end = 4.dp)
.width(12.dp)
.height(12.dp)
Row(verticalAlignment = Alignment.CenterVertically) {
Icon(
painter = painterResource(R.drawable.icon_1),
modifier = iconModifier,
tint = GenericLightGrayColor2
)
Text(
text = content.text_1,
fontSize = 14.sp,
color = GenericLightGrayColor2
)
Icon(
painter = painterResource(R.drawable.icon_2),
modifier = iconModifier,
tint = GenericLightGrayColor2
)
Text(
text = content.text2,
fontSize = 14.sp,
color = GenericLightGrayColor2
)
Icon(
painter = painterResource(R.drawable.icon_3),
modifier = iconModifier,
tint = GenericLightGrayColor2
)
Text(
text = content.text3,
fontSize = 14.sp,
color = GenericLightGrayColor2
)
}
}
}
}
}
}
Jetpack Compose is a separate library and is not included in the Android Operating System. Hence the code from the library should be Just In Time (JIT) compiled on the first run. This makes it inherently slower than Android View based code which is Ahead Of Time compiled(AOT) and binaries are stored inside the OS on the device.
This design decision of making Jetpack Compose as a standalone library has its advantages too. It makes it easier to update and use different versions of the library irrespective of the Android OS version, and enables backwards compatibility between compose and android versions.
In iOS, Swift takes the other approach and the Swift binaries are Ahead Of Time compiled and included in the OS. This is one of the main reasons other than Apple's laziness that prevents backwards compatibility in iOS.
Regarding the performance differences between RecyclerView and LazyLists, LazyLists are considerably less performant than RecyclerView. This has multiple reasons. I think it's mainly because Compose is a newer library and is constantly improving. The performance of earlier versions of LazyLists were considerably worse. Performance would be further improved in the upcoming compose versions.
For the time being, Since Jetpack Compose has interoperability with Android View based code, you can use RecyclerView in Compose with minimal performance overhead. Using AndroidView() function in Jetpack Compose.
#Composable
fun MyView(data: State<List<Item>>) {
//This function enables Compose to interop with View based code.
AndroidView(
factory = { context ->
RecyclerView(context).apply {
layoutParams = ViewGroup.LayoutParams(MATCH_PARENT, WRAP_CONTENT)
layoutManager = LinearLayoutManager(context)
adapter = ItemListAdapter().also { it.submitList().value }
}
},
update = { recyclerView ->
//Callback that runs on each recomposition.
}
)
}
I am creating a simple app in Compose with the following layout:
val viewModel: HomeScreenViewModel = viewModel()
val scrollState = rememberScrollState()
Column(
modifier = Modifier.verticalScroll(scrollState).fillMaxSize()
) {
Text(
"Editor picks".uppercase(),
style = MaterialTheme.typography.labelMedium,
modifier = Modifier.padding(8.dp),
)
ElevatedCard(
modifier = Modifier.clickable {
}.fillMaxSize()
) {
Column {
AsyncImage(
viewModel.topVideo?.thumbnailSrc,
contentDescription = null,
modifier = Modifier.fillMaxWidth()
)
}
}
}
The output looks like so:
The image is not taking up all of the space which I need, it only works if I remove the Modifier.verticalScroll, so that the topmost element is just a simple column. When I do that, the output is what I expected:
I am extremely confused as to why this is happening, as I did not believe that making the root element scrollable would have any effect on the output.
I tried to look for a couple of hours online for solutions and I didn't find anything or any info as to why applying this modifier is ruining the layout.
This other person asked a similar question but the answer didn't work.
If you want your image to fill the entire screen width maintaining the correct aspect ratio, you can set contentScale to ContentScale.FillWidth for the Image composable.
Column(
modifier = Modifier.verticalScroll(scrollState)
) {
Text(
"Editor picks".uppercase(),
modifier = Modifier.padding(8.dp),
)
Card(
modifier = Modifier.clickable {}
) {
Column {
AsyncImage(
model = viewModel.topVideo?.thumbnailSrc,
contentDescription = null,
contentScale = ContentScale.FillWidth,
modifier = Modifier.fillMaxWidth()
)
}
}
}
I'm trying to build a custom Drop down menu and I encountered some issues in animating its state. The animation is both laggy and sketchy, even on a real device and even on a release build (APK). The Compose version I'm using is 1.1.1.
Observe the flicker (and the lag?).
The code:
Column(modifier = Modifier.fillMaxWidth()) {
var visible by remember { mutableStateOf(false) }
//header
Column {
Row(
modifier = Modifier
.fillMaxWidth()
.clickable { visible = !visible }
.padding(8.dp),
horizontalArrangement = Arrangement.SpaceBetween
) {
Text(
text = "Click me",
style = MaterialTheme.typography.h6
)
Icon(
modifier = Modifier.rotate(animateFloatAsState(if (visible) 180f else 0f).value),
imageVector = Icons.Default.KeyboardArrowDown,
contentDescription = null
)
}
Divider(
modifier = Modifier.fillMaxWidth(),
color = Color.Black.copy(ContentAlpha.disabled)
)
}
//the 4 items
Column {
(1..4).forEach {
AnimatedVisibility(
visible = visible,
enter = expandVertically(
spring(
stiffness = Spring.StiffnessLow,
visibilityThreshold = IntSize.VisibilityThreshold
),
),
exit = shrinkVertically(),
) {
Column {
Row(
modifier = Modifier
.fillMaxWidth()
.padding(16.dp),
horizontalArrangement = Arrangement.SpaceBetween
) {
Text(
text = "Hello",
style = MaterialTheme.typography.h6
)
Icon(
imageVector = Icons.Default.KeyboardArrowRight,
contentDescription = null
)
}
Divider(
modifier = Modifier.fillMaxWidth(),
color = Color.Black
)
}
}
}
}
}
If I add some bottom padding to the bigger Column or if I make it occupy the whole screen's height, there's no more flicker, but I feel like that is a workaround and also I'm not sure whether the animation is lagging or not, so this wouldn't be a solution to all my problems. The parent Column wraps around its content and as the content size increases, it tries to "keep up" with the new size, but it doesn't do a perfect job. Am I using AnimatedVisibility improperly? How else could I create a custom Drop down menu?
I am using Jetpack Compose for designing dynamic Layout inside android app. Column layout is used to place elements like Vertical layout. This kind of behaviour is achieved using LinearLayout orientation Vertical usage without compose. Inside Column I have added one Image on top and one text I want to display below it. But after placing the Text inside the Compose Column layout it is getting overlapped. How to resolve this.
The code for adding Image and Text is as follows
#Composable
fun GreetingSectionExperimental(
name: String = "Philipp"
) {
//var text by remember { mutableStateOf(TextFieldValue("")) }
Column(
verticalArrangement = Arrangement.Top,
modifier = Modifier.fillMaxSize()
) {
Image(
painterResource(id = R.drawable.ic_launcher_background),
modifier = Modifier
.fillMaxWidth()
.height(80.dp).offset(0.dp,35.dp),
alignment = Alignment.Center,
contentDescription = ""
)
Text(
text = "Login / Sign Up",
Modifier.padding(10.dp).align(CenterHorizontally),
color = textColor, fontSize = 18.sp,
fontWeight = FontWeight.SemiBold
)
}
}
I am getting output as
How to resolve this ? Inside the current what is missing or Column does not support different kind of elements.
You need to remove the property "Offset" from your image
the code should looks like
#Composable
fun GreetingSectionExperimental(
name: String = "Philipp"
) {
//var text by remember { mutableStateOf(TextFieldValue("")) }
Column(
verticalArrangement = Arrangement.Top,
modifier = Modifier.fillMaxSize()
) {
Image(
painterResource(id = R.drawable.ic_launcher_background),
modifier = Modifier
.fillMaxWidth()
.height(80.dp), //Remove the offset here
alignment = Alignment.Center,
contentDescription = ""
)
Text(
text = "Login / Sign Up",
Modifier.padding(10.dp).align(CenterHorizontally),
color = textColor, fontSize = 18.sp,
fontWeight = FontWeight.SemiBold
)
}
}
Best solution is to use Spacer(modifier = Modifier.height(20.dp)). It will create space between the those Elements this in between this is added.
i am new to jetpack compose and also have some experience with flutter so when move to compose its a little bit similar to flutter declarative UI style but i don't quite understand
like in flutter when i want some widget to expanded and want it to take full available space we can use Exapanded widget and if i use this in Column its automatically add flex of 1 or weight in terms of jetpack compose so i want to do same thing in jetpack compose but its seems like jetpack column not automatically and weight to its children and also i cant add
Modifier.weight(1f) in Card of NetworkCardComposable so i have to pass Modifier from as Parameter and in when using this Compoasable in columns this Modifier.weight(1f) have to set through parameter which is odd i think.
#Preview(showBackground = true, widthDp = 500, heightDp = 200)
#Composable
fun NetworkCardComposable(modifier: Modifier = Modifier) {
Card(
modifier = modifier
.fillMaxSize(1f)
.padding(16.dp),
shape = RoundedCornerShape(16.dp),
backgroundColor = colorResource(id = R.color.jazz_color_primary)
) {
Row {
Card(
modifier = Modifier
.weight(2f)
.fillMaxSize()
.padding(8.dp),
shape = RoundedCornerShape(16.dp),
) {
Image(
modifier = Modifier
.padding(8.dp),
painter = painterResource(R.drawable.jazz_logo),
contentDescription = null,
)
}
Row(
modifier = Modifier
.weight(2f)
.fillMaxSize(),
verticalAlignment = Alignment.CenterVertically,
) {
Text(
modifier = Modifier
.weight(1f)
.fillMaxWidth(),
text = "Bundles & Offers",
color = Color.White,
textAlign = TextAlign.Center,
fontSize = MaterialTheme.typography.h5.fontSize,
)
Icon(
modifier = Modifier.size(56.dp),
imageVector = Icons.Default.ChevronRight,
tint = Color.White,
contentDescription = null,
)
}
}
}
}
#Preview(showBackground = true, showSystemUi = true)
#Composable
fun networkCard() {
Column(Modifier.fillMaxSize()) {
NetworkCardComposable(Modifier.weight(1f))
NetworkCardComposable(Modifier.weight(1f))
NetworkCardComposable(Modifier.weight(1f))
NetworkCardComposable(Modifier.weight(1f))
}
}
all thought i have acheive my desired layout but if there is any better approach to this please answer
Jetpack Compose aims at providing the maximum control over the UI. Hence, by default, the elements of a container like column default to wrap content. If you want them to occupy Max available space of the container, the proper way to do it is using Modifier.fillMaxSize()
As far as weights are concerned, if you do not specify then explicitly while adding more than one elements inside a container, the weights are calculated accordingly, based on the minimum required dimensions, (i.e., again, wrapContentSize ())