From 148c6af8800fc448cdb56edba08d253c53cfff9c Mon Sep 17 00:00:00 2001 From: cp-megh Date: Thu, 28 Dec 2023 16:13:11 +0530 Subject: [PATCH 1/2] Add item type builder and enable drag to reorder --- .../ComposeRecyclerView.kt | 85 +++++++++-- .../adapter/ComposeRecyclerViewAdapter.kt | 57 ++++++-- .../composerecyclerview/MainActivity.kt | 136 +++++++++++++----- 3 files changed, 218 insertions(+), 60 deletions(-) diff --git a/compose-recyclerview/src/main/java/com/example/compose_recyclerview/ComposeRecyclerView.kt b/compose-recyclerview/src/main/java/com/example/compose_recyclerview/ComposeRecyclerView.kt index c38123f..2ab7813 100644 --- a/compose-recyclerview/src/main/java/com/example/compose_recyclerview/ComposeRecyclerView.kt +++ b/compose-recyclerview/src/main/java/com/example/compose_recyclerview/ComposeRecyclerView.kt @@ -1,6 +1,5 @@ package com.example.compose_recyclerview -import android.os.Parcelable import androidx.compose.runtime.Composable import androidx.compose.runtime.DisposableEffect import androidx.compose.runtime.getValue @@ -12,12 +11,16 @@ import androidx.compose.ui.Modifier import androidx.compose.ui.platform.LocalContext import androidx.compose.ui.viewinterop.AndroidView import androidx.core.os.bundleOf +import androidx.recyclerview.widget.ItemTouchHelper +import androidx.recyclerview.widget.ItemTouchHelper.DOWN +import androidx.recyclerview.widget.ItemTouchHelper.END +import androidx.recyclerview.widget.ItemTouchHelper.START +import androidx.recyclerview.widget.ItemTouchHelper.UP import androidx.recyclerview.widget.LinearLayoutManager import androidx.recyclerview.widget.RecyclerView import com.example.compose_recyclerview.adapter.ComposeRecyclerViewAdapter import com.example.compose_recyclerview.data.LayoutOrientation - /** * Composable function to display a RecyclerView with dynamically generated Compose items. * @@ -26,6 +29,11 @@ import com.example.compose_recyclerview.data.LayoutOrientation * @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. + * @param itemTypeBuilder The optional lambda function to determine the type of each item. + * * Required for effective drag and drop. Provide a non-null [ComposeRecyclerViewAdapter.ItemTypeBuilder] when enabling drag and drop functionality. + * @param onDragCompleted Callback triggered when an item drag operation is completed. + * @param onItemMove Callback triggered when an item is moved within the RecyclerView. + * @param onCreate Callback to customize the RecyclerView after its creation. */ @Composable fun ComposeRecyclerView( @@ -34,14 +42,17 @@ fun ComposeRecyclerView( itemBuilder: @Composable (index: Int) -> Unit, onScrollEnd: () -> Unit = {}, orientation: LayoutOrientation = LayoutOrientation.Vertical, - onCreate: (RecyclerView) -> Unit? = {} + itemTypeBuilder: ComposeRecyclerViewAdapter.ItemTypeBuilder? = null, + onDragCompleted: (position: Int) -> Unit = { _ -> }, + onItemMove: (fromPosition: Int, toPosition: Int, itemType: Int) -> Unit = { _, _, _ -> }, + onCreate: (RecyclerView) -> Unit = {} ) { val context = LocalContext.current var scrollState by rememberSaveable { mutableStateOf(bundleOf()) } + val layoutManager = remember { val layoutManager = LinearLayoutManager(context) - val parcelableState = scrollState.getParcelable("RecyclerviewState") - layoutManager.onRestoreInstanceState(parcelableState) + layoutManager.onRestoreInstanceState(scrollState.getParcelable("RecyclerviewState")) layoutManager.orientation = when (orientation) { LayoutOrientation.Horizontal -> RecyclerView.HORIZONTAL LayoutOrientation.Vertical -> RecyclerView.VERTICAL @@ -53,13 +64,15 @@ fun ComposeRecyclerView( ComposeRecyclerViewAdapter().apply { this.totalItems = itemCount this.itemBuilder = itemBuilder + itemTypeBuilder?.let { + this.itemTypeBuilder = itemTypeBuilder + } this.layoutOrientation = orientation } } val composeRecyclerView = remember { RecyclerView(context).apply { - onCreate.invoke(this) this.layoutManager = layoutManager addOnScrollListener(object : InfiniteScrollListener() { override fun onScrollEnd() { @@ -70,18 +83,68 @@ fun ComposeRecyclerView( } } + val itemTouchHelper = remember { + ItemTouchHelper(object : ItemTouchHelper.SimpleCallback(UP or DOWN or START or END, 0) { + override fun onMove( + recyclerView: RecyclerView, + viewHolder: RecyclerView.ViewHolder, + target: RecyclerView.ViewHolder + ): Boolean { + val fromType = adapter.getItemViewType(viewHolder.bindingAdapterPosition) + val toType = adapter.getItemViewType(target.bindingAdapterPosition) + + if (fromType != toType) { + return false + } + + adapter.onItemMove(viewHolder.bindingAdapterPosition, target.bindingAdapterPosition) + onItemMove.invoke( + viewHolder.bindingAdapterPosition, + target.bindingAdapterPosition, + fromType + ) + return true + } + + override fun onSwiped(viewHolder: RecyclerView.ViewHolder, direction: Int) { } + + override fun clearView( + recyclerView: RecyclerView, + viewHolder: RecyclerView.ViewHolder + ) { + viewHolder.itemView.alpha = 1f + onDragCompleted.invoke(viewHolder.bindingAdapterPosition) + } + + override fun onSelectedChanged(viewHolder: RecyclerView.ViewHolder?, actionState: Int) { + super.onSelectedChanged(viewHolder, actionState) + if (actionState == ItemTouchHelper.ACTION_STATE_DRAG) { + viewHolder?.itemView?.alpha = 0.5f + } + } + + override fun isLongPressDragEnabled(): Boolean { + return true + } + }) + } + // Use AndroidView to embed the RecyclerView in the Compose UI AndroidView( - factory = { composeRecyclerView }, + factory = { + composeRecyclerView.apply { + onCreate.invoke(this) + itemTypeBuilder?.let { + itemTouchHelper.attachToRecyclerView(this) + } + } + }, modifier = modifier, update = { - adapter.totalItems = itemCount - adapter.itemBuilder = itemBuilder - adapter.layoutOrientation = orientation + adapter.update(itemCount, itemBuilder, orientation, itemTypeBuilder) } ) - DisposableEffect(key1 = Unit, effect = { onDispose { scrollState = bundleOf("RecyclerviewState" to layoutManager.onSaveInstanceState()) diff --git a/compose-recyclerview/src/main/java/com/example/compose_recyclerview/adapter/ComposeRecyclerViewAdapter.kt b/compose-recyclerview/src/main/java/com/example/compose_recyclerview/adapter/ComposeRecyclerViewAdapter.kt index 8d3e86c..bf248f5 100644 --- a/compose-recyclerview/src/main/java/com/example/compose_recyclerview/adapter/ComposeRecyclerViewAdapter.kt +++ b/compose-recyclerview/src/main/java/com/example/compose_recyclerview/adapter/ComposeRecyclerViewAdapter.kt @@ -11,8 +11,12 @@ import com.example.compose_recyclerview.data.LayoutOrientation /** * RecyclerView adapter for handling dynamically generated Compose items. */ -internal class ComposeRecyclerViewAdapter : - RecyclerView.Adapter() { +class ComposeRecyclerViewAdapter : + RecyclerView.Adapter(){ + + interface ItemTypeBuilder { + fun getItemType(position: Int): Int + } var totalItems: Int = 0 set(value) { @@ -25,7 +29,10 @@ internal class ComposeRecyclerViewAdapter : } } - var itemBuilder: (@Composable (index: Int) -> Unit)? = null + var itemBuilder: (@Composable (index: Int) -> Unit)? = + null + + var itemTypeBuilder: ItemTypeBuilder? = null var layoutOrientation: LayoutOrientation = LayoutOrientation.Vertical set(value) { @@ -34,6 +41,7 @@ internal class ComposeRecyclerViewAdapter : notifyItemChanged(0) } + inner class ComposeRecyclerViewHolder(val composeView: ComposeView) : RecyclerView.ViewHolder(composeView) @@ -44,17 +52,20 @@ internal class ComposeRecyclerViewAdapter : } override fun onBindViewHolder(holder: ComposeRecyclerViewHolder, position: Int) { - holder.composeView.setContent { - when (layoutOrientation) { - LayoutOrientation.Horizontal -> { - Row { - itemBuilder?.invoke(position) + holder.composeView.apply { + tag = holder + setContent { + when (layoutOrientation) { + LayoutOrientation.Horizontal -> { + Row { + itemBuilder?.invoke(position) + } } - } - LayoutOrientation.Vertical -> { - Column { - itemBuilder?.invoke(position) + LayoutOrientation.Vertical -> { + Column { + itemBuilder?.invoke(position) + } } } } @@ -64,6 +75,24 @@ internal class ComposeRecyclerViewAdapter : override fun getItemCount(): Int = totalItems override fun getItemViewType(position: Int): Int { - return position + return itemTypeBuilder?.getItemType(position) ?: 0 + } + + fun onItemMove(fromPosition: Int, toPosition: Int) { + notifyItemMoved(fromPosition, toPosition) + } + + fun update( + itemCount: Int, + itemBuilder: @Composable (index: Int) -> Unit, + layoutOrientation: LayoutOrientation, + itemTypeBuilder: ItemTypeBuilder? + ) { + this.totalItems = itemCount + this.itemBuilder = itemBuilder + this.layoutOrientation = layoutOrientation + itemTypeBuilder?.let { + this.itemTypeBuilder = it + } } -} +} \ No newline at end of file diff --git a/sample-app/src/main/java/com/example/composerecyclerview/MainActivity.kt b/sample-app/src/main/java/com/example/composerecyclerview/MainActivity.kt index 0361509..ba01747 100644 --- a/sample-app/src/main/java/com/example/composerecyclerview/MainActivity.kt +++ b/sample-app/src/main/java/com/example/composerecyclerview/MainActivity.kt @@ -1,6 +1,7 @@ package com.example.composerecyclerview import android.os.Bundle +import android.util.Log import androidx.activity.ComponentActivity import androidx.activity.compose.setContent import androidx.compose.foundation.layout.Box @@ -22,7 +23,14 @@ import androidx.compose.ui.Modifier import androidx.compose.ui.graphics.Color import androidx.compose.ui.unit.dp import androidx.recyclerview.widget.DividerItemDecoration +import androidx.recyclerview.widget.GridLayoutManager +import androidx.recyclerview.widget.ItemTouchHelper +import androidx.recyclerview.widget.ItemTouchHelper.DOWN +import androidx.recyclerview.widget.ItemTouchHelper.END +import androidx.recyclerview.widget.ItemTouchHelper.START +import androidx.recyclerview.widget.ItemTouchHelper.UP import com.example.compose_recyclerview.ComposeRecyclerView +import com.example.compose_recyclerview.adapter.ComposeRecyclerViewAdapter import com.example.composerecyclerview.model.UserData import com.example.composerecyclerview.ui.theme.ComposeRecyclerViewTheme @@ -35,7 +43,7 @@ class MainActivity : ComponentActivity() { modifier = Modifier.fillMaxSize(), color = MaterialTheme.colorScheme.background ) { - val userDataList = List(200) { index -> + var userDataList = List(20) { index -> UserData( "User ${index + 1}", 20 + index, @@ -43,7 +51,7 @@ class MainActivity : ComponentActivity() { ) } - val otherUsersDataList = List(20) { index -> + var otherUsersDataList = List(20) { index -> UserData( "User ${index + 21}", 20 + index, @@ -55,44 +63,88 @@ class MainActivity : ComponentActivity() { modifier = Modifier.fillMaxSize(), itemCount = 1 + userDataList.size + 1 + otherUsersDataList.size, itemBuilder = { 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) - ) + 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 } - return@ComposeRecyclerView - } - val userIndex = index - 1 - if (userIndex < userDataList.size) { - CustomUserItem(user = userDataList[userIndex]) - return@ComposeRecyclerView - } + 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) - ) + 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) + ) + } + return@ComposeRecyclerView } - return@ComposeRecyclerView - } - val otherUserIndex = index - userDataList.size - 2 - if (otherUserIndex < otherUsersDataList.size) { - CustomUserItem(user = otherUsersDataList[otherUserIndex]) - 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) { + 0 -> { + // Do nothing + } + 1 -> { + val fromIndex = fromPosition - 1 + val toIndex = toPosition - 1 + val list = ArrayList(userDataList) + val fromUser = userDataList[fromIndex] + list.removeAt(fromIndex) + list.add(toIndex, fromUser) + userDataList = list + } + 2 -> { + // Do nothing + } + else -> { + val fromIndex = fromPosition - userDataList.size - 2 + val toIndex = toPosition - userDataList.size - 2 + val list = ArrayList(otherUsersDataList) + val fromUser = otherUsersDataList[fromIndex] + list.removeAt(fromIndex) + list.add(toIndex, fromUser) + otherUsersDataList = list + } + } + }, + onDragCompleted = { + Log.e("XXX", "List:${userDataList.map { it.name }}") + }, + 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 -> 0 // Header type + position <= userDataList.size -> 1 // First list item type + position == userDataList.size + 1 -> 2 // Header type + else -> 3 // Second list item type + } } } ) { recyclerView -> @@ -102,6 +154,20 @@ class MainActivity : ComponentActivity() { 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*/ } } } From 45587927d912b5f7cda7815d0bdecf0abb419f45 Mon Sep 17 00:00:00 2001 From: cp-megh Date: Fri, 29 Dec 2023 11:39:53 +0530 Subject: [PATCH 2/2] Minor refactoring --- .../ComposeRecyclerView.kt | 29 +---- .../utils/InfiniteScrollListener.kt | 29 +++++ .../composerecyclerview/MainActivity.kt | 106 ++++++++++-------- 3 files changed, 89 insertions(+), 75 deletions(-) create mode 100644 compose-recyclerview/src/main/java/com/example/compose_recyclerview/utils/InfiniteScrollListener.kt diff --git a/compose-recyclerview/src/main/java/com/example/compose_recyclerview/ComposeRecyclerView.kt b/compose-recyclerview/src/main/java/com/example/compose_recyclerview/ComposeRecyclerView.kt index 2ab7813..5e53b21 100644 --- a/compose-recyclerview/src/main/java/com/example/compose_recyclerview/ComposeRecyclerView.kt +++ b/compose-recyclerview/src/main/java/com/example/compose_recyclerview/ComposeRecyclerView.kt @@ -20,6 +20,7 @@ import androidx.recyclerview.widget.LinearLayoutManager import androidx.recyclerview.widget.RecyclerView import com.example.compose_recyclerview.adapter.ComposeRecyclerViewAdapter import com.example.compose_recyclerview.data.LayoutOrientation +import com.example.compose_recyclerview.utils.InfiniteScrollListener /** * Composable function to display a RecyclerView with dynamically generated Compose items. @@ -31,6 +32,7 @@ import com.example.compose_recyclerview.data.LayoutOrientation * @param orientation The layout direction of the RecyclerView. * @param itemTypeBuilder The optional lambda function to determine the type of each item. * * Required for effective drag and drop. Provide a non-null [ComposeRecyclerViewAdapter.ItemTypeBuilder] when enabling drag and drop functionality. + * * Useful when dealing with multiple item types, ensuring proper handling and layout customization for each type. * @param onDragCompleted Callback triggered when an item drag operation is completed. * @param onItemMove Callback triggered when an item is moved within the RecyclerView. * @param onCreate Callback to customize the RecyclerView after its creation. @@ -150,29 +152,4 @@ fun ComposeRecyclerView( scrollState = bundleOf("RecyclerviewState" to layoutManager.onSaveInstanceState()) } }) -} - -/** - * Abstract class for handling infinite scrolling events in a RecyclerView. - */ -abstract class InfiniteScrollListener : RecyclerView.OnScrollListener() { - override fun onScrolled(recyclerView: RecyclerView, dx: Int, dy: Int) { - super.onScrolled(recyclerView, dx, dy) - if (dy > 0 || dx > 0) { - val layoutManager = recyclerView.layoutManager - if (layoutManager is LinearLayoutManager) { - val visibleItemCount = layoutManager.childCount - val totalItemCount = layoutManager.itemCount - val pastVisibleItems = layoutManager.findFirstVisibleItemPosition() - if (visibleItemCount + pastVisibleItems >= totalItemCount) { - onScrollEnd() - } - } - } - } - - /** - * Callback triggered when the user reaches the end of the list during scrolling. - */ - protected abstract fun onScrollEnd() -} +} \ No newline at end of file diff --git a/compose-recyclerview/src/main/java/com/example/compose_recyclerview/utils/InfiniteScrollListener.kt b/compose-recyclerview/src/main/java/com/example/compose_recyclerview/utils/InfiniteScrollListener.kt new file mode 100644 index 0000000..9672d13 --- /dev/null +++ b/compose-recyclerview/src/main/java/com/example/compose_recyclerview/utils/InfiniteScrollListener.kt @@ -0,0 +1,29 @@ +package com.example.compose_recyclerview.utils + +import androidx.recyclerview.widget.LinearLayoutManager +import androidx.recyclerview.widget.RecyclerView + +/** + * Abstract class for handling infinite scrolling events in a RecyclerView. + */ +abstract class InfiniteScrollListener : RecyclerView.OnScrollListener() { + override fun onScrolled(recyclerView: RecyclerView, dx: Int, dy: Int) { + super.onScrolled(recyclerView, dx, dy) + if (dy > 0 || dx > 0) { + val layoutManager = recyclerView.layoutManager + if (layoutManager is LinearLayoutManager) { + val visibleItemCount = layoutManager.childCount + val totalItemCount = layoutManager.itemCount + val pastVisibleItems = layoutManager.findFirstVisibleItemPosition() + if (visibleItemCount + pastVisibleItems >= totalItemCount) { + onScrollEnd() + } + } + } + } + + /** + * Callback triggered when the user reaches the end of the list during scrolling. + */ + protected abstract fun onScrollEnd() +} diff --git a/sample-app/src/main/java/com/example/composerecyclerview/MainActivity.kt b/sample-app/src/main/java/com/example/composerecyclerview/MainActivity.kt index ba01747..3707a56 100644 --- a/sample-app/src/main/java/com/example/composerecyclerview/MainActivity.kt +++ b/sample-app/src/main/java/com/example/composerecyclerview/MainActivity.kt @@ -23,17 +23,16 @@ import androidx.compose.ui.Modifier import androidx.compose.ui.graphics.Color import androidx.compose.ui.unit.dp import androidx.recyclerview.widget.DividerItemDecoration -import androidx.recyclerview.widget.GridLayoutManager -import androidx.recyclerview.widget.ItemTouchHelper -import androidx.recyclerview.widget.ItemTouchHelper.DOWN -import androidx.recyclerview.widget.ItemTouchHelper.END -import androidx.recyclerview.widget.ItemTouchHelper.START -import androidx.recyclerview.widget.ItemTouchHelper.UP import com.example.compose_recyclerview.ComposeRecyclerView import com.example.compose_recyclerview.adapter.ComposeRecyclerViewAdapter import com.example.composerecyclerview.model.UserData import com.example.composerecyclerview.ui.theme.ComposeRecyclerViewTheme +const val ITEM_TYPE_FIRST_HEADER = 0 +const val ITEM_TYPE_FIRST_LIST_ITEM = 1 +const val ITEM_TYPE_SECOND_HEADER = 2 +const val ITEM_TYPE_SECOND_LIST_ITEM = 3 + class MainActivity : ComponentActivity() { override fun onCreate(savedInstanceState: Bundle?) { super.onCreate(savedInstanceState) @@ -63,53 +62,54 @@ class MainActivity : ComponentActivity() { modifier = Modifier.fillMaxSize(), itemCount = 1 + userDataList.size + 1 + otherUsersDataList.size, itemBuilder = { 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 + 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 + } - val userIndex = index - 1 - if (userIndex < userDataList.size) { - CustomUserItem(user = userDataList[userIndex]) - return@ComposeRecyclerView - } + 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) - ) - } - 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) + ) } + return@ComposeRecyclerView + } - val otherUserIndex = index - userDataList.size - 2 - if (otherUserIndex < otherUsersDataList.size) { - CustomUserItem(user = otherUsersDataList[otherUserIndex]) - 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) { - 0 -> { + when (itemType) { + ITEM_TYPE_FIRST_HEADER -> { // Do nothing } - 1 -> { + + ITEM_TYPE_FIRST_LIST_ITEM -> { val fromIndex = fromPosition - 1 val toIndex = toPosition - 1 val list = ArrayList(userDataList) @@ -118,9 +118,12 @@ class MainActivity : ComponentActivity() { list.add(toIndex, fromUser) userDataList = list } - 2 -> { + + ITEM_TYPE_SECOND_HEADER -> { // Do nothing } + + // ITEM_TYPE_SECOND_LIST_ITEM else -> { val fromIndex = fromPosition - userDataList.size - 2 val toIndex = toPosition - userDataList.size - 2 @@ -133,19 +136,24 @@ class MainActivity : ComponentActivity() { } }, onDragCompleted = { - Log.e("XXX", "List:${userDataList.map { it.name }}") + // 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 -> 0 // Header type - position <= userDataList.size -> 1 // First list item type - position == userDataList.size + 1 -> 2 // Header type - else -> 3 // Second list item type + 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") } ) { recyclerView -> recyclerView.addItemDecoration(