Reputation: 326
I have a RecyclerView, which displays a series of views derived from a database. I wish to confirm that the user has selected one of the views by highlighting its edges, and I have succeeded in doing so. If the user proceeds to select a differant option, I wish to remove the highlight on the original selection. This is where I have ran into a bit of trouble.
The initial highlight was no trouble, as I was doing it, internally. However, I do not know how to access the previous view with only its adapter position. I have been searching StackOverflow for about an hour now, as I can not find much to go on in the Android API, or from google. Many users appear to be asking similar questions, but ultimately, subtle differences void any useful answers.
Inside my ViewHolder
, which is an internal public class
of my RecyclerView
, I have an OnClickListener
as follows:
@Override
public void onClick(View view) {
if(!selected) {
selected = true;
view.setBackgroundResource(R.drawable.default_selection_background);
} else {
selected = false;
view.setBackgroundResource(0);
}
UpdateSelected(getAdapterPosition());
}
This points back to my RecyclerView
, and frankly, I am yet to add any working code to my UpdateSelected(int position)
method.
If it helps, I intend to have it function like this:
void UpdateSelected(int position) {
if(position != currentlySelected) {
ViewHolder[currentlySelected].Deselect();
}
}
public class ViewHolder extends RecyclerView.ViewHolder {
...
public void Deselect() {
// and from here, I can go on as normal.
}
}
I have noticed users advising others to use getLayoutPosition()
to derive the position for UI purposes, unless they specifically wish to continue working with the internal data, which is what I intend to do.
How do I access a particular ViewHolder from my RecyclerView, using its position?
Upvotes: 0
Views: 2316
Reputation: 15334
Instead of swapping the background resource manually, you can instead mark the view as selected or not, and have Android change the background using drawable selectors.
res/drawable/background.xml:
<?xml version="1.0" encoding="utf-8"?>
<selector xmlns:android="http://schemas.android.com/apk/res/android">
<item android:state_selected="true" android:drawable="@drawable/default_selection_background" />
<item android:drawable="@null" />
</selector>
background.xml
would be set as the android:background
in your item's layout XML.
Then in your onBindViewHolder(ViewHolder holder, int position)
you update the state of the Android View:
@Override
public void onBindViewHolder(ViewHolder holder, int position) {
boolean isSelected = position == selectedPosition;
holder.itemView.setSelected(isSelected);
}
For me, I would prefer having an Adapter which was dumber and had less logic. You can do this by keeping a list of ViewModels (data classes that represent what you'll show in the view - no more, no less) where one of the properties could be the selected flag (in each ViewModel).
When the user selects or deselects a View, a callback issues an update to the presenter, which then updates the adapter, and notifies the adapter which positions have been updated. This would mean keeping the current selected item (preferably ID, but could store ViewModel or position) in the presenter so you can update it.
Upvotes: 0
Reputation: 159
You can access the viewholder by it's position from recyclerview
Here is my implementation
recyclerView.addOnItemTouchListener(new RecyclerItemClickListener(this,
recyclerView, new RecyclerItemClickListener.OnItemClickListener() {
@Override
public void onItemClick(View view, int position) {
/*your coding */
}
@Override
public void onItemLongClick(View view, int position) {
}
}));
and create the Class
public class RecyclerItemClickListener implements RecyclerView.OnItemTouchListener {
private OnItemClickListener mListener;
private GestureDetector mGestureDetector;
public RecyclerItemClickListener(Context context, final RecyclerView recyclerView, OnItemClickListener listener) {
mListener = listener;
mGestureDetector = new GestureDetector(context, new GestureDetector.SimpleOnGestureListener() {
@Override
public boolean onSingleTapUp(MotionEvent e) {
return true;
}
@Override
public void onLongPress(MotionEvent e) {
View childView = recyclerView.findChildViewUnder(e.getX(), e.getY());
if (childView != null && mListener != null) {
mListener.onItemLongClick(childView, recyclerView.getChildPosition(childView));
}
}
});
}
@Override
public boolean onInterceptTouchEvent(RecyclerView view, MotionEvent e) {
View childView = view.findChildViewUnder(e.getX(), e.getY());
if (childView != null && mListener != null && mGestureDetector.onTouchEvent(e)) {
mListener.onItemClick(childView, view.getChildPosition(childView));
}
return false;
}
@Override
public void onTouchEvent(RecyclerView view, MotionEvent motionEvent) {
}
@Override
public void onRequestDisallowInterceptTouchEvent(boolean disallowIntercept) {
}
public interface OnItemClickListener {
void onItemClick(View view, int position);
void onItemLongClick(View view, int position);
}
}
Upvotes: 1
Reputation: 2725
You could do something like this.
In your adapter , create a member variable which will keep track of the selected item position.
int selectedPosition = -1; // -1 some default value for nothing selected
Then in your onBindViewHolder
of recycler view adapter
int backgroundRes = (position == selectedPosition)? R.drawable.default_selection_background : 0;
view.setBackgroundResource(backgroundRes);
Finally in onClick of your viewholder
@Override
public void onClick(View view) {
selectedPosition = getAdapterPosition();
notifyDataSetChanged();
}
Upvotes: 1