How to display an ArrayList in a RecyclerView?

FET picture FET · Jun 13, 2016 · Viewed 38.3k times · Source

I need to add an Activity where I can list the elements of an ArrayList<CustomClass> and I've seen there's a newer and better way to display lists - RecyclerView.

My question is how to implement this into my app. I've found that I need to use an Adapter, but I don't quite understand how to implement the whole process correctly.

If you're wondering, I'm referring to this examples of the docs, which I have been reading.

EDIT:

After having updated my code, it says it cannot resolve the symbol setOnEntryClickListener:

public class voting extends Activity {


RecyclerView myList;
private ArrayList<Player> players; // Players


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

    Intent intent = this.getIntent();
    Bundle bundle = intent.getExtras();
    players = (ArrayList<Player>)bundle.getSerializable("PLAYERS");

    myList = (RecyclerView) findViewById(R.id.charactersList);
    myList.setLayoutManager(new LinearLayoutManager(this));
    CoursesAdapter adapter = new CoursesAdapter(players);
    myList.setAdapter(adapter);
}

// OR RecyclerView with a click listener

CoursesAdapter clickAdapter = new CoursesAdapter(players);
clickAdapter.setOnEntryClickListener(new CoursesAdapter.OnEntryClickListener() {
    @Override
    public void onEntryClick(View view, int position) {
        // stuff that will happen when a list item is clicked
    }
});
recyclerView.setAdapter(clickAdapter);
}

So I've thought I had put that interface in the wrong place (very probably), infact I've put it in the Adapter class, at the end of it, right after the onAttachedToRecyclerView() Method:

    @Override
public void onAttachedToRecyclerView(RecyclerView recyclerView) {
    super.onAttachedToRecyclerView(recyclerView);
}


private OnEntryClickListener mOnEntryClickListener;

public interface OnEntryClickListener {
    void onEntryClick(View view, int position);
}

public void setOnEntryClickListener(OnEntryClickListener onEntryClickListener) {
    mOnEntryClickListener = onEntryClickListener;
}



}

Answer

Farbod Salamat-Zadeh picture Farbod Salamat-Zadeh · Jun 13, 2016

I remember when I was first reading about RecyclerViews - I agree it can be a little confusing at first. Hopefully, this explanation will help you understand it better.


RecyclerView basics

1. Adding the RecyclerView

First you need to add your RecyclerView to your XML layout. I'm assuming you know how to do this. You also declare it in your Java code:

RecyclerView recyclerView = (RecyclerView) findViewById(R.id.your_recycler_view);

2. Creating the Adapter and understanding ViewHolder

Next, you need to create an Adapter for it. This is a class that implements RecyclerView.Adapter<YourAdapter.YourViewHolder>. I will explain what this means in a minute.

I believe it helps to look at an example of an Adapter to understand how it works (e.g. one I created for an open-source app). I would also highly recommend looking through a set of Java files I have made as an example on Gist on GitHub:

https://gist.github.com/FarbodSalamat-Zadeh/7646564f48ee708c1582c013e1de4f07

I will be referencing the example files from the link above in this explanation so you can follow along.

You can see that the Adapter class contains an inner class, which is your ViewHolder. Therefore, it needs to extend RecyclerView.ViewHolder.

Inside this ViewHolder, you declare the variables for the layouts that will be used for each list item in your RecyclerView. In the constructor for your ViewHolder, you assign these variables. I'm referring to this part of the code (I'm giving my example below):

ExampleViewHolder(View itemView) {
    super(itemView);
    text1 = (TextView) itemView.findViewById(R.id.text1);
    text2 = (TextView) itemView.findViewById(R.id.text2);
}

That's all you need for your ViewHolder (the inner class in your Adapter).

3. Understanding the Adapter

Like most Java objects, you will need to have a constructor some private variables in your Adapter class. Here are mine:

private ArrayList<CustomClass> mCustomObjects;

public ExampleAdapter(ArrayList<CustomClass> arrayList) {
    mCustomObjects = arrayList;
}

You will need to have your ArrayList<CustomClass> as a constructor parameter so you can pass the list so your Adapter can use it.

If you look at the rest of the Adapter class, it contains some methods which it overrides from what it extends. Let's have a quick look at what these are:

  • getItemCount() returns the size of your list.
  • onCreateViewHolder(...) is used to inflate the layout for your list item.
  • onBindViewHolder(...) configures your layouts for the list item (e.g. setting text to a TextView)

For most cases, getItemCount() will just return the size() of your ArrayList<CustomClass>.

The onCreateViewHolder(...) method generally stays the same too:

@Override
public ExampleViewHolder onCreateViewHolder(ViewGroup parent, int viewType) {
    View view = LayoutInflater.from(parent.getContext()).inflate(android.R.layout.simple_list_item_2, parent, false);
    return new ExampleViewHolder(view);
}

You can see that I am inflating the layout that I will use as my list item (android.R.layout.simple_list_item_2). This layout is built in to Android so I don't need to create it - of course, your can use whatever layout you wish and then modify your Adapter for widgets that you may be using. The return type of this method will match whatever you named your ViewHolder inner class.

Now, the interesting bit is in onBindViewHolder(...). You configure your layouts here, so it is completely up to you what you want to do. Here's a template you could use:

@Override
public void onBindViewHolder(ExampleViewHolder holder, int position) {
    CustomClass object = mCustomObjects.get(position);

    String firstText = object.getFirstText()
    String secondText = object.getSecondText()

    holder.text1.setText(firstText);
    holder.text2.setText(secondText);
}

Basically, you access your ViewHolder variables (for the widgets in your list item layout) by doing holder.myWidget. The holder part is coming from the parameter, which is your ViewHolder we talked about earlier, and myWidget would be the name of the View variable from that.

In the example above, the object has a getFirstText() method, and the ViewHolder contains a TextView (text1), so I am setting the text.

There is also one more method - onAttachedToRecyclerView(...). You can use this for more complex things, but at a basic level, it is usually this:

@Override
public void onAttachedToRecyclerView(RecyclerView recyclerView) {
    super.onAttachedToRecyclerView(recyclerView);
}

4. Configuring RecyclerView

Remember at the beginning, when we declared and assigned our RecyclerView?:

RecyclerView recyclerView = (RecyclerView) findViewById(R.id.your_recycler_view);

Now we are going to configure it.

You start by setting a "layout manager". This determines how each list item will be displayed on screen. The common ones are LinearLayoutManager and GridLayoutManager. The former puts your list items into a standard list (nothing special really, but it is very useful), and the latter organises your list items into a grid type of layout.

In our example, we're going to use a LinearLayoutManager. To set this on the RecyclerView, we do this:

recyclerView.setLayoutManager(new LinearLayoutManager(this));

That's all.

And all we have to do next is to set the Adapter class we created and customised earlier to your RecyclerView:

ExampleAdapter adapter = new ExampleAdapter(yourCustomArrayList);
recyclerView.setAdapter(adapter);

In the above, I'm assuming your adapter only has one parameter, but this will depend on how you configured it earlier.

5. Using your RecyclerView

The steps above should give you a working RecyclerView. If you get stuck, you can look at how I added one into my app here.

You can also look through the Google samples for the RecyclerView implementation.

I hope all of this gave you a clear idea about how RecyclerView works.


Adding a click listener

You may want to add a click listener so that you are not using the RecyclerView just for displaying items.

To do this, your inner ViewHolder class needs to implement View.OnClickListener. This is because you will set an OnClickListener to the itemView parameter of the ViewHolder's constructor. Let me show you what I mean:

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

    TextView text1, text2;

    ExampleClickViewHolder(View itemView) {
        super(itemView);

        // we do this because we want to check when an item has been clicked:
        itemView.setOnClickListener(this);

        // now, like before, we assign our View variables
        title = (TextView) itemView.findViewById(R.id.text1);
        subtitle = (TextView) itemView.findViewById(R.id.text2);
    }

    @Override
    public void onClick(View v) {
        // The user may not set a click listener for list items, in which case our listener
        // will be null, so we need to check for this
        if (mOnEntryClickListener != null) {
            mOnEntryClickListener.onEntryClick(v, getLayoutPosition());
        }
    }
}

The only other things you need to add are a custom interface for your Adapter and a setter method:

private OnEntryClickListener mOnEntryClickListener;

public interface OnEntryClickListener {
    void onEntryClick(View view, int position);
}

public void setOnEntryClickListener(OnEntryClickListener onEntryClickListener) {
    mOnEntryClickListener = onEntryClickListener;
}

So your new, click-supporting Adapter is complete.

Now, let's use it...

ExampleClickAdapter clickAdapter = new ExampleClickAdapter(yourObjects);
clickAdapter.setOnEntryClickListener(new ExampleClickAdapter.OnEntryClickListener() {
    @Override
    public void onEntryClick(View view, int position) {
        // stuff that will happen when a list item is clicked
    }
});
recyclerView.setAdapter(clickAdapter);

It's basically how you would set up a normal Adapter, except that you use your setter method that you created to control what you will do when your user clicks a particular list item.


To reiterate, you can look through a set of examples I made on this Gist on GitHub:

https://gist.github.com/FarbodSalamat-Zadeh/7646564f48ee708c1582c013e1de4f07