I want to update a view of the recyclerview when a notifyItemChanged has been called. The thing is that I don't want to refresh the entire row but only the view of the row. (to avoid the blinking effect of the row)
There is a method called notifyItemChanged(int, payload obj).
Can I use that to achieve that? If so how to do it?
Finally I found how to update only the specific view of a row in RecyclerView.
(1) Override onBindViewHolder(Recycler.ViewHolder VH, int position, List payloads) in the adapter
(2) Inside that onBindViewHolder method,
if(payloads != null && !payloads.isEmpty() && (payloads.get(0) instanceof customObject)){
// update the specific view
}else{
// I have already overridden the other onBindViewHolder(ViewHolder, int)
// The method with 3 arguments is being called before the method with 2 args.
// so calling super will call that method with 2 arguments.
super.onBindViewHolder(holder,position,payloads);
}
(3) So to notify data change and update the view, need to call the notifyItemChanged(int position, Object payload). Need to pass the customObject(model which holds the data) as the payload object.
adapter.notifyItemChanged(i, obj);
Note : Need to disable the RecyclerView's ItemAnimator(By default it is enabled). Otherwise payload object will be empty.
recyclerView.setItemAnimator(null);
or
((SimpleItemAnimator)recyclerView.getItemAnimator()).setSupportsChangeAnimations(false);
UPDATE: I used this method recently without disabling the ItemAnimator. So no need to disable ItemAnimator. - (recyclerview-v7:25.4.0)
for further reference
You can use this to get the view of updated item:
View v = recyclerView.getLayoutManager().findViewByPosition(position);
if (v != null){
//update your view
}
Let's say you have an ArrayList of items, and it is fed to the RecyclerView adapter. Let it be something like this.
List<String> items = new ArrayList<>();
RecyclerAdapter adapter = new RecyclerAdapter(items);
recyclerView.setAdapter(adapter);
Now as I can see you want to insert items into the RecyclerView and call notifyItemChanged(int position) method to add only the single item.
For this let's say you have changed the value of item in position 0. That's the first item in the list.
Now the proper usage of the method notifyItemChanged(int position) will be
adapter.notifyItemChanged(0);
This means, the adapter will update the recyclerview with the new value in 0 position.
Related
I need to refresh / rebind ListView or RecyclerView contents without refreshing the header item itself.
Any tips on how to achieve this?
Thanks.
Yes, you can do this. Generally, your header has 0 position in the list, so the header places on the top of your list. So, for your lists, e.g. RecyclerView you must initialize the adapter (in case of RecyclerView you must create accessor of RecyclerView.Adapter class) and this adapter has a lot of methods for updating data in adapter (notifyDataSetChanged(), notifyItemInserted() etc.) and you can use one of this methods, depends on your purpose. So, in your case you can use notifyItemRangeChanged(int positionStart, int itemCount).
You can find more information about these methods in the official documentation
Assuming the header is at position 0:
Lets say you want to bind the header once and then stop binding it after refresh:
When you call notifyDataSetChanged() to reload, the onBindViewHolder() method in the adapter gets called again to refresh the data, keeping that in mind. You can set a boolean so that you bind your header once. So that eventhough the onBindViewHolder() is called multiple times the header would bind once.
class Adapter extends ...........{
//use a boolean as a flag
private boolean bindHeader = true;
........
.......
.......
#Override
public void onBindViewHolder(RecyclerView.ViewHolder holder, int position) {
..........
if(position == 0 && bindHeader){
//bind the header only for the first time
......
......
//then stop binding after refresh
bindHeader = false;
}else if(position > 0){
//bind the reset of the items here
}
}
..............
..............
}
In my app I am using recycler view.I want to show and hide view on particular condition.But when I scroll recycler views I am not getting expected behaviour.When I Visible a view it gets visible for other rows as well randomly.
What I understand is when it recycles it reuses view and when previous view when it gets recycled it finds the visibility of that view.How can I hide view on particular condition? here is my adapter code
#Override
public void onBindViewHolder(UrduRhymesViewHolder holder, int position) {
RhymesModel current = mUrduRhymesList.get(position);
AppUtility.setCustomFont(mContext, holder.tvUrduRhymesName, Constants.HANDLEE_REGULAR);
holder.tvUrduRhymesName.setText(current.getRhymeName());
holder.ivUrduRhymesLogo.setImageUrl(current.getThumbnailUrl(), mImageRequest);
int status = AppUtility.getFavouriteStatus(mContext, current.getRhymeName(), new UrduRhymesDb(mContext));
if (status == 0)
holder.btnFavourite.setBackgroundResource(R.mipmap.btn_star_unactive);
else
holder.btnFavourite.setBackgroundResource(R.mipmap.btn_star);
ProgressbarDetails progressbarDetails = ProgressbarDetails.getProgressDetail(current.getRhymeName());
if (progressbarDetails == null) {
progressbarDetails = new ProgressbarDetails();
progressbarDetails.prgProgressBar = holder.pbRhymeDownload;
progressbarDetails.download_btn_settings = holder.downloadButtonLayout;
} else {
progressbarDetails.prgProgressBar = holder.pbRhymeDownload;
progressbarDetails.download_btn_settings = holder.downloadButtonLayout;
holder.pbRhymeDownload.setProgress(progressbarDetails.progress);
}
ProgressbarDetails.addUpdateProgressDetail(current.getRhymeName(), progressbarDetails);
if (progressbarDetails != null && progressbarDetails.isDownloading) {
Log.e("test","downloading foe position "+position );
holder.downloadButtonLayout.setBackgroundResource(R.mipmap.btn_download);
holder.pbRhymeDownload.setVisibility(View.VISIBLE);
holder.pbRhymeDownload.setProgress(progressbarDetails.progress);
} else {
Log.e("test","should not be visible for position "+position);
holder.pbRhymeDownload.setVisibility(View.GONE);
}
}
Here progressbarDetails.isDownloading (having value true) is the criteria when I want to show my view but is else clause it is not hiding my view
Edit: Here ProgressbarDetails (Singleton )is a class keeping reference of every row of adapter's progress bar.
No direct way of hiding and unhiding recylerview childitems.
Solution:
Let us assume that the recyclerview adapter is ArrayList
Now make another arraylist (temp_list)
Scenarios:
Hide: iterate through your adapter items and remove the ones that you want to hide. Put each of these into temp_list. After iteration is over, call notifyDataSetChanged()
Show: iterate through your temp_list items and remove the ones that you want to show. Put each of these into adapter. After iteration is over, call notifyDataSetChanged()
You should add a flag in your viewHolder that indicates if this view should be displayed or not . and check this flag every time in the onBindViewHolder.
because the recyclerView reuses the same views you should make a decision depending on something special for every view in you viewHolder.
do u mean when your data has been changed?
and your layout want to change ?
adapter.notifyDataSetChanged();
I am getting data from Database and displaying using a custom list adapter in a ListView. I need to display only even position items in ListView.
i am able to solve this in two ways.
1. sorting data before attaching to adapter, but i want to do those task in getView() method of adapter/by using other available methods in adapter.
2.
#Override
public View getView(int position, View convertView, ViewGroup parent)
{
if( position%2 == 0 ) {
// display
} else {
// not display
}
return view;
in this i am getting alternate view's are empty view's.. i want to avoid these empty view.
i ant to do those all calculations in getView() method, without empty view in the ListView. How i can do this?
I will suggest you to create new List with filtered data and use it in your adapter. Still if you want an alternate solution you can try below code:
#Override
public int getCount() {
int halfCountOfList = itemList.size()/2;
// Add +1 in halfCountOfList if itemList size is odd.
int finalCount = halfCountOfList + itemList.size()%2;
return finalCount;
}
#Override
public View getView(int position, View convertView, ViewGroup parent) {
Item item = getItem(position*2);
//No need to to check for even item
return view;
}
No, just no. This is a really bad idea.
Don't do this. It makes no sense. Think about it: getView() is called because a View for an item is required. It is too late to make any decision about whether this item needs to be displayed in the ListView or not, it already has to be displayed. What you are trying to do is simply not how ListViews and Adapters are supposed to work.
Just filter the List before passing it to the Adapter, you can modify the List while it is being displayed by exposing it through a getter method on the Adapter and then immediately calling notifyDataSetChanged(). Any other solution which deviates from this or tries to make Adapters work like you expect it would just be a brittle hack and should never be used.
PS: Use a RecyclerView instead of a ListView. It's much better.
If you give a ListView adapter a set of data to display, it's going to try to call getView() on all of the data when it's needed - it doesn't discriminate. The decisions on how to display it are made in the getView() function.
The two are options are to either modify the data set, or only provide full-fledged views to your even index-ed values. Unfortunately, there aren't other ways around this.
Make a another list which contains only even position items in it and pass it on adapter. adapter will call getView method for all items present in list.
make another function in adapter
public int getItemViewType(int position) {
// return a value between 0 and (getViewTypeCount - 1)
return position % 2;
}
After that in GetView() method
int viewType = getItemViewType(position);
switch (viewType) {
case 0:
layoutResource = ;//Even Item
break;
case 1:
layoutResource = ;//Odd Item
break;
}
IF Your data will be of some objectType and the database will return
ArrayList<Object>,
then you can do this
Eg:-
ListView lv; // assign this view to some ListView
final List<Object> listResult; //sorted list returned by database
int len = listResult.size();
for (int i = 1; i <= len; i++) {
if (i % 2 == 0){
Object x = listResult.get(i);
//extract the data you need from the object returned
lv.add(x);
}
}
I am using a RecyclerView, I add items before the first item, the scroll position moves up to the newly first item added. How can I maintain my scroll position after adding new items at its first index and call notifydatasetchange() ?
this is what i do in my adapter
mCurrentFragment.items.addAll(0, createLineItems(dataArrayList));
notifyDataSetChanged();
Any suggestions ?
There are two options:
Use the more sophisticated versions of notify
List newData = createLineItems(dataArrayList);
mCurrentFragment.items.addAll(0, newData);
notifyItemRangeInserted(0, newData.size());
Use stable IDs. On your adapter override public long getItemId (int position) to make it return MEANINGFUL values and call setHasStableIds(true); on it.
I want to filter/hide specific Items in the RecycleView if the item content matches with an preference set in SharedPreferences.
I guess I have to somehow prevent from these specific items from getting inflated in the adapter but I have no idea how.
Any ideas?
Cheers!
An adapter is the Model part of the Model-View-Controller design pattern for using ListView, GridView, and RecyclerView. So you have to think of it this way: The adapter, at any moment, has to reflect what you want to have displayed in the RecyclerView.
So here's an example: Let's say you have four items, and you want to filter the third item because it matches your preference. Your adapter's getCount() method must return 3. For getView(), position == 0 must return the first item view, position == 1 must return your second item view, and position == 2 must return your fourth item view.
It's up to your adapter code to figure out all calculations and offsets to make sure that it is always presenting a consistent state to the view. So for example, let's say you have a String array with the items, and an index dontshow pointing to the array item that shouldn't be displayed. You need to do something like this for getView():
int index = position; // position is input parameter
if (index >= dontshow) {
index++; // skip over the don't-show item
}
String item = items[index];
// now construct your view from this item
And
#Override
public int getCount() {
return items.length - 1;
}
Then when you make changes to your model, calling notifyDataSetChanged() tells the RecyclerView it has to call getCount() and getView() on your adapter all over again to redisplay the changed data.