'How can i define selection (hotspot) area in Multi-Selection RecyclerView?
I have a Multi-Selection RecyclerView. I am tring to handle button click inside to RecyclerView Item when It's selected/activated. But when an item selected/activated in RecyclerView I am not able to click the ChildView (Button) It handles RootView of content and reverts to the deactivated/notselected state.
I read the documantation It says
Selection Hotspot
This is an optional feature identifying an area within a view that is single-tap to select. Ordinarily a single tap on an item when there is no existing selection will result in that item being activated. If the tap occurs within the "selection hotspot" the item will instead be selected.
See OnItemActivatedListener for details on handling item activation.
I tried to override inSelectionHotspot function
/**
* Areas are often included in a view that behave similar to checkboxes, such
* as the icon to the left of an email message. "selection
* hotspot" provides a mechanism to identify such regions, and for the
* library to directly translate taps in these regions into a change
* in selection state.
*
* @return true if the event is in an area of the item that should be
* directly interpreted as a user wishing to select the item. This
* is useful for checkboxes and other UI affordances focused on enabling
* selection.
*/
public boolean inSelectionHotspot(@NonNull MotionEvent e) {
return false;
}
The document's comment reffers to Gmail application, that example to selection by clicking e-mail's leftside ImageView and you can able to move e-mail's content by clicking to Title or Description area. That's exactly what I'm trying to do.
ItemDetailsLookup.ItemDetails Class
class PurchaseItemDetails(var binding: ListObjectLevelPurchaseBinding) : ItemDetailsLookup.ItemDetails<LevelModel>() {
var itemPosition: Int = 0
lateinit var item: LevelModel
override fun getSelectionKey(): LevelModel? {
return item
}
override fun getPosition(): Int {
return itemPosition
}
override fun inSelectionHotspot(e: MotionEvent): Boolean {
val locationOnScreen = IntArray(2)
binding.buttonLevelPurchaseInspect.getLocationOnScreen(locationOnScreen)
val (left) = locationOnScreen
val right = left + binding.buttonLevelPurchaseInspect.width
Log.e("InSelectionHotspot","${e.x.roundToInt() !in left..right}")
return e.x.roundToInt() !in left..right
}
}
I also tried to override onItemActivatedListener nothing changed.
tracker = SelectionTracker.Builder<LevelModel>(
"selection.levels",
binding.recyclerviewBundleDetailPurchaseLevels,
PurchaseItemKeyProvider(adapter.currentList),
PurchaseItemLookup(binding.recyclerviewBundleDetailPurchaseLevels),
StorageStrategy.createParcelableStorage(LevelModel::class.java)
).withSelectionPredicate(
SelectionPredicates.createSelectAnything()
).build()
Solution 1:[1]
Although this question is old, I think there always will be Android devs that are struggling with the RecyclerView's selection API. As always the Google's library is lacking details and some useful examples. That's why I'd like to share a solution for those who keep struggling for a working setup of selection hotspot. Sorry for the inconvenience, the code is in Java since I don't use Kotlin yet. But I think it is easy to convert it to Kotlin.
ItemDetailsLookup.ItemDetails Class
public static class PurchaseItemDetails extends ItemDetailsLookup.ItemDetails<Long> {
private long position;
private Rect hotSpot;
public PurchaseItemDetails() {}
@Override
public int getPosition() {
return (int) position;
}
@Nullable
@Override
public Long getSelectionKey() {
return position;
}
@Override
public boolean inSelectionHotspot(@NonNull MotionEvent e) {
if(hotSpot == null) {
Log.d(TAG, "inSelectionHotspot: null; Event X "+e.getX()+", Y "+e.getY());
return false;
}
// Here the coordinates of selection hotspot rect can be observed
Log.d(TAG, String.format("inSelectionHotspot: Hotspot %s; event X %f, Y " +
"%f", hotSpot.toShortString(), e.getX(), e.getY()));
return hotSpot.contains((int) e.getX(), (int) e.getY());
}
void setPosition(long position) {
this.position = position;
}
void setHotSpot(Rect hotSpot) {
this.hotSpot = hotSpot;
}
}
Now that we have implemented the ItemDetails class we have to have this class within the ViewHolder class in order to be able to define and set the selection hotspot.
SampleViewHolder Class
class SampleViewHolder extends RecyclerView.ViewHolder {
private final PurchaseItemDetails details;
//...
public SampleViewHolder(@NonNull View itemView) {
super(itemView);
details = new PurchaseItemDetails();
}
void bind() {
// Setup selection hotspot
Rect rect = new Rect();
// Attention here! We must get the rectangle after the viewholder object has created.
// Otherwise the getGlobalVisibleRect() will return unexpected values.
binding.buttonLevelPurchaseInspect.getGlobalVisibleRect(rect);
// Set the rectangle spot so that the Item details can check and recognize the spot.
details.setHotSpot(rect);
// Setup position
details.setPosition(getBindingAdapterPosition());
//...
}
//...
}
And finally somewhere in the adapter class where we bind the view holder:
@Override
public void onBindViewHolder(@NonNull SampleViewHolder holder, int position) {
holder.bind();
}
Warning!! This is not a whole code at all. The implementers must adapt it to their codes and convenience.
Sources
This article follows the attribution requirements of Stack Overflow and is licensed under CC BY-SA 3.0.
Source: Stack Overflow
| Solution | Source |
|---|---|
| Solution 1 | Kozmotronik |
