Mahmudul Hasan Shohag
Mahmudul Hasan Shohag

Reputation: 3156

recyclerview-selection: stop auto item deselection when touching to the blank space inside the recyclerview

I am trying to use the recyclerview-selection library in my project. I followed this tutorial:

https://proandroiddev.com/a-guide-to-recyclerview-selection-3ed9f2381504

Everything workes fine. But I have a problem. If I tap/touch any blank space inside the RecyclerView, all the selected elements got deselected! I don't find any method or solution to disable this. What should I do?

I am using implementation 'androidx.recyclerview:recyclerview-selection:1.1.0-rc01' in my project.

Edit 1:

example image

I set the RecyclerView background red to describe my problem. Here, blue items are selected items. If I click any red area, then all the selected items got unselected! The select and deselect should only be done by clicking the items. So, I need to disable this feature (or bug!), that unselect all items!

Example project: https://github.com/ImaginativeShohag/multiselection

Upvotes: 4

Views: 1222

Answers (2)

pinkfloyd
pinkfloyd

Reputation: 255

Solution is to have an out of context selection item like:

class ItemLookup(private val recyclerView: RecyclerView) : ItemDetailsLookup<Long>() {
    private val outOfContextSelection = object : ItemDetails<Long>() {
        override fun getPosition(): Int = OUT_OF_CONTEXT_POSITION.toInt()
        override fun getSelectionKey() = OUT_OF_CONTEXT_POSITION
    }
    
    override fun getItemDetails(e: MotionEvent): ItemDetails<Long>? {
        recyclerView.findChildViewUnder(e.x, e.y)?.let {
            return (recyclerView.getChildViewHolder(it) as? SelectorBarAdapter.SelectorBarViewHolder)?.itemDetails
        }
        return outOfContextSelection
    }
    
    companion object {
        const val OUT_OF_CONTEXT_POSITION = 10000L
    }
}

So that when a view which is not one of our clickable elements is clicked we can identify further on a predicate like follows:

class SingleSelectionPredicate : SelectionTracker.SelectionPredicate<Long>() {
    override fun canSetStateForKey(key: Long, nextState: Boolean): Boolean {
        // warranties that an item can't be unselected on click
        // warranties that clicks out of the item's scope are disabled
        return nextState && key != ItemLookup.OUT_OF_CONTEXT_POSITION
    }

    override fun canSetStateAtPosition(position: Int, nextState: Boolean) = true
    override fun canSelectMultiple() = false
}

Upvotes: 2

Gregor B.
Gregor B.

Reputation: 142

You create your own SelectionTracker.SelectionPredicate<Long>.

Override the method canSetStateForKey(key: Long, nextState: Boolean)

like this:

override fun canSetStateForKey(@NonNull key: Long, nextState: Boolean): Boolean {
    rv.findViewHolderForItemId(key)?.let { holder -> adapter.canSetStateForItem(holder as YourItemHolder<YourItem>, nextState)}
    return true
}

and in your ViewHolder check if the item is already selected if so return false or vice versa.

Edit:

You can also define a "Selection Hotspot" by inSelectionHotspot(e: MotionEvent) in your ItemDetailsLookup.ItemDetails.

In your ViewHolder you can then check if the TouchEvent is in an area that requires a select or unselect.

for example:

override fun inSelectionHotspot(e: MotionEvent): Boolean {
    val rect = Rect()
    itemView.getGlobalVisibleRect(rect)
    if (rect.contains(e.rawX.toInt(), e.rawY.toInt())) { 
        //  in select region
    } else { 
        // not in select region 
    }
}

Upvotes: -1

Related Questions