187

I have an activity with a RecyclerView and an ImageView. I am using the RecyclerView to show a list of images horizontally. When I click on an image in the RecyclerView the ImageView in the activity should show a bigger picture of the image. So far everything works fine.

Now there are two more ImageButtons in the activity: imageButton_left and imageButton_right. When I click on imageButton_left, the image in the ImageView should turn left and also, the thumbnail in the RecyclerView should reflect this change. Similar is the case with imageButton_right.

I am able to rotate the ImageView. But, how can I rotate the thumbnail in the RecyclerView? How can I get the ViewHolder's ImageView?

Code:

Activity XML:

<?xml version="1.0" encoding="utf-8"?>
<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
    xmlns:app="http://schemas.android.com/apk/res-auto"
    android:layout_width="match_parent"
    android:layout_height="match_parent"
    android:orientation="vertical">

    <android.support.v7.widget.RecyclerView
        android:id="@+id/recyclerview"
        android:layout_width="match_parent"
        android:layout_height="wrap_content"
        android:layout_margin="10dp" />


    <LinearLayout
        android:layout_width="match_parent"
        android:layout_height="wrap_content"
        android:layout_margin="10dp"
        android:orientation="vertical">

        <ImageView
            android:id="@+id/original_image"
            android:layout_width="200dp"
            android:layout_height="200dp"
            android:scaleType="fitXY"
            android:src="@drawable/image_not_available_2" />

        <LinearLayout
            android:layout_width="match_parent"
            android:layout_height="wrap_content"
            android:layout_marginTop="10dp"
            android:gravity="center_horizontal"
            android:orientation="horizontal">


            <ImageButton
                android:id="@+id/imageButton_left"
                android:layout_width="wrap_content"
                android:layout_height="wrap_content"
                android:layout_marginRight="20dp"
                android:background="@drawable/rotate_left_icon" />

            <ImageButton
                android:id="@+id/imageButton_right"
                android:layout_width="wrap_content"
                android:layout_height="wrap_content"
                android:background="@drawable/rotate_right_icon" />

        </LinearLayout>
    </LinearLayout>
</LinearLayout>

My Activity Code:

public class SecondActivity extends AppCompatActivity implements IRecyclerViewClickListener {


    RecyclerView mRecyclerView;
    LinearLayoutManager mLayoutManager;
    RecyclerViewAdapter mRecyclerViewAdapter;
    List<String> urls = new ArrayList<String>();
    ImageView mOriginalImageView;
    ImageButton mLeftRotate, mRightRotate;

    @Override
    protected void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        setContentView(R.layout.activity_second);
        urls.clear();

        mRecyclerView = (RecyclerView) findViewById(R.id.recyclerview);
        mLayoutManager = new LinearLayoutManager(this, android.support.v7.widget.LinearLayoutManager.HORIZONTAL, false);
        mLayoutManager.setOrientation(android.support.v7.widget.LinearLayoutManager.HORIZONTAL);
        mRecyclerView.setLayoutManager(mLayoutManager);
        mRecyclerViewAdapter = new RecyclerViewAdapter(this, urls);
        mRecyclerView.setAdapter(mRecyclerViewAdapter);

        mOriginalImageView = (ImageView) findViewById(R.id.original_image);
        mLeftRotate = (ImageButton) findViewById(R.id.imageButton_left);
        mLeftRotate.setOnClickListener(new View.OnClickListener() {
            @Override
            public void onClick(View v) {
                mOriginalImageView.setRotation(mOriginalImageView.getRotation() - 90);
            }
        });


        mRightRotate = (ImageButton) findViewById(R.id.imageButton_right);
        mRightRotate.setOnClickListener(new View.OnClickListener() {
            @Override
            public void onClick(View v) {
                mOriginalImageView.setRotation(mOriginalImageView.getRotation() + 90);
            }
        });

        Intent intent = getIntent();
        if (intent != null) {

            String portfolio = intent.getStringExtra("portfolio");

            try {

                JSONArray jsonArray = new JSONArray(portfolio);

                for (int i = 0; i < jsonArray.length(); i++) {

                    JSONObject jsonObject = jsonArray.getJSONObject(i);

                    String url = jsonObject.getString("url");
                    urls.add(url);
                }

                Log.d(Const.DEBUG, "URLs: " + urls.toString());

                mRecyclerViewAdapter.notifyDataSetChanged();

            } catch (Exception e) {
                e.printStackTrace();
            }

        }

    }


    @Override
    public void onItemClick(int position) {
        Picasso.with(this).load(urls.get(position)).into(mOriginalImageView);
    }
}

My Custom Adapter for RecyclerView:

public class RecyclerViewAdapter extends RecyclerView.Adapter<RecyclerViewAdapter.ViewHolder> {

    Context context;
    List<String> mUrls = new ArrayList<String>();

    IRecyclerViewClickListener mIRecyclerViewClickListener;

    public int position;

    public int getPosition() {
        return position;
    }

    public void setPosition(int position) {
        this.position = position;
    }



    public RecyclerViewAdapter(Context context, List<String> urls) {
        this.context = context;
        this.mUrls.clear();
        this.mUrls = urls;

        Log.d(Const.DEBUG, "Urls Size: " + urls.size());
        Log.d(Const.DEBUG, urls.toString());

        if (context instanceof IRecyclerViewClickListener)
            mIRecyclerViewClickListener = (IRecyclerViewClickListener) context;
        else
            Log.d(Const.DEBUG, "Implement IRecyclerViewClickListener in Activity");
    }

    @Override
    public ViewHolder onCreateViewHolder(ViewGroup parent, int viewType) {
        View view = LayoutInflater.from(context).inflate(R.layout.item_horizontal_recyclerview, parent, false);
        ViewHolder holder = new ViewHolder(view);
        return holder;
    }

    @Override
    public void onBindViewHolder(ViewHolder holder, int position) {
        Picasso.with(context).load(mUrls.get(position)).into(holder.mImageView);
    }

    @Override
    public int getItemCount() {
        return mUrls.size();
    }


    public void rotateThumbnail() {


    }

    public class ViewHolder extends RecyclerView.ViewHolder implements View.OnClickListener {

        public ImageView mImageView;
        public View v;

        public ViewHolder(View v) {
            super(v);
            v.setTag(getAdapterPosition());
            v.setOnClickListener(this);
            this.mImageView = (ImageView) v.findViewById(R.id.image);
        }

        @Override
        public void onClick(View v) {
            this.v = v;
            mIRecyclerViewClickListener.onItemClick(getAdapterPosition());
        }
    }


}
Vivek Barai
  • 1,338
  • 13
  • 26
Vamsi Challa
  • 11,038
  • 31
  • 99
  • 149
  • 1
    You should make it in your viewHolder constructor, add a property that will tell you if the image needs to be rotated, then just change that property on your data and call notifyDataSetChanged to make the recyclerView reload – Nanoc Nov 18 '15 at 15:47
  • Do any of the answers solve your problem? – SMBiggs Sep 08 '16 at 16:44
  • ViewHolder should be static for performance issue. All variables from the outer class should be passed through the constructor. – AppiDevo Mar 13 '17 at 21:19
  • As a supplement to @Scott's answer Click [here](https://stackoverflow.com/questions/35428735/how-to-get-child-view-from-recyclerview/49061085#49061085) – mut tony Mar 02 '18 at 00:57

16 Answers16

314

This is what you're looking for.

I had this problem too. And like you, the answer is very hard to find. But there IS an easy way to get the ViewHolder from a specific position (something you'll probably do a lot in the Adapter).

myRecyclerView.findViewHolderForAdapterPosition(pos);

NOTE: If the View has been recycled, this will return null. Thanks to Michael for quickly catching this omission. Furthermore, if the user is scrolling quickly, the view can be recycled even after you obtain this ViewHolder, causing all kinds of problems. So be careful when using this technique, especially if you're doing relatively slow work like changing the look of the View(s).

SMBiggs
  • 11,034
  • 6
  • 68
  • 83
  • 3
    This is exactly what I was looking for in the LayoutManager – Ed Lee Sep 22 '16 at 23:43
  • 18
    This works, however, if the `ViewHolder` you are trying to reference was "recycled", it will return `null`. – Michael Jul 13 '17 at 03:54
  • 8
    @Michael, good catch! That's a warning to all you out there, test for null first (yeah, like everything else in Android)! – SMBiggs Jul 14 '17 at 20:52
  • 2
    Can we do this from inside the adapter? – Mauker Jul 24 '17 at 22:16
  • 2
    @Mauker : You can use this from anything that can access the RecyclerView instance. – SMBiggs Aug 02 '17 at 23:32
  • @ScottBiggs is there any way to access children that are not yet shown or visible ? – MHSaffari Dec 12 '17 at 06:45
  • @MHSFisher Children that are not visible don't really exist in the world of RecyclerViews. The `onBindViewHolder()` is called once the View is about to be displayed, which is the best you can do. To access children, you have to manually traverse the hierarchy, as usual. So that's a long-winded way of saying, "Not that I know." – SMBiggs Dec 18 '17 at 04:32
  • As a supplement to @Scott's answer Click [here](https://stackoverflow.com/a/49061085/1457696) – mut tony Mar 02 '18 at 01:21
  • For those who are struggling to find `RecycledViews` store the holders in List. Have a look at this answer https://stackoverflow.com/a/49554028/6549598 – nimi0112 May 29 '18 at 06:48
  • See also https://stackoverflow.com/questions/32836844/android-recyclerview-findviewholderforadapterposition-returns-null. If you call `notifyDataSetChanged()`, it will return `null`. – CoolMind Apr 06 '22 at 15:57
181

I suppose you are using a LinearLayoutManager to show the list. It has a nice method called findViewByPosition that

Finds the view which represents the given adapter position.

All you need is the adapter position of the item you are interested in.

edit: as noted by Paul Woitaschek in the comments, findViewByPosition is a method of LayoutManager so it would work with all LayoutManagers (i.e. StaggeredGridLayoutManager, etc.)

Community
  • 1
  • 1
stan0
  • 11,549
  • 6
  • 42
  • 59
  • 11
    Thats actually a method of the `LayoutManager` so this will work for all LayoutManager subclasses. – Paul Woitaschek Aug 05 '16 at 13:46
  • Looked at the source code. LinearLayoutManger looks up via directAccess on the backing array when not in prelayout, whereas the recyclerView method always does traversal. So this method is likely more efficient. – NameSpace Sep 13 '16 at 05:46
  • Hey @stanO can you please help me with this https://stackoverflow.com/questions/49952965/recyclerview-horizontal-scrolling-to-left?noredirect=1#comment87861669_49952965 –  May 19 '18 at 13:15
  • 4
    Returns: View--The child view that represents the given position or null if the position is not laid out... recyclerview --> most things return null – me_ Sep 23 '18 at 01:24
  • 1
    Keep in mind that you have to wait until adapter will added to list, see my answer below for details – Konstantin Konopko Feb 18 '19 at 16:48
  • @stanO can you see my question please https://stackoverflow.com/questions/68554233/how-to-get-view-with-position-in-recyclerview ? – Nima Khalili Aug 01 '21 at 06:03
34

If you want the View, make sure to access the itemView property of the ViewHolder like so: myRecyclerView.findViewHolderForAdapterPosition(pos).itemView;

Horatio
  • 1,695
  • 1
  • 18
  • 27
16

You can use use both

recyclerViewInstance.findViewHolderForAdapterPosition(adapterPosition) and recyclerViewInstance.findViewHolderForLayoutPosition(layoutPosition). Be sure that RecyclerView view uses two type of positions

Adapter position: Position of an item in the adapter. This is the position from the Adapter's perspective.

Layout position: Position of an item in the latest layout calculation. This is the position from the LayoutManager's perspective. You should use getAdapterPosition() for findViewHolderForAdapterPosition(adapterPosition) and getLayoutPosition() for findViewHolderForLayoutPosition(layoutPosition).

Take a member variable to hold previously selected item position in recyclerview adapter and other member variable to check whether user is clicking for first time or not.

Sample code and screen shots are attached for more information at the bottom.

public class MainActivity extends AppCompatActivity {     

private RecyclerView mRecyclerList = null;    
private RecyclerAdapter adapter = null;    

@Override    
protected void onCreate(Bundle savedInstanceState) {    
    super.onCreate(savedInstanceState);    
    setContentView(R.layout.activity_main);    

    mRecyclerList = (RecyclerView) findViewById(R.id.recyclerList);    
}    

@Override    
protected void onStart() {    
    RecyclerView.LayoutManager layoutManager = null;    
    String[] daysArray = new String[15];    
    String[] datesArray = new String[15];    

    super.onStart();    
    for (int i = 0; i < daysArray.length; i++){    
        daysArray[i] = "Sunday";    
        datesArray[i] = "12 Feb 2017";    
    }    

    adapter = new RecyclerAdapter(mRecyclerList, daysArray, datesArray);    
    layoutManager = new LinearLayoutManager(MainActivity.this);    
    mRecyclerList.setAdapter(adapter);    
    mRecyclerList.setLayoutManager(layoutManager);    
}    
}    


public class RecyclerAdapter extends RecyclerView.Adapter<RecyclerAdapter.MyCardViewHolder>{          

private final String TAG = "RecyclerAdapter";        
private Context mContext = null;        
private TextView mDaysTxt = null, mDateTxt = null;    
private LinearLayout mDateContainerLayout = null;    
private String[] daysArray = null, datesArray = null;    
private RecyclerView mRecyclerList = null;    
private int previousPosition = 0;    
private boolean flagFirstItemSelected = false;    

public RecyclerAdapter(RecyclerView mRecyclerList, String[] daysArray, String[] datesArray){    
    this.mRecyclerList = mRecyclerList;    
    this.daysArray = daysArray;    
    this.datesArray = datesArray;    
}    

@Override    
public MyCardViewHolder onCreateViewHolder(ViewGroup parent, int viewType) {    
    LayoutInflater layoutInflater = null;    
    View view = null;    
    MyCardViewHolder cardViewHolder = null;    
    mContext = parent.getContext();    
    layoutInflater = LayoutInflater.from(mContext);    
    view = layoutInflater.inflate(R.layout.date_card_row, parent, false);    
    cardViewHolder = new MyCardViewHolder(view);    
    return cardViewHolder;    
}    

@Override    
public void onBindViewHolder(MyCardViewHolder holder, final int position) {    
    mDaysTxt = holder.mDaysTxt;    
    mDateTxt = holder.mDateTxt;    
    mDateContainerLayout = holder.mDateContainerLayout;    

    mDaysTxt.setText(daysArray[position]);    
    mDateTxt.setText(datesArray[position]);    

    if (!flagFirstItemSelected){    
        mDateContainerLayout.setBackgroundColor(Color.GREEN);    
        flagFirstItemSelected = true;    
    }else {    
        mDateContainerLayout.setBackground(null);    
    }    
}    

@Override    
public int getItemCount() {    
    return daysArray.length;    
}    

class MyCardViewHolder extends RecyclerView.ViewHolder{    
    TextView mDaysTxt = null, mDateTxt = null;    
    LinearLayout mDateContainerLayout = null;    
    LinearLayout linearLayout = null;    
    View view = null;    
    MyCardViewHolder myCardViewHolder = null;    

    public MyCardViewHolder(View itemView) {    
        super(itemView);    
        mDaysTxt = (TextView) itemView.findViewById(R.id.daysTxt);    
        mDateTxt = (TextView) itemView.findViewById(R.id.dateTxt);    
        mDateContainerLayout = (LinearLayout) itemView.findViewById(R.id.dateContainerLayout);    

        mDateContainerLayout.setOnClickListener(new View.OnClickListener() {    
            @Override    
            public void onClick(View v) {    
                LinearLayout linearLayout = null;    
                View view = null;    

                if (getAdapterPosition() == previousPosition){    
                    view = mRecyclerList.findViewHolderForAdapterPosition(previousPosition).itemView;    
                    linearLayout = (LinearLayout) view.findViewById(R.id.dateContainerLayout);    
                    linearLayout.setBackgroundColor(Color.GREEN);    
                    previousPosition = getAdapterPosition();    

                }else {    
                    view = mRecyclerList.findViewHolderForAdapterPosition(previousPosition).itemView;    
                    linearLayout = (LinearLayout) view.findViewById(R.id.dateContainerLayout);    
                    linearLayout.setBackground(null);    

                    view = mRecyclerList.findViewHolderForAdapterPosition(getAdapterPosition()).itemView;    
                    linearLayout = (LinearLayout) view.findViewById(R.id.dateContainerLayout);    
                    linearLayout.setBackgroundColor(Color.GREEN);    
                    previousPosition = getAdapterPosition();    
                }    
            }    
        });    
    }    
}       

} first element selected second element selected and previously selected item becomes unselected fifth element selected and previously selected item becomes unselected

Jorge Gil
  • 4,265
  • 5
  • 38
  • 57
  • And I thought this discussion was exhausted! Thanks for further clarifying this issue. – SMBiggs Mar 05 '18 at 20:50
  • 2
    Here I am getting the NullPointer exception in the else block when I am trying to change the color of the previous item, so I added the null check, now it is never entering into that block if it is having NullPointer exception, so previous item color is never changed. now my Recycler view has items with multiple colored items. how do i solve this – Abhilash Reddy Jun 05 '18 at 20:56
12

You can simply use "findViewHolderForAdapterPosition" method of recycler view and you will get a viewHolder object from that then typecast that viewholder into your adapter viewholder so you can directly access your viewholder's views

following is the sample code for kotlin

 val viewHolder = recyclerView.findViewHolderForAdapterPosition(position)
 val textview=(viewHolder as YourViewHolder).yourTextView
Mayank Sharma
  • 2,735
  • 21
  • 26
9

You can make ArrayList of ViewHolder :

 ArrayList<MyViewHolder> myViewHolders = new ArrayList<>();
 ArrayList<MyViewHolder> myViewHolders2 = new ArrayList<>();

and, all store ViewHolder(s) in the list like :

 @Override
    public void onBindViewHolder(@NonNull final MyViewHolder holder, final int position) {
        final String str = arrayList.get(position);

        myViewHolders.add(position,holder);
}

and add/remove other ViewHolder in the ArrayList as per your requirement.

Tarun Umath
  • 900
  • 10
  • 7
8

To get a View from a specific position of recyclerView you need to call findViewHolderForAdapterPosition(position) on recyclerView object that will return RecyclerView.ViewHolder

from the returned RecyclerView.ViewHolder object with itemView you can access all Views at that particular position

RecyclerView.ViewHolder rv_view = recyclerView.findViewHolderForAdapterPosition(position);

ImageView iv_wish = rv_view.itemView.findViewById(R.id.iv_item);
tushar
  • 315
  • 4
  • 6
6

You can get a view for a particular position on a recyclerview using the following

int position = 2;
RecyclerView.ViewHolder viewHolder = recyclerview.findViewHolderForItemId(position);
View view = viewHolder.itemView;
ImageView imageView = (ImageView)view.findViewById(R.id.imageView);
Sunday G Akinsete
  • 802
  • 13
  • 14
6

Keep in mind that you have to wait until adapter will added to list, then you can try to getting view by position

final int i = 0;
recyclerView.setAdapter(adapter);
recyclerView.post(new Runnable() {
    @Override
    public void run() {
        View view = recyclerView.getLayoutManager().findViewByPosition(i);
    }
});
Konstantin Konopko
  • 5,229
  • 4
  • 36
  • 62
  • 2
    This is not always working if you add multiple items (i.e.: if you have a button that adds them and press it fast). You should add the runnable with delay. – Alex Burdusel Nov 03 '19 at 17:43
2

To get specific view from recycler view list OR show error at edittext of recycler view.

private void popupErrorMessageAtPosition(int itemPosition) {

    RecyclerView.ViewHolder viewHolder = recyclerView.findViewHolderForAdapterPosition(itemPosition);
    View view = viewHolder.itemView;
    EditText etDesc = (EditText) view.findViewById(R.id.et_description);
    etDesc.setError("Error message here !");
}
Chirag Prajapati
  • 337
  • 6
  • 14
1

You can as well do this, this will help when you want to modify a view after clicking a recyclerview position item

@Override
public void onClick(View view, int position) {

            View v =  rv_notifications.getChildViewHolder(view).itemView;
            TextView content = v.findViewById(R.id.tv_content);
            content.setText("Helloo");

        }
mut tony
  • 357
  • 7
  • 9
0

You can use below

mRecyclerview.getChildAt(pos);
N. Kyalo
  • 47
  • 3
  • It's not true. This way you will get a child view to RecyclerView, not it's item (0th, 1st, 2nd, etc). – CoolMind Apr 06 '22 at 15:55
0

RecyclerView - Get view at particular position. //int is = position; This line of code worked for me.

  @Override
  public void onBindViewHolder(@NonNull @org.jetbrains.annotations.NotNull QuizAdapter.MyViewHolder holder, int position) {

        final QuizModel tmodel = modellist.get(position);
        holder.tv_question.setText(tmodel.getQuestion());
        holder.tv_option_One.setText(tmodel.getOptionA());
        holder.tv_option_two.setText(tmodel.getOptionB());
        holder.tv_option_three.setText(tmodel.getOptionC());
        holder.tv_option_four.setText(tmodel.getOptionD());
        holder.tv_option_five.setText(tmodel.getOptionE());
        holder.ll.setOnClickListener(new View.OnClickListener() {
            @Override
            public void onClick(View v) {

                int is = position;
                Toast.makeText(v.getContext(), "count" + is, Toast.LENGTH_LONG).show();

                Intent i = new Intent(context, DetailsQuizActivity.class);
                i.putExtra("question", tmodel.getQuestion());
                i.putExtra("option_a", tmodel.getOptionA());
                i.putExtra("option_b", tmodel.getOptionB());
                i.putExtra("option_c", tmodel.getOptionC());
                i.putExtra("option_d", tmodel.getOptionD());
                i.putExtra("option_e", tmodel.getOptionE());
                i.putExtra("answer", tmodel.getOptionAnswer());
                i.setFlags(Intent.FLAG_ACTIVITY_NEW_TASK);
                context.startActivity(i);
            }
        });
Samudra Ganguly
  • 637
  • 4
  • 24
0

*If the adapter remains of equal size, (not changed by added item or remove), you can create a list of ViewHolder in adapter, then in onCreateViewHolder you add the new ViewHolder to the list. Where you can access adapter you can access to ViewHolder to the particular position with the list: adapter.listHolder.get(position)

*For adpater not equaled size: little bite complex, update listHolder in fonction of new item added to holder, or removed

  • Lists can be unpredictably long. Suggesting to save all the views in a list can lead to memory issues. – Eury Pérez Beltré Apr 14 '22 at 23:45
  • 1
    Your answer could be improved with additional supporting information. Please [edit] to add further details, such as citations or documentation, so that others can confirm that your answer is correct. You can find more information on how to write good answers [in the help center](/help/how-to-answer). – Community Apr 14 '22 at 23:46
-1

If you guys are having null with every attempt to get a view with any int position, try to add a new constructor parameter to your adapter like this for example:

class RecyclerViewTableroAdapter(
private val fichas: Array<MFicha?>,
private val activity: View.OnClickListener,
private val indicesGanadores:MutableList<Int>
) : RecyclerView.Adapter<RecyclerViewTableroAdapter.ViewHolder>() {
//CODE 
}

I added indicesGanadores to color my cardview background if my game is won.

override fun onBindViewHolder(holder: ViewHolder, position: Int) {
//CODE
if(indicesGanadores.contains(position)){
        holder.cardViewFicha.setCardBackgroundColor((activity as MainActivity).resources.getColor(R.color.DarkGreen))
    }
//MORE CODE
}  

If I don't have to color my background yet I just send an empty mutable list like this:

binding.recyclerViewMain.adapter = RecyclerViewTableroAdapter(fichasTablero, this@MainActivity, mutableListOf<Int>())

Happy coding!...

Ramiro G.M.
  • 357
  • 4
  • 7
-1
recyclerView.post(new Runnable() {
@Override
public void run() {
    for (int i = 0; i < 5; i++) {
        View viewTemp = recyclerView.getLayoutManager().findViewByPosition(i);
        CardView cardView = (CardView) viewTemp.findViewById(R.id.cardView); 
        //TO DO
    }
}

});

  • Hello @peter-linh, thank you for contributing to Stackoverflow, Please make your answers elaborate. Take a look at https://stackoverflow.com/help/how-to-answer to see how to write a good answer. – kplus Jun 18 '21 at 20:16