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;
}
}
I remember when I was first reading about RecyclerView
s - I agree it can be a little confusing at first. Hopefully, this explanation will help you understand it better.
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);
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
).
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);
}
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.
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.
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