In the following code, I have two parts A and B. I need to extract part B as a common part for more of my pages.
But it contains item, I cannot extract item, but item must be included in the if judgment because paging3 will scroll to the top for extra item.
Is there a way to extract item?
LazyColumn(Modifier.fillMaxSize()) {
// Part A
items(pagingItems) { wind ->
WindRow(navController, wind!!)
}
val refresh = pagingItems.loadState.refresh
val append = pagingItems.loadState.append
// Part B
if (refresh is LoadState.NotLoading && append is LoadState.NotLoading) {
if (pagingItems.itemCount == 0) {
item {
PosterCompose() {
navController.navigate("blowWind")
}
}
}
} else {
item {
LoadStateView(path = FOLLOW_WIND_LIST, refresh = refresh, append = append) {
pagingItems.retry()
}
}
}
}
I solved this problem
fun <T : Any> LazyListScope.newItems(pagingItems: LazyPagingItems<T>) {
val refresh = pagingItems.loadState.refresh
val append = pagingItems.loadState.append
if (refresh is LoadState.NotLoading && append is LoadState.NotLoading) {
if (pagingItems.itemCount == 0) {
item {
PosterCompose() {
}
}
}
}else{
item {
LoadStateView(path = FOLLOW_WIND_LIST, refresh = refresh, append = append) {
pagingItems.retry()
}
}
}
}
This is how to use
LazyColumn(Modifier.fillMaxSize()) {
items(pagingItems) { wind ->
WindRow(navController, wind!!)
}
newItems(pagingItems)
}
Related
When I modify the properties of the objects in the List, the UI does not update
my code:
#OptIn(ExperimentalFoundationApi::class)
#Composable
fun ContactCard(
) {
var stateList = remember {
mutableStateListOf<ListViewData>()
}
viewModel!!.recordRespListLiveData!!.observe(this) { it ->
it.forEach {
stateList.add(ListViewData(false, it))
}
}
LazyColumn() {
stateList.forEachIndexed { index, bean ->
stickyHeader() {
Box(Modifier.clickable {
stateList[index].visible = true
}) {
ContactNameCard(bean.data.contact, index)
}
}
items(bean.data.records) { data ->
if (bean.visible) {
RecordItemCard(record = data)
}
}
}
}
}
When I click on the Box, visible is set to true, but the RecordItemCard doesn't show,why?
For SnapshotList to trigger you need to add, delete or update existing item with new instance. Currently you are updating visible property of existing item.
If ListViewData is instance from data class you can do it as
stateList[index] = stateList[index].copy(visible = true)
How to make UI changes in a paginated list with jetpack compose.
Use case
I have a paginated list which has data name(string) and like(boolean). If i click on the particular item in the list, i need to place a like button in the UI. But the image is not updated in UI based on condition.
Snippet
userList -> LazyPagingItems<AllDoctorsResponse.Data.Doctor>
//viewModel
userList.itemSnapshotList.find { it?.id == user.id }?.liked = true
// Composable
items(userList.itemCount){ index ->
userList[index]?.let {
if (it.liked == true) {
UserCardWithLike(it, onClick = { userId ->
onUserCardClicked(userId)
}, onLikeChange = { isLiked, user ->
onLikeChange(isLiked, user)
})
} else {
UserCard(it, onClick = { userId ->
onUserCardClicked(userId)
}, onLikeChange = { isLiked, user ->
onLikeChange(isLiked, user)
})
}
}
}
I don't use paging3 anymore, so I didn't test this code, but I think it will work:
items(userList.itemCount){ index ->
userList[index]?.let {
var liked by rememberSaveable { mutableStateOf(it.liked) }
if (liked == true) {
UserCardWithLike(
it,
onClick = { userId -> onUserCardClicked(userId) },
onLikeChange = { isLiked, user -> liked = false }
)
} else {
UserCard(
it,
onClick = { userId -> onUserCardClicked(userId) },
onLikeChange = { isLiked, user -> liked = true }
)
}
}
}
If you want something like notifyItemChange, it's not possible in Paging3. In that case, I suggest trying to rewrite the paging library, which is surprisingly easy.
https://gist.github.com/FishHawk/6e4706646401bea20242bdfad5d86a9e
Here I wrote a function where when a particular textView is clicked it will sort the recycler view according to that property but the function only does sorting in descending order. I want functionality where when click the text the first time it sort in ascending order and in the second time it will sort in descending or vice versa. Can please anyone help?
private fun sortTheData(){
binding.statusTextTV.setOnClickListener {
statusAsc = if(statusAsc){
vehicleList.sortedBy {
it.status
}
false
}else{
vehicleList.sortByDescending {
it.status
}
true
}
}
binding.permitTextTV.setOnClickListener {
Log.e(TAG, "sortTheData: $permitAsc", )
permitAsc = if(permitAsc){
vehicleList.sortedBy { vehicle ->
vehicle.permit
}.forEach { println(it.permit) }
false
}else{
vehicleList.sortByDescending { vehicle ->
vehicle.permit
}
true
}
adapter.updateList(vehicleList)
}
binding.licenseTextTV.setOnClickListener {
licenseAsc = if(licenseAsc){
vehicleList.sortedBy {
it.license
}
adapter.updateList(vehicleList)
false
}else{
vehicleList.sortByDescending {
it.license
}
adapter.updateList(vehicleList)
true
}
}
binding.spaceTextTV.setOnClickListener {
spaceAsc = if(spaceAsc){
vehicleList.sortedBy {
it.space
}
vehicleList.forEach{
println(it.space)
}
false
}else{
vehicleList.sortByDescending {
it.space
}
vehicleList.forEach{
println(it.space)}
true
}
}
}
You need to keep a boolean flag and sort accordingly. Pseudo code:
var reversed = false
textView.setOnClickListener {
reversed = !reversed
list = vehicleList.sortedBy { it.license }
if (reversed) {
list = list.reversed()
}
}
You can use type varaible or enum
e.g
var sortType : Int = 1 // 1 or asc & 2 for desc. You can use enum as well
Your sort function will be
fun sort() {
if(sortType == 1) {
sortType = 2
vehicleList.sortByDescending {it.license}
} else {
sortType = 1
vehicleList.sortBy {it.license}
}
adapter.updateList(vehicleList)
}
I have implemented LazyColumn with Paging, but I'm now trying to add sticky headers as well.
The stickyHeader() function is not available inside the items() scope, so I don't see how this should work.
#Composable
fun MovieList(movies: Flow<PagingData<Movie>>) {
val lazyMovieItems: LazyPagingItems<Movie> = movies.collectAsLazyPagingItems()
LazyColumn {
// TODO: Add sticky headers
items(lazyMovieItems) { movie ->
MovieItem(movie = movie!!)
}
}
}
How can I add the stickyHeaders?
#Composable
fun MovieList(movies: Flow<PagingData<Movie>>) {
val lazyMovieItems = movies.collectAsLazyPagingItems()
LazyColumn {
val itemCount = lazyMovieItems.itemCount
var lastCharacter: Char? = null
for (index in 0 until itemCount) {
// Gets item without notifying Paging of the item access,
// which would otherwise trigger page loads
val movie = lazyMovieItems.peek(index)
val character = movie?.name?.first()
if (movie !== null && character != lastCharacter) {
stickyHeader(key = character) {
MovieHeader(character)
}
}
item(key = movie?.id) {
// Gets item, triggering page loads if needed
val movieItem = lazyMovieItems[index]
Movie(movieItem)
}
lastCharacter = character
}
}
}
I'm currently playing around with the new Jetpack compose UI toolkit and I like it a lot. One thing I could not figure out is how to use stickyHeaders in a LazyColumn which is populated by the paging library. The non-paging example from the documentation is:
val grouped = contacts.groupBy { it.firstName[0] }
fun ContactsList(grouped: Map<Char, List<Contact>>) {
LazyColumn {
grouped.forEach { (initial, contactsForInitial) ->
stickyHeader {
CharacterHeader(initial)
}
items(contactsForInitial) { contact ->
ContactListItem(contact)
}
}
}
}
Since I'm using the paging library I cannot use the groupedBy so I tried to use the insertSeparators function on PagingData and insert/create the headers myself like this (please ignore the legacy Date code, it's just for testing):
// On my flow
.insertSeparators { before, after ->
when {
before == null -> ListItem.HeaderItem(after?.workout?.time ?: 0)
after == null -> ListItem.HeaderItem(before.workout.time)
(Date(before.workout.time).day != Date(after.workout.time).day) ->
ListItem.HeaderItem(before.workout.time)
// Return null to avoid adding a separator between two items.
else -> null
}
}
// In my composeable
LazyColumn {
items(workoutItems) {
when(it) {
is ListItem.HeaderItem -> this#LazyColumn.stickyHeader { Header(it) }
is ListItem.SongItem -> WorkoutItem(it)
}
}
}
But this produces a list of all my items and the header items are appended at the end. Any ideas what is the right way to use the stickyHeader function when using the paging library?
I got it to work by looking into the source code of the items function: You must not call stickyHeader within the items function. No need to modify the PagingData flow at all. Just use peek to get the next item without triggering a reload and then layout it:
LazyColumn {
val itemCount = workoutItems.itemCount
var lastWorkout: Workout? = null
for(index in 0 until itemCount) {
val workout = workoutItems.peek(index)
if(lastWorkout?.time != workout?.time) stickyHeader { Header(workout) }
item { WorkoutItem(workoutItems.getAsState(index).value) } // triggers reload
lastWorkout = workout
}
}
I believe the issue in your code was that you were calling this#LazyColumn from inside an LazyItemScope.
I experimented too with insertSeparators and reached this working LazyColumn code:
LazyColumn {
for (index in 0 until photos.itemCount) {
when (val peekData = photos.peek(index)) {
is String? -> stickyHeader {
Text(
text = (photos.getAsState(index).value as? String).orEmpty(),
)
}
is Photo? -> item(key = { peekData?.id }) {
val photo = photos.getAsState(index).value as? Photo
...
}
}
}
}