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
Related
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
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.
I like to keep the current page indicator a specific shape like in the picture when I'm on the active page, but want to keep the previous tab indicator like a dot shape when I swipe from one screen to another. But, from what I can tell, it doesn't seem like I can change the indicatorShape below based on the currently active page state, and keep the previous shape different. Any ideas would be greatly appreciated. Thank You!
#OptIn(ExperimentalPagerApi::class)
#Composable
fun HorizontalPagerScreen() {
Column(
modifier = Modifier
.fillMaxSize()
.padding(30.dp)
) {
val items = createItems()
val pagerState = rememberPagerState()
val coroutineScope = rememberCoroutineScope()
HorizontalPager(
count = items.size,
state = pagerState,
modifier = Modifier.weight(1f)
) { currentPage ->
Column(
modifier = Modifier.fillMaxSize()
) {
Text(
text = items[currentPage].title,
style = MaterialTheme.typography.h2
)
Spacer(modifier = Modifier.height(10.dp))
Text(
text = items[currentPage].subtitle,
style = MaterialTheme.typography.h4
)
Spacer(modifier = Modifier.height(10.dp))
Text(
text = items[currentPage].description,
style = MaterialTheme.typography.body1
)
}
}
HorizontalPagerIndicator(
pagerState = pagerState,
modifier = Modifier
.align(Alignment.CenterHorizontally)
.padding(16.dp),
indicatorHeight = 8.dp,
activeColor = Color.Blue,
indicatorWidth = ChangeSizeOfShape(currentPage = pagerState.currentPage),
pageCount = 2,
indicatorShape =RoundedCornerShape(corner = CornerSize(40.dp))
)
Button(
onClick = {
coroutineScope.launch {
pagerState.animateScrollToPage(page = 2)
}
},
modifier = Modifier.align(Alignment.CenterHorizontally)
) {
Text(text = "Scroll to the third page")
}
}
}
As of Oct 14, 2022, there isn't anything built-in that can allow us to change view pager indicator size/shape the way we want. But, there are few other ways of doing it for now that are available online.
Animated worm page Indicator. This is in compose.
I'am new to jetpack compose and i really liked it. But ran into a problem : I to have a modal that take all my screen size, following multiples tutorial i was able to do it using ModalBottomSheetLayout, Here is my code :
#ExperimentalMaterialApi
#Composable
fun DetailsScreen() {
val coroutineScope = rememberCoroutineScope()
val sheetState = rememberModalBottomSheetState(initialValue = ModalBottomSheetValue.Hidden)
ModalBottomSheetLayout(
sheetContent = {
Box(
modifier = Modifier
.navigationBarsWithImePadding()
.fillMaxSize()
.wrapContentHeight() //changed here to have full screen size, but it was only half screen, i had to slide with my finger to make the modal to full screen
//.height(200.dp) initial value i have a little modal on the bottom
//.fillMaxWidth()
.background(Color.White)
) {
Column(
modifier = Modifier
.fillMaxSize()
.padding(10.dp)
) {
Text(
text = "Bottom Sheet",
textAlign = TextAlign.Center,
style = MaterialTheme.typography.h5,
fontWeight = FontWeight.Bold,
modifier = Modifier.fillMaxWidth()
)
Text(
text = "Item 1",
style = MaterialTheme.typography.h6,
modifier = Modifier.padding(top = 10.dp)
)
Text(
text = "Item 2",
style = MaterialTheme.typography.h6,
modifier = Modifier.padding(top = 10.dp)
)
Text(
text = "Item 3",
style = MaterialTheme.typography.h6,
modifier = Modifier.padding(top = 10.dp)
)
}
}
},
sheetState = sheetState,
sheetBackgroundColor = Color.White
) {
Scaffold(
topBar = {
TopAppBar(
title = {
Text(
text = "Modal Bottom Sheet",
modifier = Modifier.fillMaxWidth(),
textAlign = TextAlign.Center
)
}
)
}
) {
MainContent(
onClick = {
coroutineScope.launch {
//sheetState.show() // changed here make the modal full screen at the beginning
sheetState.animateTo(ModalBottomSheetValue.Expanded)
}
},
modifier = Modifier.padding(it)
)
}
}
}
#Composable
fun MainContent(
onClick: () -> Unit,
modifier: Modifier = Modifier
) {
Box(
contentAlignment = Alignment.Center,
modifier = modifier.fillMaxSize()
) {
Button(
onClick = onClick,
shape = RoundedCornerShape(10.dp)
) {
Text(
text = "Click Me!",
textAlign = TextAlign.Center,
style = MaterialTheme.typography.h6.copy(color = Color.White)
)
}
}
}
Problem i encounter : at first the modal was just at the bottom so i made my box to fillMaxSize to make it full screen, but it only open to half screen and i slide to swipe up to make it full screen.
Then i found that i could change sheetState.show() to sheetState.animateTo(ModalBottomSheetValue.Expanded) to make my modal full screen from the moment it display. Perfect it's what i wanted. here is a screen of the modal :
The problem is that now when i slide down to dismiss the modal it's stop half scree as bellow:
From there i have to swipe down an other time to make it disappear. Also when it's on the middle i can swipe up to make it back to full screen. It's seem that there is 3 states: fullscreen, halfscreen, and gone. And the modal can be full screen or halfscreen. I would like to remove the half screen. So when i open the modal it's fullscreen and when i swipe down to remove it, it disappear without stoping in the middle. So i would have only one gesture to remove it.
I think you can get this behavior with skipHalfExpanded property:
val bottomSheetState = rememberModalBottomSheetState(initialValue = ModalBottomSheetValue.Expanded, skipHalfExpanded = true)
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.