I've a data class:
data class Feed_Status(val img:Int, val name_id: String)
I've a class:
class Feed_helper {
fun Image_getter(): List<() -> Feed_Status> {
val Images = listOf {
Feed_Status(R.drawable.image_demo1, "name1")
Feed_Status(R.drawable.image_demo2, "name2")
Feed_Status(R.drawable.image_demo3, "name3")
Feed_Status(R.drawable.image_demo4, "name4")
Feed_Status(R.drawable.image_demo5, "name5")
Feed_Status(R.drawable.image_demo6, "name6")
Feed_Status(R.drawable.image_demo7, "name7")
Feed_Status(R.drawable.image_demo8, "name8")
Feed_Status(R.drawable.image_demo9, "name9")
Feed_Status(R.drawable.image_demo10, "name10")
Feed_Status(R.drawable.image_demo11, "name11")
Feed_Status(R.drawable.image_demo12, "name12")
Feed_Status(R.drawable.image_demo13, "name13")
Feed_Status(R.drawable.image_demo14, "name14")
Feed_Status(R.drawable.image_demo15, "name15")
Feed_Status(R.drawable.image_demo16, "name16")
Feed_Status(R.drawable.image_demo17, "name17")
Feed_Status(R.drawable.image_demo18, "name18")
Feed_Status(R.drawable.image_demo19, "name19")
Feed_Status(R.drawable.image_demo20, "name20")
Feed_Status(R.drawable.image_demo21, "name21")
Feed_Status(R.drawable.image_demo22, "name22")
Feed_Status(R.drawable.image_demo23, "name23")
Feed_Status(R.drawable.image_demo24, "name24")
Feed_Status(R.drawable.image_demo25, "name25")
Feed_Status(R.drawable.image_demo25, "name26")
}
return Images
}
}
through which I'm calling items() in lazyRow
#Composable
fun feed() {
LazyColumn(
reverseLayout = false,
modifier = Modifier
.fillMaxSize(),
userScrollEnabled = true
) {
// Status(es)
item {
LazyRow(
reverseLayout = false,
modifier = Modifier
.fillMaxWidth()
.height(100.dp),
horizontalArrangement = Arrangement.SpaceBetween,
userScrollEnabled = true
) {
val statuses = Feed_helper().Image_getter()
items(statuses) { status ->
Column(
verticalArrangement = Arrangement.Center,
modifier = Modifier
.width(80.dp)
) {
Card(
shape = CircleShape,
modifier = Modifier
.padding(8.dp)
.size(64.dp)
) {
Image(
painterResource(id = status.img),
contentDescription = status.name_id + "'s status",
contentScale = ContentScale.Crop
)
}
Text(
text = status.name_id,
modifier = Modifier.fillMaxWidth(),
textAlign = TextAlign.Center,
)
}
}
}
}
}
}
But whenever I'm calling element from statuses through statuses in items() it is giving me Reference Not found!
Callers:
painterResource(id = status.img) in Image()
contentDescription = status.name_id + "'s status" in Image()
text = status.name_id in Text
All callers are in items(statuses){ status ->
I've been trying to solve this for hours. So any help will be very apreciated.
If you find any typo please update it or tell me to fix.
PS: This is my first time here and I have an almost zero experience on android development and Kotlin. I developed terminal apps and worked on ML kinda work in Python, C++, C. So I may need more information in explanation. I started learning Android Development a week ago only.
Edit: You can ask me any more information.
Peace
Change the return type of Image_getter to List<Feed_status> instead of it having a type of lambda, and change the braces to parenthesis when you declare the list of them.
class Feed_helper {
fun Image_getter(): List<Feed_Status> { // change it to this instead of type of lambda `() -> Feed_status`
val Images = listOf ( // change it to this instead of braces
...
)
return Images
}
}
and import
import androidx.compose.foundation.lazy.items
Related
I am writing a small gallery app for my cat. It has a button by clicking on which a new PhotoItem is added to the displayed list, but it appears only after phone rotation and I want it to appear on the screen right after button was clicked.
Right now everything is stored in a mutableList inside savedStateHandle.getStateFlow but I also tried regular MutableStateFlow and mutableStateOf and it didn't help. I havent really used jatpack compose and just can't figure what to do (
App
#SuppressLint("UnusedMaterialScaffoldPaddingParameter")
#Composable
fun BebrasPhotosApp() {
val galaryViewModel = viewModel<GalaryViewModel>()
val allPhotos by galaryViewModel.loadedPics.collectAsState()
Scaffold(topBar = { BebraTopAppBar() }, floatingActionButton = {
FloatingActionButton(
onClick = { galaryViewModel.addPicture() },
backgroundColor = MaterialTheme.colors.onBackground
) {
Icon(
imageVector = Icons.Rounded.Add,
contentDescription = "Add Photo",
tint = Color.White,
)
}
}) {
LazyColumn(modifier = Modifier.background(color = MaterialTheme.colors.background)) {
items(allPhotos) {
PhotoItem(bebra = it)
}
}
}
}
ViewModel
class GalaryViewModel(
private val savedStateHandle: SavedStateHandle
) : ViewModel() {
val loadedPics = savedStateHandle.getStateFlow(
"pics", initialValue = mutableListOf<Bebra>(
Bebra(R.string.photo_1, R.string.desc_1, R.drawable.bebra_pic_1, R.string.add_desc_1),
Bebra(R.string.photo_2, R.string.desc_2, R.drawable.bebra_pic_2, R.string.add_desc_2),
Bebra(R.string.photo_3, R.string.desc_3, R.drawable.bebra_pic_3, R.string.add_desc_3)
)
)
fun addPicture() {
val additionalBebraPhoto = Bebra(
R.string.photo_placeholder,
R.string.desc_placeholder,
R.drawable.placeholder_cat,
R.string.add_desc_placeholder
)
savedStateHandle.get<MutableList<Bebra>>("pics")!!.add(additionalBebraPhoto)
}
}
PhotoItem
#Composable
fun PhotoItem(bebra: Bebra, modifier: Modifier = Modifier) {
var expanded by remember { mutableStateOf(false) }
Card(elevation = 4.dp, modifier = modifier
.padding(8.dp)
.clickable { expanded = !expanded }) {
Column(
modifier = modifier
.padding(8.dp)
.animateContentSize(
animationSpec = spring(
dampingRatio = Spring.DampingRatioMediumBouncy,
stiffness = Spring.StiffnessLow
)
)
) {
Text(
text = stringResource(id = bebra.PicNumber),
style = MaterialTheme.typography.h1,
modifier = modifier.padding(bottom = 8.dp)
)
Text(
text = stringResource(id = bebra.PicDesc),
style = MaterialTheme.typography.body1,
modifier = modifier.padding(bottom = 8.dp)
)
Image(
painter = painterResource(id = bebra.Picture),
contentDescription = stringResource(id = bebra.PicDesc),
contentScale = ContentScale.Crop,
modifier = Modifier
.fillMaxWidth()
.height(256.dp)
.clip(RoundedCornerShape(12))
)
if (expanded) {
BebraAdditionalDesc(bebra.additionalDesc)
}
}
}
}
Bebra Data class
data class Bebra(
#StringRes val PicNumber: Int,
#StringRes val PicDesc: Int,
#DrawableRes val Picture: Int,
#StringRes val additionalDesc: Int
)
So, I am also not super familiar with JC, but from first glance it looks like your method, addPicture() - which is called when the user taps on the button, does not update the state, therefore there's no recomposition happening, so the UI does not get updated.
Check:
fun addPicture() {
// ...
savedStateHandle.get<MutableList<Bebra>>("pics")!!.add(additionalBebraPhoto)
}
So here you are basically adding a new item to savedStateHandle, which I assume does not trigger a recomposition.
What I think you need to do, is to update loadedPics, somehow.
However, loadedPics is a StateFlow, to be able to update it you would need a MutableStateFlow.
For simplicity, this is how you would do it if you were operating with a list of strings:
// declare MutableStateFlow that can be updated and trigger recomposition
val _loadedPics = MutableStateFlow(
savedStateHandle.get<MutableList<String>>("pics") ?: mutableListOf()
)
// use this in the JC layout to listen to state changes
val loadedPics: StateFlow<List<String>> = _loadedPics
// addPicture:
val prevList = _loadedPics.value
prevList.add("item")
_loadedPics.value = prevList // triggers recomposition
// here you probably will want to save the item in the
// `savedStateHandle` as you already doing.
So i began learning jetpack compose and i started following a simple tutorial on youtube to list Harry Potter actors in a LazyColumn.
However, i was not able to display a simple Toast message by passing the actors name as a Lambda PArameter as shown below :
#Composable
fun HomeScreen(context: Context) {
val viewModel = getViewModel<HomeViewModel>()
val state by viewModel.state.collectAsState()
LazyColumn {
if (state.isEmpty()) {
item {
CircularProgressIndicator(
modifier = Modifier
.fillMaxSize()
.wrapContentSize(align = Alignment.Center)
)
}
} else {
items(state) { character ->
CharacterImageCard(character) {
Toast.makeText(context, it, Toast.LENGTH_SHORT).show()
Log.e("TAG", "HomeScreen:$it ", )
}
}
}
}
}
PS: note that theres a Log.e, it prints the characters name just fine.
heres the Character Image card
#Composable
fun CharacterImageCard(character: CharactersItemX, selectedItem: (String) -> Unit) {
val imagePainter = rememberImagePainter(data = character.image)
Card(
shape = MaterialTheme.shapes.medium,
modifier = Modifier
.padding(16.dp)
) {
Row(Modifier.clickable(onClick = { selectedItem(character.name) })) {
Box {
Image(
painter = imagePainter,
contentDescription = character.name,
modifier = Modifier
.fillMaxWidth()
.height(200.dp),
contentScale = ContentScale.Fit,
)
Surface(
color = MaterialTheme.colors.primary.copy(alpha = 0.5f),
modifier = Modifier
.align(Alignment.BottomCenter),
contentColor = MaterialTheme.colors.surface
) {
Column(
modifier = Modifier
.fillMaxWidth()
.padding(4.dp)
) {
Text(
text = character.name,
style = MaterialTheme.typography.h6
)
Text(
text = character.house,
style = MaterialTheme.typography.body2
)
}
}
}
}
}
}
I call HomeScreen on my MainActivty
Example:
#Composable
fun HomeScreen(context: Context) {
val viewModel = getViewModel<HomeViewModel>()
val state by viewModel.state.collectAsState()
// use localContext
val localContext = LocalContext.current
LazyColumn {
if (state.isEmpty()) {
item {
CircularProgressIndicator(
modifier = Modifier
.fillMaxSize()
.wrapContentSize(align = Alignment.Center)
)
}
} else {
items(state) { character ->
CharacterImageCard(character) {
Toast.makeText(localContext, it, Toast.LENGTH_SHORT).show()
Log.e("TAG", "HomeScreen:$it ", )
}
}
}
}
}
I reproduced your code and Toast works fine. I don't know what's the problem, maybe try getting context from composable instead of passing it from the main activity.
I am using HorizontalViewPager from the Accompanist library, and one thing I've noticed is a delay in page transitions when swiping.
For some reason the page only really changes the text when you have swiped more than halfway through the next page. If you swipe less than halfway through the next page the text reverts back to its previous state. What I want is that the user immediately sees the right text state, I don't want the user to have to swipe more than halfway to see the changes.
Composable:
#Composable
fun TutorialPage(page: TutorialPage) {
Column {
Spacer(
modifier = Modifier.height(16.dp)
)
Column(
modifier = Modifier
.weight(1f)
.fillMaxSize(),
horizontalAlignment = Alignment.CenterHorizontally,
) {
}
Spacer(
modifier = Modifier.height(16.dp)
)
Column(
horizontalAlignment = Alignment.CenterHorizontally,
verticalArrangement = Arrangement.Bottom,
) {
Column(
modifier = Modifier
.padding(start = 32.dp, end = 32.dp)
) {
Text(
page.title,
style = MaterialTheme.typography.displayMedium,
textAlign = TextAlign.Center,
fontFamily = FontFamily(
Font(R.font.manrope_medium)
)
)
Spacer(
modifier = Modifier.height(8.dp)
)
Text(
page.description,
style = MaterialTheme.typography.titleMedium,
textAlign = TextAlign.Center,
fontFamily = FontFamily(
Font(R.font.manrope_medium)
),
modifier = Modifier.alpha(0.7f)
)
}
Spacer(
modifier = Modifier.height(32.dp)
)
Row(
modifier = Modifier.padding(16.dp)
) {
}
}
}
}
#OptIn(ExperimentalPagerApi::class)
#Composable
fun TutorialScreen(
viewModel: TutorialScreenViewModel,
state: TutorialScreenState
) {
val pagerState = rememberPagerState()
Column {
Column(
modifier = Modifier.weight(1f)
) {
HorizontalPager(
count = state.tutorial.pages.size,
state = pagerState
) {
TutorialPage(state.tutorial.pages[pagerState.currentPage])
}
}
Row(
modifier = Modifier
.padding(16.dp)
.fillMaxWidth(),
horizontalArrangement = Arrangement.Center
) {
repeat(state.tutorial.pages.size) {
Box(
modifier = Modifier
.clip(CircleShape)
.size(10.dp)
.background(if (it == pagerState.currentPage) Color.Gray else Color.LightGray)
) {
}
Spacer(
modifier = Modifier.width(8.dp)
)
}
}
}
}
View model:
#HiltViewModel
class TutorialScreenViewModel #Inject constructor() : ViewModel() {
private val _state = mutableStateOf(
TutorialScreenState(tutorial)
)
val state: State<TutorialScreenState>
get() = _state
private val tutorial: Tutorial
get() =
Tutorial.Builder()
.addPage(
TutorialPage(
title = "Creating a project",
description = "Creating a project is easy! To do so, simply tap the squircle-shaped plus button at the bottom right of your home screen.",
image = R.drawable.tutorial_img_1
)
)
.addPage(
TutorialPage(
title = "Setting up a project",
description = "From then, you will see a screen in which you can input your project's name, width, and height. Confused where to start? Feel free to use some of PixaPencil's ready made quick presets or add your own for future use.",
image = R.drawable.tutorial_img_2
)
)
.addPage(
TutorialPage(
title = "Let's draw",
description = "Now, you should be navigated to your drawing screen. The screen is divided into three main sections: your current color palette, your drawing view, and your tabs.",
image = 0
)
)
.addPage(
TutorialPage(
title = "Tools",
description = "PixaPencil has a wide variety of tools to get you started, such as: pencil tool, line tool, paint bucket tool, rectangle tool, square tool, ellipse tool, circle tool, and more.",
image = 0
)
)
.addPage(
TutorialPage(
title = "Features",
description = "As well as tools, PixaPencil has a wide variety of features get you started, such as: color palette functionality, replace color, import Lospec palette, pixel perfect mode, canvas filters, brushes, and more.",
image = 0
)
)
.addPage(
TutorialPage(
title = "Free and open source",
description = "PixaPencil is 100% free (as in freedom) and open source, the code is available on GitHub for anyone to view, download, or extend. We are always open for contributors to the project.",
image = 0
)
)
.addPage(
TutorialPage(
title = "Join the community",
description = "PixaPencil has a vibrant community on Discord, which you can join here.",
image = 0
)
)
.build()
}
Tutorial:
class Tutorial private constructor(val pages: MutableList<TutorialPage>) {
class Builder {
private val pages: MutableList<TutorialPage> = mutableListOf()
fun addPage(page: TutorialPage): Builder {
pages.add(page)
return this
}
fun build(): Tutorial {
return Tutorial(pages)
}
}
}
Page:
data class TutorialPage(
val title: String,
val description: String,
)
I've tried to look online (the documentation) to find solutions, and I didn't find any nor did I find someone with the same problem with the Accompanist library.
Rather than using TutorialPage(state.tutorial.pages[pagerState.currentPage]) use TutorialPage(state.tutorial.pages[it]) where "it" is the builder index that pager gives you in lambda. Hope it helps :d
I'm praticing Compose navigation. I made an App that displays a ListItem with images, on what I call HomeScreen, when a particular item is clicked it navigates to a destination containing more information/ Details, on what I called HomeInfoScreen. And to do this I used Navigation compose Arugments. I basically did this by benchmarking the Android Developers Rally Compose.
But the problem is whenever I click on any of the rows it takes me to the details of the first Item, no matter which of them I click.
I believe the problem is from coming from HomeScreen (remember, I said this was the destination I'm navigating from)
I've previously researched, and got an Idea of passing my model object as a parameter.
But I think I did something wrong, because I get errors, and I don't know how to fix it.
Please understand that I arranged the code, in multiple composables, in this format;
SecondBodyElement -> SecondBodyGrid ------> HomeContentScreen
And Finally I call HomeContentScreen on the HomeScreen.
SecondBodyElement;
#Composable
fun SecondBodyElement(
#StringRes text: Int,
#DrawableRes drawable: Int,
modifier: Modifier = Modifier,
onHomeCardClick: (Int) -> Unit
) {
Surface(
shape = MaterialTheme.shapes.small,
elevation = 10.dp,
modifier = modifier
.padding(horizontal = 12.dp, vertical = 12.dp)
.clip(shape = RoundedCornerShape(corner = CornerSize(8.dp)))
.clickable { onHomeCardClick(drawable) }
) {
Column(
horizontalAlignment = Alignment.Start,
modifier = Modifier
) {
Image(
painter = painterResource(drawable),
contentDescription = null,
contentScale = ContentScale.Crop,
modifier = Modifier
.fillMaxWidth()
.height(300.dp)
)
Text(
text = stringResource(text),
style = MaterialTheme.typography.h3,
maxLines = 3,
modifier = Modifier.padding(horizontal = 8.dp, vertical = 16.dp)
)
}
}
}
SecondyBodyGrid;
#Composable
fun SecondBodyGrid(
onHomeCardClick: (Int) -> Unit = {},
) {
LazyVerticalGrid(
columns = GridCells.Fixed(1),
contentPadding = PaddingValues(horizontal = 16.dp),
horizontalArrangement = Arrangement.spacedBy(8.dp),
verticalArrangement = Arrangement.spacedBy(8.dp),
modifier = Modifier
.height(1450.dp)
//.disabledVerticalPointerInputScroll()
) {
items(SecondBodyData) { item ->
SecondBodyElement(
onHomeCardClick = onHomeCardClick,
drawable = item.drawable,
text = item.text,
modifier = Modifier
.height(380.dp)
.clickable { onHomeCardClick(item.drawable + item.text) }
)
}
}
}
HomeContentScreen;
#Composable
fun HomeContentScreen(
modifier: Modifier = Modifier,
onHomeCardClick: (String) -> Unit,
accountType: String? = HomeInfoModel.homeInfoModelList.first().title
) {
val homeInfo = remember(accountType) { HomeInfoModel.getHomeInfo(accountType) }
Column(
modifier
.verticalScroll(rememberScrollState())
.padding(vertical = 16.dp)
) {
HomeQuote()
HomeTitleSection(title = R.string.favorite_collections) {
SecondBodyGrid { onHomeCardClick(homeInfo.title) }
}
}
}
And Finally HomeScreen;
#Composable
fun HomeScreen(onHomeCardClick: (String) -> Unit) {
HomeContentScreen(onHomeCardClick = onHomeCardClick)
}
Please like I said I'm practicing, I don't know what you are going to need. But I'm going to add my NavHost (Nav Graph) and the Model file, just incase, If you need any other thing I'm more than happy to provide.
Model, HomeInfoModel;
data class HomeInfoData(
val id: Int,
val title: String,
val sex: String,
val age: Int,
val description: String,
val homeInfoImageId: Int = 0
)
object HomeInfoModel {
val homeInfoModelList = listOf(
HomeInfoData(
id = 1,
title = "There's Hope",
sex = "Male",
age = 14,
description = "Monty enjoys chicken treats and cuddling while watching Seinfeld.",
homeInfoImageId = R.drawable.ab1_inversions
),
....
)
fun getHomeInfo(accountName: String?): HomeInfoData {
return homeInfoModelList.first { it.title == accountName }
}
}
My NavHost (Nav Graph);
....
composable(route = Home.route) {
HomeScreen(
onHomeCardClick = { accountType ->
navController.navigateToHomeInfoScreen(accountType)
}
)
}
composable(
route = HomeInfoDestination.routeWithArgs,
arguments = HomeInfoDestination.arguments,
) { navBackStackEntry ->
// Retrieve the passed argument
val accountType =
navBackStackEntry.arguments?.getString(HomeInfoDestination.accountTypeArg)
// Pass accountType
HomeInfoScreen(accountType)
}
....
}
}
....
private fun NavHostController.navigateToHomeInfoScreen(accountType: String) {
this.navigateSingleTopTo("${HomeInfoDestination.route}/$accountType")
}
I think the problem is from the Homescreen but I don't really know, So I'm going to Add the HomeInfoScreen, Sorry making this question any longer.
HomeInfoScreen;
#Composable
fun HomeInfoScreen(
accountType: String? = HomeInfoModel.homeInfoModelList.first().title
) {
DisplayHomeInfo(accountType)
}
#Composable
fun WelcomeText() {
Text(
text = "Welcome, to Home Information",
style = MaterialTheme.typography.h3,
modifier = Modifier.padding(horizontal = 12.dp, vertical = 18.dp)
)
}
#Composable
fun HomeInfoDetails(
accountType: String? = HomeInfoModel.homeInfoModelList.first().title
) {
val homeInfo = remember(accountType) { HomeInfoModel.getHomeInfo(accountType) }
Column(
modifier = Modifier
.fillMaxWidth()
.padding(10.dp)
) {
Image(
painter = painterResource(id = homeInfo.homeInfoImageId),
contentDescription = null,
modifier = Modifier
.fillMaxWidth()
.clip(shape = RoundedCornerShape(topEnd = 4.dp, bottomEnd = 4.dp)),
contentScale = ContentScale.Crop
)
Spacer(modifier = Modifier.height(16.dp))
Text(
text = homeInfo.title,
style = MaterialTheme.typography.h3
)
Spacer(modifier = Modifier.height(16.dp))
Text(
text = homeInfo.description,
style = MaterialTheme.typography.h5
)
}
}
// Step: Home screen - Scrolling
#Composable
fun DisplayHomeInfo(
accountType: String? = HomeInfoModel.homeInfoModelList.first().title
) {
Column(
Modifier
.verticalScroll(rememberScrollState())
.padding(vertical = 16.dp)
) {
WelcomeText()
HomeInfoDetails(accountType)
}
}
For Clarity Sake; How can I navigate to the exact item when it is clicked on the SuccessScreen.
I'll sinerely be greatful for any help. Thanks a lot in advance for your help.
In case somebody needs this, I've been able to do this. I followed a website's Developer's Breach tutorial.
Hi i'm trying to implement a lazycolumn of a list of posts, I tested it on the emulator api 21 and 29 and it looks kinda smooth on the api 29 it's a little bit laggy, when I tested it on a physical device it was lagging, It looks like it's skipping frames or something..
I tried to remove some views that uses imageVector to see if that was the problem and still the same problem.
This is my composable view:
#Composable
fun HomePostView(
category: String,
imagesUrl: List<String> = listOf(imageHolder),
doctorProfileImage: String = imageUrl,
title: String,
subTitle: String
) {
Card(
shape = PostCardShape.large, modifier = Modifier
.padding(horizontal = 3.dp)
.fillMaxWidth()
) {
Column {
PostTopView(
category = category,
onOptionsClicked = { /*TODO option click*/ },
onBookmarkClicked = {/*TODO bookmark click*/ })
CoilImage(
data = imagesUrl[0],
fadeIn = true,
contentDescription = "post_image",
modifier = Modifier
.fillMaxWidth()
.requiredHeight(190.dp)
.padding(horizontal = contentPadding),
contentScale = ContentScale.Crop
)
Spacer(modifier = Modifier.height(10.dp))
PostDoctorContent(
doctorProfileImage = doctorProfileImage,
title = title,
subTitle = subTitle
)
Spacer(modifier = Modifier.height(contentPadding))
PostBottomView(likesCount = 293, commentsCount = 22)
Spacer(modifier = Modifier.height(contentPadding))
}
}
Spacer(modifier = Modifier.height(10.dp))
}
#Composable
private fun PostDoctorContent(doctorProfileImage: String, title: String, subTitle: String) {
Row(
modifier = Modifier
.fillMaxWidth()
.padding(horizontal = contentPadding)
) {
CoilImage(data = doctorProfileImage,
contentScale = ContentScale.Crop,
contentDescription = null,
fadeIn = true,
modifier = Modifier
.size(30.dp)
.clip(CircleShape)
.clickable {
/*Todo on doctor profile clicked*/
})
Column {
Text(
text = title, fontSize = 14.sp, maxLines = 1,
overflow = TextOverflow.Ellipsis,
modifier = Modifier.padding(horizontal = contentPadding)
)
Text(
text = subTitle,
fontSize = 11.sp,
color = LightTextColor,
maxLines = 2,
overflow = TextOverflow.Ellipsis,
modifier = Modifier.padding(horizontal = contentPadding)
)
}
}
}
#Composable
private fun PostBottomView(likesCount: Long, commentsCount: Long) {
Row(
modifier = Modifier.padding(horizontal = contentPadding),
verticalAlignment = Alignment.CenterVertically
) {
Row(
Modifier
.clip(RoundedCornerShape(50))
.clickable { /*Todo on like clicked*/ }
.padding(5.dp),
verticalAlignment = Alignment.CenterVertically
) {
Icon(
imageVector = ImageVector.vectorResource(id = R.drawable.ic_heart),
contentDescription = "Like"
)
Spacer(modifier = Modifier.width(5.dp))
Text(text = likesCount.toString(), fontSize = 9.sp)
}
Spacer(Modifier.width(20.dp))
Row(
Modifier
.clip(RoundedCornerShape(50))
.clickable { /*Todo on comment clicked*/ }
.padding(5.dp),
verticalAlignment = Alignment.CenterVertically
) {
Icon(
imageVector = ImageVector.vectorResource(id = R.drawable.ic_comment),
contentDescription = "Comment"
)
Spacer(modifier = Modifier.width(5.dp))
Text(text = commentsCount.toString(), fontSize = 9.sp)
}
}
}
#Composable
private fun PostTopView(
category: String,
onOptionsClicked: () -> Unit,
onBookmarkClicked: () -> Unit
) {
Row(
modifier = Modifier.fillMaxWidth(),
verticalAlignment = Alignment.CenterVertically,
horizontalArrangement = Arrangement.SpaceBetween
) {
Row(verticalAlignment = Alignment.CenterVertically) {
IconButton(onClick = onOptionsClicked) {
Icon(
imageVector = ImageVector.vectorResource(id = R.drawable.ic_threedots),
contentDescription = "Options",
tint = Color.Unspecified
)
}
Text(text = category, fontSize = 16.sp, color = LightTextColor)
}
IconButton(onClick = onBookmarkClicked) {
Icon(
imageVector = ImageVector.vectorResource(id = R.drawable.ic_bookmark),
contentDescription = "Bookmark"
)
}
}
}
and the lazyColumn:
LazyColumn(contentPadding = paddingValues , state = state ) {
item {
Spacer(modifier = Modifier.height(10.dp))
DoctorsList(
viewModel.doctorListData.value,
onCardClicked = {})
}
items(30) { post ->
HomePostView(
category = "Public Health ",
title = "Food Importance",
subTitle = "you should eat every day it's healthy and important for you, and drink water every 2 hours and what you should do is you should run every day for an hour"
)
}
}
Note: I'm still not using a viewmodel i'm just testing the view with fake data
This may not work for anyone else but on an earlier version (1.0.0-beta01) I saw a very large improvement in performance when I switched
lazy(items) { item ->
...
}
to
items.forEach { item ->
lazy {
...
}
}
I have no idea why and I'm not sure if this is still the case in later versions but its worth checking. So for the example given in the question, that would mean changing
items(30) {
...
}
to
repeat(30) {
item {
...
}
}
TLDR: Make sure your new LazyColumn compose element is not within a RelativeLayout or LinearLayout.
After some investigation the solution to this issue for us was the view in which the LazyColumn was constrained.
Our project uses a combination of Jetpack Compose and the older XML layout files. Our new compose elements were embedded within a RelativeLayout of an existing XML file. This was where the problem was. The compose element would be given the entire screen and then the onMeasure function of the compose element was called to re-configure the view and add our bottom nav bar...this onMeasure was called over and over again, which also in the case of a LazyColumn the re-measuring was throwing out the cache as the height had changed.
The solution for us was to change the RelativeLayout that contained both the new compose element and the bottom nav bar and replace it with a ConstraintLayout. This prevented the onMeasure from being called more than twice and gave a massive performance increase.
Try to build release build with turn off debug logs. should be works fine.
Ok, So far I know that there is an issue with the API when it comes to performance ...But what I found is this
Actually, In my case, I was just loading the image with 2980*3750 pixels image. I just crunched my resources to shorter pixels by some other tools
Now the lag is not present...
In my case, after I set the height of ComposeView to a specific value, it make LazyColumn scroll smooth.
Therefore, I create a XML file like
<FrameLayout xmlns:android="http://schemas.android.com/apk/res/android"
android:layout_width="match_parent"
android:layout_height="match_parent">
<androidx.compose.ui.platform.ComposeView
android:id="#+id/compose"
android:layout_width="match_parent"
android:layout_height="match_parent" />
</FrameLayout>
Fragment
override fun onViewCreated(view: View, savedInstanceState: Bundle ? ) {
super.onViewCreated(view, savedInstanceState)
view.doOnLayout {
compose.layoutParams.height = view.height
compose.requestLayout()
}
}
I know ComposeView height already match_parent so set the height for it again on Fragment seem useless. However, without setting the height, LazyColumn will lagging when scroll.
I am not sure if it will work in all device but work well on my Pixel and Xiomi.
If you are using JPGs or PNGs in your project then check for their size, images having larger size causes a lots of lagging issues on low end devices.
I was having the same issue with my simple LazyColumn list and turned out I was using a JPG having size larger than 2MBs.