Reputation: 41759
I am trying to speed up an app as much as possible by calling findViewById()
as little as possible in the Adapter class.
I've heard of a "way" where I can create a HashMap<T>
containing View
instances and integers as values, and use it along or together with adapter's ViewHolder
inner class. As you can see, I heard very little about this and did not have chance to ask more questions about this "way".
I have been searching for the solution on the Internet, but I am either searching using the wrong keywords or I am not recognizing solutions as such.
Can anyone direct me in a right way? Maybe someone writing a small sample on how to accomplish this?
EDIT 1
I don't have any issues with my current ListView
as it's already using ViewHolder
pattern. The reason for this question was because I heard that you can actually gain more speed using HashMap
pattern than using ViewHolder
pattern. I know this is vague description, but that's why I came here to ask.
Sharing a code would not help at all, because it's just a regular adapter using ViewHolder
pattern, just like the one in the links you shared. There is no any experimenting or something that made the adapter slow.
Saying it again, the main reason to ask this was to find our if there is a pattern faster than the ViewHolder
Upvotes: 3
Views: 1271
Reputation: 3293
You should be using the ViewHolder paradigm which is most easily implemented with RecyclerView when Adapters are involved. Online sample code available here and GitHub sample cloneable via AndroidStudio directly or via Github.
The HashMap solution will work, but why incur a HashMap lookup when you can simply hold a reference to the object? Also, RecyclerView natively handles different View TYPEs so you don't have to roll your own solution when you have section header rows which look differently from data rows.
===============================================
A more detailed explanation of RecyclerView
follows based on snippets from the sample I linked to.
Have your CustomAdapter
extend RecyclerView
.
public class CustomAdapter extends RecyclerView.Adapter<CustomAdapter.ViewHolder> {
Create a Custom ViewHolder
class. It should almost always be a static class if embedded within your CustomAdapter
.
public static class ViewHolder extends RecyclerView.ViewHolder {
private final TextView textView;
public ViewHolder(View v) {
super(v);
// Define click listener for the ViewHolder's View.
v.setOnClickListener(new View.OnClickListener() {
@Override
public void onClick(View v) {
Log.d(TAG, "Element " + getPosition() + " clicked.");
}
});
textView = (TextView) v.findViewById(R.id.textView);
Notice that the ViewHolder
finds the views in the constructor since a ViewHolder
instance is tied to a View
instance. The ViewHolder
should have references to any UI elements that you plan to update in an onBind
callback. The ViewHolder
object is then associated with the View
by RecyclerView
when you include the following code in your CustomAdapter
.
// Create new views (invoked by the layout manager)
@Override
public ViewHolder onCreateViewHolder(ViewGroup viewGroup, int viewType) {
// Create a new view.
View v = LayoutInflater.from(viewGroup.getContext())
.inflate(R.layout.text_row_item, viewGroup, false);
return new ViewHolder(v);
}
Notice how the onCreateViewHolder
callback is given a viewType
parameter. The current code always returns the same type of custom ViewHolder
, but it could be providing different ViewHolder classes based on the type (this is how you support rows that support different views). The following code is then how you update your UI when the view is bound to a dataset.
// Replace the contents of a view (invoked by the layout manager)
@Override
public void onBindViewHolder(ViewHolder viewHolder, final int position) {
Log.d(TAG, "Element " + position + " set.");
// Get element from your dataset at this position and replace the contents of the view
// with that element
viewHolder.getTextView().setText(mDataSet[position]);
}
With these pieces in place RecyclerView
will create and reuse your Views
AND ViewHolders
by corresponding type reducing view hierarchy look ups and potentially making your app much less "janky".
Upvotes: 1
Reputation: 4775
The way to reduce the amount of calls to findViewById is to "recycle" Views with ViewHolder. The idea behind is to keep inflated views and reuse them when needed - and since they already have ViewHolders you don't need to make the findViewById calls.
As @nhaarman described in the last part of his answer the way to do it for listviews is in the getView to reuse the convertView if it's not null and using the referenced view in the ViewHolder to update them.
An approach how to create ViewHolder and store them in the View by setTag is described in the first link @nhaarman provided. Then in the getView function if convertView isn't null call the getTag inorder to get the ViewHolder back.
In another note, you should look into RecyclerView which is a listview that enforces the ViewHolder pattern.
Upvotes: 1