I want to set my view holder to not recycle, here's the code:
#Override
public void onBindViewHolder(final ViewHolder viewHolder, final int position) {
viewHolder.setIsRecyclable(false);
}
However, when I see the documentation here: https://developer.android.com/reference/androidx/recyclerview/widget/RecyclerView.ViewHolder#setIsRecyclable(boolean), it is written that the setIsRecycleable() should always be paired and I have no idea about this. Can anybody provide a sample code for this case? When should I call the setIsRecyclable(true) again? Thanks.
Not recycling the ViewHolder just means that specific ViewHolder will be retained and not overwritten when there is new data to bind, the problem with that is the Adapter will then need to supply another ViewHolder to make up for the one it can't reuse.
That is why you need to eventually let it recycle i.e. setIsRecycleable(true) because it kinda defeats the point of the RecyclerView if it ends up having to create new views to represent data.
A reason you might want to turn off the recycle is to avoid interruptions, maybe the ViewHolder is playing an animation, or loading a video. Once its done you could then turn on the recycle, to release the ViewHolder to make sure it can be used again.
It's better to use Listview in this case. The whole meaning of recyclerview is to recycle the view.
Else see this post
https://stackoverflow.com/a/36275862/3094367
Actually the recycle is related to viewType.
You just change every itemType is unique
like:
#Override
public int getItemType(int position){
return position
}
Related
Let take the following code:
#Override
public void onBindViewHolder(#NonNull RecyclerView.ViewHolder viewHolder, int position) {
// get the list item
MyListItemObject myListItemObject = getObject(i);
//set some values
viewHolder.prop1.setText(myListItemObject.prop1);
viewHolder.prop2.setText(myListItemObject.prop2);
//We got some setting from another object.
if(externalObject.showProp2){
viewHolder.prop2.setVisibility(View.VISIBLE);
}else{
viewHolder.prop2.setVisibility(View.GONE);
}
}
By some external settings value we decide to show or hide prop2 Now assume this value changes from true to false, how can I rerender the list. notifyDatasetChanged() will not work because it simply had not changed, only some external settings has.
Have you tried notifyDatasetChanged()? According to the documentation:
This event does not specify what about the data set has changed, forcing any observers to assume that all existing items and structure may no longer be valid. LayoutManagers will be forced to fully rebind and relayout all visible views.
You would make the change to hide/show prop2 in onBindViewHolder(). This should work for simple changes. If the external change cause a more involved layout change such as using a whole new view holder layout then a different approach may be in order.
I am using recycler view in a chat app, now as you all know in a chat room we have 2 different views.
Right view : the one that you sent.
Left view : the one that you received.
Now I managed to achieve what I want by using one layout item and inside that item I used 2 relative layouts, one layout for the right view and another for the left view.
And in order to know what layout to show I did this in onBindViewholder:
onBindViewHolder(){
if(/*I sent the message*/){
right_layout.setVisibility(view.VISIBLE);
left_layout.setVisibility(view.GONE);
}
else {
right_layout.setVisibility(view.GONE);
left_layout.setVisibility(view.VISIBLE);
}
}
I don't have any problem with using the above method. But my question is why others use that thing that is called multiple view types in which they use 2 view holders? Should I use it instead?
First: in your case, I'm not sure whether it's really necessary to use two view types. A typical RecyclerView on a mobile phone will show between seven and ten rows at a time and it will generate some more just to be able to scroll smoothly should the need arise. Compared to a gaming app, there's next to nothing to do UI-wise. But you asked why someone might want to use multiple view types, so here goes:
Right now 50% of the Views you're inflating will be set to GONE, so it's like a continous waste of CPU time and (worse on a mobile device) it's draining the battery unnecessarily. By using two view types, you avoid this (to a certain extent, I suppose that determining the view type takes less energy).
Another reason is better performance which results in a better user experience: RecyclerView was developed to efficiently deal with lists of items. By telling it which row can be reused for which list position, you are making the best use of its recycling algorithm.
From a developer point of view, I'd keep your approach as long as the difference between the two types is no more than two or three lines of code. But there are lists where the difference is more like twenty lines of code. In this case, using different view types, layout files and ViewHolders improves the readability of your code.
Yes, you should implement different a ViewHolder for each type of view. This provides a few advantages, like not having to maintain the relevant state, decluttering layouts, and generally easier to reason about once additionally functionality is included in the future.
It's also easy to implement:
#Override
public int getItemViewType(int position) {
Message message = messages.get(position);
if (message instanceof ReceivedMessage) {
return VIEWTYPE_RECEIVED;
}
if (message instanceof SentMessage) {
return VIEWTYPE_SENT;
}
return 0;
}
then:
#Override
public RecyclerView.ViewHolder onCreateViewHolder(ViewGroup parent, int viewType) {
LayoutInflater inflater = LayoutInflater.from(mContext);
switch (viewType) {
case VIEWTYPE_RECEIVED:
View receivedView = inflater.inflate(R.layout.item_received, parent, false);
ReceivedViewHolder receivedViewHolder = new ReceivedViewHolder(receivedView);
return receievedViewHolder;
case VIEWTYPE_SENT:
// ...
}
}
finally:
#Override
public void onBindViewHolder(RecyclerView.ViewHolder holder, int position) {
switch (getItemViewType(position)) {
case VIEWTYPE_RECEIEVED:
// ...
}
}
Is it possible to have a ListView that draws from multiple sources? I want to have some hard-coded items and items from a ContentProvider in the same list, and I just want to know if that's possible.
You could have both types of items implement an interface such as
public interface Item {
int TYPE_1 = 1;
int TYPE_2 = 2;
int getViewType();
View getView(LayoutInflater inflater, View convertView, ViewGroup parent);
}
Then your Adapter can be for a list of Items. Also, if you're unfamiliar with the View Holder pattern I'd recommend looking it up. A quick search revealed a pretty good looking example here
You can let them extends a common parent class, and then use it to construct the adapter. I have done this before, I hope to help you.
So, I realized that
I don't care if ContentProvider data is updated after the fragment loads and
I want to do manipulations on the data in my list that I do not want reflected in the ContentProvider.
So I'm going to dump my cursor results into an ArrayList along with my hardcoded items, and I think that should work fine.
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
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.
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".
Is it crucial for performance to have ViewHolder as static in a ViewHolder pattern?
A ViewHolder object stores each of the component views inside the tag
field of the Layout, so you can immediately access them without the
need to look them up repeatedly. First, you need to create a class to
hold your exact set of views. For example:
static class ViewHolder {
TextView text;
TextView timestamp;
ImageView icon;
ProgressBar progress;
int position;
}
It's not crucial for performance, it is about using. If ViewHolder class will not be static - you have to provide instance of parent class:
No enclosing instance of type Type is accessible.
Must qualify the allocation with an enclosing instance of type Type
(e.g. x.new A() where x is an instance of Type).
Edit: misunderstood the question -- it seems you are asking specifically about making it static. That shouldn't be crucial for performance, but the idea is every bit helps.
Final edit here: Static nested class in Java, why?
====
Orig answer below:
It's very nice to squeeze performance out of a heavy ListView (or some other type of recycled AdapterView). However the best way to tell would be to profile the performance somehow.
Also at Google IO 2010 they recommend this method:
http://www.youtube.com/watch?v=wDBM6wVEO70
Edit:
Also here's a link to traceview to profile the performance, though I'm unsure how well it works.
http://developer.android.com/tools/debugging/debugging-tracing.html
It's not mandatory to do that. But when you use to do like this, you are using the views again when your adapter view is null. You are creating a view and assigning values to the view part, and tag the whole view using static class ViewHolder. So when you come back and view is not null, then visible part will come from to get the tag. This is how you will create less object as well less work load on adapter.