LazyColumn вызывает задержку из-за вычислений для каждого элемента. Как его оптимизировать?Android

Форум для тех, кто программирует под Android
Ответить
Anonymous
 LazyColumn вызывает задержку из-за вычислений для каждого элемента. Как его оптимизировать?

Сообщение Anonymous »

У меня наблюдается значительная задержка при отображении LazyColumn в приложении Jetpack Compose. Каждый элемент включает в себя некоторые вычисления (например, вычисление оставшегося времени), и я загружаю изображения асинхронно с помощью Coil. Это вызывает проблемы с производительностью, особенно во время прокрутки.
Вот соответствующий код:
У меня есть класс данных FeedViewState и FeedViewModel, который извлекает разбивку на страницы. список объектов опроса с сервера. LazyColumn отображает этот список, и когда пользователь прокручивает его до конца, я получаю больше данных (нумерация страниц).

Код: Выделить всё

@Composable
fun FeedComp(
mainViewModel: MainViewModel,
mainHomeVM: MainHomeViewModel,
showBottomSheet: MutableState,
navControllerMain: NavController,
) {
// Initial data fetch
LaunchedEffect(Unit) {
feedVM.GetFeedFunctionality(mainViewModel)
}

val listState = rememberLazyListState()

Scaffold(
modifier = Modifier.blur(if (showBottomSheet.value) 15.dp else 0.dp),
topBar = {
MainHomeTopBarComp(mainViewModel = mainViewModel, showBottomSheet)
},
) { paddingValues ->
Box(Modifier.fillMaxSize()) {
LazyColumn(
state = listState,
modifier = Modifier
.fillMaxSize()
.padding(paddingValues)
.background(Color.White),
verticalArrangement = Arrangement.spacedBy(8.dp)
) {
item { Spacer(modifier = Modifier.height(8.dp)) }
items(state.FeedList) { poll ->
PollItem(
poll = poll,
mainViewModel = mainViewModel,
navControllerMain = navControllerMain,
pollReportId = pollReportId,
showManagementDialog = showManagementDialog,
managementOwner = managementOwner,
feedVM = feedVM
)  // Each PollItem involves calculations and Coil image loading
}
}
}
}

// Pagination - fetch more data when user scrolls close to the end
LaunchedEffect(listState.firstVisibleItemIndex) {
val visibleItemCount = listState.layoutInfo.visibleItemsInfo.size
val totalItemCount = listState.layoutInfo.totalItemsCount
val lastVisibleItemIndex = listState.layoutInfo.visibleItemsInfo.lastOrNull()?.index ?: 0

if (lastVisibleItemIndex >= totalItemCount - 3 && !state.loading) {
feedVM.GetFeedFunctionality(mainViewModel)
}
}
}

ViewModel и данные:

Код: Выделить всё

data class FeedViewState(
val loading: Boolean = false,
var FeedList: List
  = emptyList(),
)

@HiltViewModel
class FeedViewModel @Inject constructor(
private val feedRepository: FeedRepository,
) : ViewModel() {
private val _state = MutableStateFlow(FeedViewState())
val state = _state.asStateFlow()

private var currentPage by mutableStateOf(1)

fun GetFeedFunctionality(mainViewModel: MainViewModel) {
if (state.value.loading) return

val pageSize = 10
val token = "Bearer ${mainViewModel.GetToken()}"
viewModelScope.launch {
_state.update { it.copy(loading = true) }
feedRepository.getFeed(token = token, pageSize = pageSize, pageNumber = currentPage)
.onRight { response ->
if (response.isSuccessful) {
response.body()?.data?.polls?.let { polls ->
_state.update {
it.copy(
FeedList = it.FeedList + polls
)
}
currentPage++
}
}
}
.onLeft { error ->
Log.e("FeedViewModel", "Network error: ${error.error.message}")
}
_state.update { it.copy(loading = false) }
}
}
}

Компонуемый PollItem:
Каждый PollItem включает в себя некоторые вычисления и загрузку изображений с использованием Coil.
каждый опрос (объект) представляет собой класс данных, который я получил от API

Код: Выделить всё

data class Poll(
val commentCloesed: Boolean,
val createdAt: String,
val endedAt: String,
val id: String,
var isPollUp: Boolean,
var likes: Int,
val maximumParticipate: Int,
var numberofcommnets: Int,
var numberofshare: Int,
val numberofviews: Int,
val options: List,
val owner: Owner?,
val isOwner:Boolean,
var participate: Int,
val pollUrl: String,
val polltype: Int,
val question: String,
val isAlphaUser:Boolean,
val isAlpha:Boolean,
val isSensitive:Boolean,
var status: Int,
val tags: List,
val topics: List,
var user: List,
var userCommentedPoll: Boolean,
var userLike: Boolean,
var userReShare: Boolean,
val userViewedPoll: Boolean,
var userVotedOnPoll: Boolean,
val votingtype: Int,
var deleted :Boolean = false
)

Код: Выделить всё

@Composable
fun PollItem(
poll: Poll,
mainViewModel: MainViewModel,
navControllerMain: NavController,
pollReportId: MutableState,
showManagementDialog: MutableState,
managementOwner: MutableState,
feedVM:  FeedViewModel
) {
val screenHeight = LocalConfiguration.current.screenHeightDp.dp
val screenWidth = LocalConfiguration.current.screenWidthDp.dp
if (!poll.deleted) {
Column(
Modifier
.fillMaxWidth()
.padding(end = 16.dp, start = 16.dp, bottom = 8.dp)
.clip(RoundedCornerShape(4.dp))
.background(backgroundpollcard)
.pointerInput(Unit) {
detectTapGestures(
onTap = {
mainViewModel.rememberedPoll.value = poll
navControllerMain.navigate("pollPage/${poll.id}")
},
onLongPress = {
pollReportId.value = poll.id
managementOwner.value = poll.isOwner
showManagementDialog.value = true

}
)
}
.padding(16.dp)
) {
//polled up by who
if (poll.isPollUp) {
Row(
Modifier.fillMaxWidth(),
verticalAlignment = Alignment.CenterVertically,
horizontalArrangement = Arrangement.SpaceBetween
) {
Box(
modifier = Modifier
.fillMaxWidth()
.clip(RoundedCornerShape(4.dp))
.border(1.dp, textSecondary, RoundedCornerShape(4.dp))
.background(backgroundTabs)
.padding(8.dp),
contentAlignment = Alignment.Center
) {
Row(
Modifier.fillMaxWidth(),
verticalAlignment = Alignment.CenterVertically
) {
Icon(
painter = painterResource(id = R.drawable.unpollup),
contentDescription = "pollup icon",
tint = textSecondary,
modifier = Modifier
.size(20.dp)
)
Spacer(modifier = Modifier.width(8.dp))
Text(
fontFamily = sfPro,
text = "PollUpped by ",
fontSize = 12.sp,
//letterSpacing = (-0.41).sp,
lineHeight = 14.sp,
fontWeight = FontWeight(400),
color = textSecondary
)
val currentUserId =
mainViewModel.singedInProfileResponse.value?.data?.id
val userName =
poll.user.find { it.userId == currentUserId }
?.let { "You" }
?: poll.user.firstOrNull()?.userName
?: poll.user[0].userName
Text(
modifier = Modifier
.sizeIn(maxWidth = 100.dp)
// .background(Color.LightGray)
,
overflow = TextOverflow.Ellipsis,
fontFamily = sfPro,
text = "$userName",
maxLines = 1,
fontSize = 12.sp,
//letterSpacing = (-0.41).sp,
lineHeight = 14.sp,
fontWeight = FontWeight(600),
color = textSecondary
)
if (poll.user.size > 1) {
Text(
fontFamily = sfPro,
text = "  and ",
fontSize = 12.sp,
//letterSpacing = (-0.41).sp,
lineHeight = 14.sp,
fontWeight = FontWeight(400),
color = textSecondary
)
Text(
overflow = TextOverflow.Ellipsis,
fontFamily = sfPro,
text = "${poll.user.size - 1}",
maxLines = 1,
fontSize = 12.sp,
//letterSpacing = (-0.41).sp,
lineHeight = 14.sp,
fontWeight = FontWeight(600),
color = textSecondary
)
Text(
fontFamily = sfPro,
text = "${if (poll.user.size - 1 > 1) "others" else "other"}",
fontSize = 12.sp,
//letterSpacing = (-0.41).sp,
lineHeight = 14.sp,
fontWeight = FontWeight(400),
color = textSecondary
)

}
}

}
}
Spacer(modifier = Modifier.height(24.dp))
}

//profile & progress
Row(
Modifier.fillMaxWidth(),
verticalAlignment = Alignment.CenterVertically,
horizontalArrangement = Arrangement.SpaceBetween
) {
Row(verticalAlignment = Alignment.CenterVertically) {
Box(
modifier = Modifier
.size(40.dp)
.clip(RoundedCornerShape(100))
.background(Color.LightGray)
) {
if (poll.owner != null) {
if (poll.owner.pictureUrl != null &&  poll.owner.pictureUrl != "") {
AsyncImage(
modifier = Modifier.fillMaxSize(),
model = poll.owner?.pictureUrl,
contentDescription = "poll owner profile image",
contentScale = ContentScale.Crop
)
} else {
Image(
painter = painterResource(id = R.drawable.defaultprof),
modifier = Modifier.fillMaxSize(),
contentDescription = "poll owner profile image",
contentScale = ContentScale.Crop
)
}

} else {
Image(
painter = painterResource(id = R.drawable.anonymous_emblem),
modifier = Modifier.fillMaxSize(),
contentDescription = "poll owner profile image",
contentScale = ContentScale.Crop
)
}

}
Spacer(modifier = Modifier.width(8.dp))
if (poll.owner != null) {
Column(Modifier.offset(y = -2.dp)) {
Row(verticalAlignment = Alignment.CenterVertically) {
poll.owner?.firstName?.let {
Text(
fontFamily = sfPro,
text = it,
fontSize = 16.sp,
//letterSpacing = (-0.41).sp,
lineHeight = 19.sp,
fontWeight = FontWeight(500),
color = textSecondary
)
}
if (poll.isAlphaUser) {
Spacer(modifier = Modifier.width(8.dp))
Image(
painter = painterResource(id = R.drawable.alpha),
contentDescription = "alpha sign",
modifier = Modifier
.size(17.dp)
)

}
}

Spacer(modifier = Modifier.height(8.dp))
Text(
fontFamily = sfPro,
text = "@"  + poll.owner?.userName,
fontSize = 10.sp,
// letterSpacing = (-0.41).sp,
lineHeight = 11.sp,
fontWeight = FontWeight(500),
color = textThirt
)
}
} else {
Text(
fontFamily = sfPro,
text = "Anonymous Poll",
fontSize = 16.sp,
//letterSpacing = (-0.41).sp,
lineHeight = 19.sp,
fontWeight = FontWeight(500),
color = textSecondary
)
//todo:inja YOU biyad ,  fix condition
if (poll.isOwner == true) {
Spacer(modifier = Modifier.width(8.dp))
Text(
modifier = Modifier
.border(
0.5.dp,
color = textSecondary,
RoundedCornerShape(4.dp)
)
.padding(
start = 6.dp,
end = 6.dp
),
fontFamily = sfPro,
text = "You",
fontSize = 8.sp,
//letterSpacing = (-0.41).sp,
lineHeight = 19.sp,
fontWeight = FontWeight(500),
color = textSecondary
)
}

}

}

Box(contentAlignment = Alignment.Center) {
if (poll.status == 1) {
CircularProgressIndicator(
modifier = Modifier.size(40.dp),
strokeWidth = 2.dp,
color = feedVM.timeRemaining(poll.endedAt).second,
trackColor = strokesVoteTime,
strokeCap = StrokeCap.Round,
progress = {
feedVM.calculatePercentage(
startDate = poll.createdAt,
endDate = poll.endedAt
)
}
)

Box {
Text(
fontFamily = sfPro,
text = "${feedVM.timeRemaining(poll.endedAt).first}",
fontSize = 10.sp,
//letterSpacing = (-0.41).sp,
lineHeight = 12.sp,
fontWeight = FontWeight(500),
color = if (poll.status == 0) textthird else feedVM.timeRemaining(
poll.endedAt
).second
)
}
} else {
CircularProgressIndicator(
modifier = Modifier.size(40.dp),
strokeWidth = 2.dp,
color = textthird,
trackColor = strokesVoteTime,
strokeCap = StrokeCap.Round,
progress = {
1f
}
)

Box {
Text(
fontFamily = sfPro,
text = "Ended",
fontSize = 10.sp,
//letterSpacing = (-0.41).sp,
lineHeight = 12.sp,
fontWeight = FontWeight(500),
color = textthird
)
}
}

}
}
Spacer(modifier = Modifier.height(16.dp))
//question
Box(modifier = Modifier.fillMaxWidth()) {
HashtagText(
text = poll.question,
onHashtagClick = { hashtag ->
navControllerMain.navigate("pollPage/${poll.id}")
println("Hashtag clicked:  $hashtag")
},
onTextClick = {
navControllerMain.navigate("pollPage/${poll.id}")
println("Non-hashtag text clicked!")
}
)
}

//first option
if (poll.options.isNotEmpty()) {
Spacer(modifier = Modifier.height(16.dp))
if (poll.options[0].type == 0) {//means its text poll
Row(
Modifier.fillMaxWidth(),
verticalAlignment = Alignment.CenterVertically
) {

CustomRadioButton(
isSelected = mutableStateOf(poll.options[0].userChoose),
onClick = {},
borderColor = textthird,
size = 16.dp,
dotColor = textthird,
isEnabled = false
)
Spacer(modifier = Modifier.width(8.dp))
Column {
Text(
fontFamily = sfPro,
text = poll.options[0].title,
fontSize = 14.sp,
//letterSpacing = (-0.41).sp,
lineHeight = 17.sp,
fontWeight = FontWeight(500),
color = textSecondary
)
Spacer(modifier = Modifier.height(4.dp))
Box(
modifier = Modifier
.fillMaxWidth()
.height(2.dp)
.background(if (poll.options[0].userChoose) textthird else strokesVoteTime)
)
}
}
} else {//means its image poll
// Text(text = "${(screenHeight*9/ 40)}")
Box(
modifier = Modifier
.fillMaxWidth()
.height(screenHeight * 9 / 40)
.clip(RoundedCornerShape(4.dp))
.background(Color.LightGray)
) {

AsyncImage(
model = poll.options[0].data,
contentDescription = null,
modifier = Modifier.fillMaxSize(),
contentScale = ContentScale.Crop
)

}
}
}

//more option(s)
Spacer(modifier = Modifier.height(8.dp))
Row(Modifier.fillMaxWidth()) {
Text(
fontFamily = sfPro,
text = "${poll.options.size - 1} more ${if (poll.options.size - 1 == 1) "option"  else "options"}",
fontSize = 12.sp,
//letterSpacing = (-0.41).sp,
lineHeight = 15.sp,
fontWeight = FontWeight(400),
color = textthird
)
}

//like comment
Spacer(modifier = Modifier.height(16.dp))
Row(
Modifier.fillMaxWidth(),
verticalAlignment = Alignment.CenterVertically,
horizontalArrangement = Arrangement.SpaceBetween
) {

//alpha
if (poll.isAlpha) {
Text(
fontFamily = sfPro,
text = "Alpha",
fontSize = 12.sp,
//letterSpacing = (-0.41).sp,
lineHeight = 15.sp,
fontWeight = FontWeight(600),
color = textImportant
)
}
//comment
if (!poll.commentCloesed) {
Row(verticalAlignment = Alignment.CenterVertically) {
Icon(
modifier = Modifier.size(17.dp),
painter = if (poll.userCommentedPoll)
painterResource(id = R.drawable.comment)
else
painterResource(id = R.drawable.uncomment),
tint = if (poll.status == 1) if (poll.userCommentedPoll) iconsFunctionalityComment else iconsFunctionalityDefault else textthird,
contentDescription = "icon"
)
Spacer(modifier = Modifier.width(4.dp))
Text(
fontFamily = sfPro,
text = "${poll.numberofcommnets}",
fontSize = 10.sp,
//letterSpacing = (-0.41).sp,
lineHeight = 12.sp,
fontWeight = FontWeight(400),
color = iconsFunctionalityDefault
)
}

}

//like
Row(verticalAlignment = Alignment.CenterVertically) {
Icon(
modifier = Modifier.size(17.dp),
painter = if (poll.userLike)
painterResource(id = R.drawable.like)
else
painterResource(id = R.drawable.unlike),
tint = if (poll.status == 1) if (poll.userLike) iconsFunctionalityLike else iconsFunctionalityDefault else textthird,
contentDescription = "icon"
)
Spacer(modifier = Modifier.width(4.dp))
Text(
fontFamily = sfPro,
text = "${poll.likes}",
fontSize = 10.sp,
//letterSpacing = (-0.41).sp,
lineHeight = 12.sp,
fontWeight = FontWeight(400),
color = iconsFunctionalityDefault
)
}

//pollup
Row(verticalAlignment = Alignment.CenterVertically) {
Icon(
modifier = Modifier.size(17.dp),
painter = if (poll.userReShare)
painterResource(id = R.drawable.pollup)
else
painterResource(id = R.drawable.unpollup),
tint = if (poll.status == 1) if (poll.userReShare)  iconsFunctionalityPollup else iconsFunctionalityDefault else textthird,
contentDescription = "icon"
)
Spacer(modifier = Modifier.width(4.dp))
Text(
fontFamily = sfPro,
text = "${poll.numberofshare}",
fontSize = 10.sp,
//letterSpacing = (-0.41).sp,
lineHeight = 12.sp,
fontWeight = FontWeight(400),
color = iconsFunctionalityDefault
)
}

//voters
Row(verticalAlignment = Alignment.CenterVertically) {
Icon(
modifier = Modifier.size(17.dp),
painter = if (poll.userVotedOnPoll)
painterResource(id = R.drawable.vote)
else
painterResource(id = R.drawable.unvote),
tint = if (poll.status == 1) if (poll.userVotedOnPoll) iconsFunctionalityVoter else iconsFunctionalityDefault else textthird,
contentDescription = "icon"
)
Spacer(modifier = Modifier.width(4.dp))
Text(
fontFamily = sfPro,
text = "${poll.participate}",
fontSize = 10.sp,
//letterSpacing = (-0.41).sp,
lineHeight = 12.sp,
fontWeight = FontWeight(400),
color = iconsFunctionalityDefault
)
}

}

}
}
}
Моя проблема:
LazyColumn вызывает задержку, особенно во время прокрутки.
Каждый PollItem требует вычислений, а изображения загружаются асинхронно с помощью Coil.>

Подробнее здесь: https://stackoverflow.com/questions/790 ... ptimize-it
Ответить

Быстрый ответ

Изменение регистра текста: 
Смайлики
:) :( :oops: :roll: :wink: :muza: :clever: :sorry: :angel: :read: *x)
Ещё смайлики…
   
К этому ответу прикреплено по крайней мере одно вложение.

Если вы не хотите добавлять вложения, оставьте поля пустыми.

Максимально разрешённый размер вложения: 15 МБ.

Вернуться в «Android»