简体   繁体   中英

Delete Item in Android ViewPager2

I am updating my code from using androidx.viewpager to androidx.viewpager2 . I am paging through an undetermined number of fragments showing data records retrieved from a database. Loading the view pager and paging through my data works nicely but I'm having some trouble with deleting an item and updating the pager adapter. I want to delete an item at any given position by calling the removeItem() method (see code below) on my adapter. That should remove the item from my database as well as my fragment and then update the view.

Result is that the correct item is removed from the database. But it does not remove the intended fragment from my view pager but the next page instead. The current page remains visible. I have a bit offsetting the position by plus or minus 1 with no success - in contrary: in those cases my delete routine performed as initially expected. I also tried similar considerations as given eg here .

I'd like to achieve the following behavior:

  1. When deleting any item, the page should be removed and the next one in the list displayed.
  2. When deleting the last/rightmost item in the list, the page should be removed and the previous (now last) page shown.
  3. When deleting the last remaining item (none left) the activity should finish.

My adapter code:

internal class ShapePagerAdapter(private val activity: AppCompatActivity) : FragmentStateAdapter(activity) {
    private val dbManager: DatabaseManager
    private var shapeIds: MutableList<String>? = null

    init {
        dbManager = DatabaseManager(activity)
        try {
            shapeIds = dbManager.getShapeIds()
        } catch (e: DatabaseAccessException) {
            // ...
        }
    }

    override fun getItemCount(): Int {
        return if (null != shapeIds) shapeIds!!.size else 0
    }

    override fun createFragment(position: Int): Fragment {
        return ShapeFragment.newInstance(shapeIds!![position])
    }

    fun removeItem(activity: AppCompatActivity, position: Int) {
        try {
            // Remove from Database.
            dbManager.deleteShape(shapeIds!![position])
            // Remove from View Pager.
            shapeIds!!.removeAt(position)
            notifyItemRemoved(position)
            notifyItemRangeChanged(position , itemCount)
            // Close if nothing to show anymore.
            if (itemCount == 0) {
                activity.finish()
            }
        } catch (e: DatabaseAccessException) {
            // ...
        }
    }
}

Closer study of FragmentStateAdapter reveals that two of its methods must be overridden in this case:

containsItem(long itemId) and getItemId(int position)

Default implementation works for collections that don't add, move, remove items.

Searching for that I found an answer to a similar question , pointing me in the right direction. It does not produce the exact behavior given in my question, which is why I'm posting a slightly adapted version.

Key is that those two methods are implemented in cases there can be changes to the sequence of items. To enable this I maintain a map of items and item ids and update when there are changes to the sequence, in this case a removed item.

internal class ShapePagerAdapter(private val activity: AppCompatActivity) : FragmentStateAdapter(activity) {
    private val dbManager: DatabaseManager
    private lateinit var shapeIds: MutableList<String>
    private lateinit var itemIds: List<Long>

    init {
        dbManager = DatabaseManager(activity)
        try {
            shapeIds = dbManager.getShapeIds()
            updateItemIds()
        } catch (e: DatabaseAccessException) {
            // ...
        }
    }

    override fun getItemCount(): Int = shapeIds.size

    override fun createFragment(position: Int): Fragment = ShapeFragment.newInstance(shapeIds[position])

    fun removeItem(activity: AppCompatActivity, position: Int) {
        try {
            dbManager.deleteShape(shapeIds[position])
            shapeIds.removeAt(position)
            notifyItemRemoved(position)
            notifyItemRangeChanged(position , itemCount)
            updateItemIds()
            if (itemCount == 0) activity.finish()
        } catch (e: DatabaseAccessException) {
            // ...
        }
    }

    private fun updateItemIds() {
        itemIds = shapeIds.map { it.hashCode().toLong() }
    }

    override fun getItemId(position: Int): Long = shapeIds[position].hashCode().toLong()

    override fun containsItem(itemId: Long): Boolean = itemIds.contains(itemId)
    }
}

The technical post webpages of this site follow the CC BY-SA 4.0 protocol. If you need to reprint, please indicate the site URL or the original address.Any question please contact:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM