Skip to content

Commit

Permalink
Fix change notifications
Browse files Browse the repository at this point in the history
  • Loading branch information
cp-megh-l committed Feb 19, 2024
1 parent 64e185d commit f9a3200
Show file tree
Hide file tree
Showing 3 changed files with 139 additions and 70 deletions.
Original file line number Diff line number Diff line change
Expand Up @@ -29,7 +29,7 @@ import com.example.compose_recyclerview.utils.ItemTouchHelperConfig
* Composable function to display a RecyclerView with dynamically generated Compose items.
*
* @param modifier The modifier to be applied to the RecyclerView.
* @param itemCount The total number of items to be displayed in the RecyclerView.
* @param items The list of items to be displayed in the RecyclerView.
* @param itemBuilder The lambda function responsible for creating the Compose content for each item at the specified index.
* @param onScrollEnd Callback triggered when the user reaches the end of the list during scrolling.
* @param orientation The layout direction of the RecyclerView.
Expand Down Expand Up @@ -76,6 +76,7 @@ fun <T> ComposeRecyclerView(
this.itemTypeBuilder = itemTypeBuilder
}
this.layoutOrientation = orientation
this.layoutManager = layoutManager
}
}

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -3,8 +3,10 @@ package com.example.compose_recyclerview.adapter
import android.view.ViewGroup
import androidx.compose.runtime.Composable
import androidx.compose.ui.platform.ComposeView
import androidx.recyclerview.widget.LinearLayoutManager
import androidx.recyclerview.widget.RecyclerView
import com.example.compose_recyclerview.data.LayoutOrientation
import kotlin.math.max

/**
* RecyclerView adapter for handling dynamically generated Compose items.
Expand Down Expand Up @@ -36,6 +38,8 @@ class ComposeRecyclerViewAdapter<T> :
notifyItemChanged(0)
}

var layoutManager: LinearLayoutManager? = null

inner class ComposeRecyclerViewHolder(val composeView: ComposeView) :
RecyclerView.ViewHolder(composeView)

Expand Down Expand Up @@ -83,10 +87,13 @@ class ComposeRecyclerViewAdapter<T> :
private fun notifyItemRangeChange(oldItems: List<T>) {
val oldSize = oldItems.size
val newSize = itemList.size
val firstVisibleIndex = layoutManager?.findFirstVisibleItemPosition() ?: 0
if (newSize < oldSize) {
notifyItemRangeRemoved(newSize, oldSize - newSize)
val position = max(0, firstVisibleIndex)
notifyItemRangeRemoved(position, oldSize - newSize)
} else if (newSize > oldSize) {
notifyItemRangeInserted(oldSize, newSize - oldSize)
val start = max(0, firstVisibleIndex)
notifyItemRangeInserted(start, newSize - oldSize)
}
}
}
195 changes: 128 additions & 67 deletions sample-app/src/main/java/com/example/composerecyclerview/MainActivity.kt
Original file line number Diff line number Diff line change
Expand Up @@ -12,15 +12,12 @@ import androidx.compose.foundation.layout.fillMaxWidth
import androidx.compose.foundation.layout.height
import androidx.compose.foundation.layout.padding
import androidx.compose.foundation.shape.RoundedCornerShape
import androidx.compose.material3.Button
import androidx.compose.material3.Card
import androidx.compose.material3.CardDefaults
import androidx.compose.material3.MaterialTheme
import androidx.compose.material3.Surface
import androidx.compose.material3.Text
import androidx.compose.runtime.Composable
import androidx.compose.runtime.remember
import androidx.compose.runtime.toMutableStateList
import androidx.compose.ui.Alignment
import androidx.compose.ui.Modifier
import androidx.compose.ui.graphics.Color
Expand All @@ -47,80 +44,144 @@ class MainActivity : ComponentActivity() {
modifier = Modifier.fillMaxSize(),
color = MaterialTheme.colorScheme.background
) {
val userDataList = remember {
List(20) { index ->
UserData(
"User ${index + 1}",
20 + index,
if (index % 2 == 0) "Male" else "Female"
)
}.toMutableStateList()
val userDataList = List(20) { index ->
UserData(
"User ${index + 1}",
20 + index,
if (index % 2 == 0) "Male" else "Female"
)
}

val otherUsersDataList = List(20) { index ->
UserData(
"User ${index + 21}",
20 + index,
if (index % 2 == 0) "Male" else "Female"
)
}

Column {
Button(
onClick = {
val indexToDelete = 0
Log.i("LOL", "index to delete: $indexToDelete")
userDataList.removeAt(
indexToDelete
)
ComposeRecyclerView(
modifier = Modifier.fillMaxSize(),
items = listOf(1) + userDataList + listOf(1) + otherUsersDataList,
itemBuilder = { item, index ->
if (index == 0) {
Box(
modifier = Modifier.fillMaxWidth(),
contentAlignment = Alignment.Center
) {
Text(
"First List Header Composable",
style = MaterialTheme.typography.titleMedium,
modifier = Modifier.padding(16.dp)
)
}
return@ComposeRecyclerView
}
) {
Text("delete 0")
}
ComposeRecyclerView(
modifier = Modifier.fillMaxSize(),
items = userDataList,
itemBuilder = { item, index ->
Log.d("XXX", "Size: ${userDataList.size}\tIndex:$index")
CustomUserItem(user = item)
},
onItemMove = { fromPosition, toPosition, itemType ->
// Update list when an item is moved
when (itemType) {
ITEM_TYPE_FIRST_LIST_ITEM -> {
val fromIndex = fromPosition - 1
val toIndex = toPosition - 1
Collections.swap(userDataList, fromIndex, toIndex)
}

val userIndex = index - 1
if (userIndex < userDataList.size) {
CustomUserItem(user = userDataList[userIndex])
return@ComposeRecyclerView
}

if (userIndex == userDataList.size) {
Box(
modifier = Modifier.fillMaxWidth(),
contentAlignment = Alignment.Center
) {
Text(
"Second List Header Composable",
style = MaterialTheme.typography.titleMedium,
modifier = Modifier.padding(16.dp)
)
}
},
onDragCompleted = {
// Update list or do API call when an item drag operation is completed
Log.d("MainActivity", "onDragCompleted: $it")
},
itemTypeBuilder = object : ComposeRecyclerViewAdapter.ItemTypeBuilder {
override fun getItemType(position: Int): Int {
// Determine the item type based on the position
// You can customize this logic based on your requirements
return ITEM_TYPE_FIRST_LIST_ITEM
return@ComposeRecyclerView
}

val otherUserIndex = index - userDataList.size - 2
if (otherUserIndex < otherUsersDataList.size) {
CustomUserItem(user = otherUsersDataList[otherUserIndex])
return@ComposeRecyclerView
}
},
onItemMove = { fromPosition, toPosition, itemType ->
// Update list when an item is moved
when (itemType) {
ITEM_TYPE_FIRST_HEADER -> {
// Do nothing
}
},
onScrollEnd = {
// Do API call when the user reaches the end of the list during scrolling
Log.d("MainActivity", "onScrollEnd")
},
itemTouchHelperConfig = {
nonDraggableItemTypes =
setOf(ITEM_TYPE_FIRST_HEADER, ITEM_TYPE_SECOND_HEADER)

/*onMove = { recyclerView, viewHolder, target ->
// Handle item move
ITEM_TYPE_FIRST_LIST_ITEM -> {
val fromIndex = fromPosition - 1
val toIndex = toPosition - 1
Collections.swap(userDataList, fromIndex, toIndex)
}
onSwiped = { viewHolder, direction ->
// Handle item swipe

ITEM_TYPE_SECOND_HEADER -> {
// Do nothing
}
// Add more customization options as needed*/
},
) { recyclerView ->
recyclerView.addItemDecoration(
DividerItemDecoration(
recyclerView.context,
DividerItemDecoration.VERTICAL
)

// ITEM_TYPE_SECOND_LIST_ITEM
else -> {
val fromIndex = fromPosition - userDataList.size - 2
val toIndex = toPosition - userDataList.size - 2
Collections.swap(otherUsersDataList, fromIndex, toIndex)
}
}
},
onDragCompleted = {
// Update list or do API call when an item drag operation is completed
Log.d("MainActivity", "onDragCompleted: $it")
},
itemTypeBuilder = object : ComposeRecyclerViewAdapter.ItemTypeBuilder {
override fun getItemType(position: Int): Int {
// Determine the item type based on the position
// You can customize this logic based on your requirements
return when {
position == 0 -> ITEM_TYPE_FIRST_HEADER // Header type
position <= userDataList.size -> ITEM_TYPE_FIRST_LIST_ITEM // First list item type
position == userDataList.size + 1 -> ITEM_TYPE_SECOND_HEADER // Header type
else -> ITEM_TYPE_SECOND_LIST_ITEM // Second list item type
}
}
},
onScrollEnd = {
// Do API call when the user reaches the end of the list during scrolling
Log.d("MainActivity", "onScrollEnd")
},
itemTouchHelperConfig = {
nonDraggableItemTypes =
setOf(ITEM_TYPE_FIRST_HEADER, ITEM_TYPE_SECOND_HEADER)

/*onMove = { recyclerView, viewHolder, target ->
// Handle item move
}
onSwiped = { viewHolder, direction ->
// Handle item swipe
}
// Add more customization options as needed*/
},
) { recyclerView ->
recyclerView.addItemDecoration(
DividerItemDecoration(
recyclerView.context,
DividerItemDecoration.VERTICAL
)
)

// To change layout to grid layout, uncomment the following lines
val gridLayoutManager = GridLayoutManager(this, 2).apply {
spanSizeLookup = object : GridLayoutManager.SpanSizeLookup() {
override fun getSpanSize(position: Int): Int {
return if (position == 0 || position == userDataList.size + 1) {
2 // To show header title at the center of the screen and span across the entire screen
} else {
1
}
}
}
}
recyclerView.layoutManager = gridLayoutManager
}
}
}
Expand Down

0 comments on commit f9a3200

Please sign in to comment.