Gnemlock
Gnemlock

Reputation: 326

Accessing a ViewHolder, directly from the RecyclerView, by position

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

Answers (3)

ataulm
ataulm

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

Baskaran Veerabathiran
Baskaran Veerabathiran

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

Sarath Kn
Sarath Kn

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

Related Questions