How to fix:'notifyDataSetChanged() not updating the ImageView in RecyclerView Adapter' - android

Suppose I have a recyclerView Adapter which is used to populate views including imageView . When i first load adapter then recycler view items are getting displayed properly . But if i update imageView and textview resource in adapter datasource and call notifyDataSetChanged() method then only textview is getting updated and not imageView . Can anyone help me with this ? Thanks in advance .
For Example - This is the first time i add all the elements in an empty arraylist inside fragment and update using notifyDataSetChanged() method . Till now everything is fine and images are loading correctly in adapter .
Below is the getMethod()
if(remindersListResponse.code == 200)
{
if(remindersListResponse.data != null)
{
photoContactList.clear();
photoContactList.addAll(remindersListResponse.data);
photoContactsAdapter.notifyDataSetChanged();
}
}
Now if i call getMethod again on click of some button in fragment and use notifyDataSetChanged() method . Then only text gets updated in adapter items but not images instead they get disappear .
This is my adapter onBindViewHolder(...) method
#Override
public void onBindViewHolder(#NonNull final ViewHolder viewHolder, int i) {
final PhotoContactsResponse.Result photoContactDetail = photoContactList.get(i);
viewHolder.photoContactName.setText(photoContactDetail.name);
if(photoContactDetail.image!=null && !photoContactDetail.image.isEmpty()) {
viewHolder.addPhotoText.setVisibility(View.GONE);
Picasso.with(context)
.load(Constants.WEB_SERVICE_BASE_URL_FOR_IMAGE+photoContactDetail.image)
.networkPolicy(NetworkPolicy.NO_CACHE)
.memoryPolicy(MemoryPolicy.NO_CACHE)
.into(viewHolder.photoContactImage);
}
else
{
viewHolder.addPhotoText.setVisibility(View.VISIBLE);
viewHolder.photoContactImage.setVisibility(View.GONE);
}
}
This is my model class
public static class Result{
public int id;
public String name;
public String number;
public String image;
public int index;
}

I was running into this same issue and fixed it by setting the recycle view's adapter to null, then to my adapter again. See: Force RecyclerView to redraw its items

I think you forgot to set ImageView as VISIBLE. When you hide it in else statement you should always bring it back when image is available. onBindViewHolder only binds data to view. Recycler views are reused between calls to onBindViewHolder method.
#Override
public void onBindViewHolder(#NonNull final ViewHolder viewHolder, int i) {
final PhotoContactsResponse.Result photoContactDetail = photoContactList.get(i);
viewHolder.photoContactName.setText(photoContactDetail.name);
if(photoContactDetail.image!=null && !photoContactDetail.image.isEmpty()) {
viewHolder.addPhotoText.setVisibility(View.GONE);
viewHolder.photoContactImage.setVisibility(View.VISIBLE); // <== ADD THIS LINE
Picasso.with(context)
.load(Constants.WEB_SERVICE_BASE_URL_FOR_IMAGE+photoContactDetail.image)
.networkPolicy(NetworkPolicy.NO_CACHE)
.memoryPolicy(MemoryPolicy.NO_CACHE)
.into(viewHolder.photoContactImage);
}
else
{
viewHolder.addPhotoText.setVisibility(View.VISIBLE);
viewHolder.photoContactImage.setVisibility(View.GONE);
}
}

Related

How to display Message on latest item in Recyclerview?

I want display badge in latest item on recyclerview and after clicking on it, its visibility is gone. Please help me
My code is here:
public void onBindViewHolder(ViewHolder Viewholder, int position) {
SubCategory_Model_List dataAdapterOBJ = dataAdapters.get(position);
if(position==dataAdapters.size()-1){
// here goes some code
// callback.sendMessage(Message);
Viewholder.triangleLabelView.setVisibility(View.VISIBLE);
}
Picasso.with(context)
.load(dataAdapterOBJ.getImageUrl())
.placeholder(R.drawable.default_placeholder)
.into(Viewholder.imageView);
Viewholder.ImageTitleTextView.setText(dataAdapterOBJ.getImageTitle());
}
The logic written by you is fine it should work all you need to do is to add else block and write like
else {
Viewholder.triangleLabelView.setVisibility(View.GONE);
}
and just add an onclicklistener in the
viewholder.item.setOnclickListener(new View.OnClickListener{
add your visibility code over here.
please manage your logic.
})
Suppose you have a MessageModel and in this class you have some property like:
class MessageModel{
String body;
int itemId;
boolean isNew;
}
You have messageList in your adapter. So when you get new message before adding in your adapterList you have to update your new messageModel
Think in your activity/fragment class you get new message Like:
#ovveride
onGetNewMessage(MessageModel message){
message.setNew(true);
mAdapter.addItem(message);
notifyDataSetChnaged();
}
Now in your adapter class onBindViewHolder section you have to update your UI like:
if(message.isNew()){
// view will be visible
}else{
// view will be gone
}
Now Last things is to update message as old. So when you click your message you still have messageModel reference in your adapter class. add below things in your onCLick method.
message.setNew(false);
notifyItemChanged(position);

Update TextView in Fragment when RecyclerView's data changes

In my Fragment, I have 2 views. A Textview and a RecyclerView. The Textview essentially displays the current size of the RecyclerView. So, when a row is removed in the adapter class, I need to update the TextView's value accordingly.
The row is removed successfully when removeBtn is clicked, but I need to update the TextView in the Fragment accordingly.
FRAGMENT
titleText.text = "SIZE (" + arrayStringList().size.toString() + ")"
//...
//...
//Set RecyclerView Adapter
mRecyclerView.layoutManager = androidx.recyclerview.widget.LinearLayoutManager(context)
val adapter = MRecyclerView(context!!, arrayStringList)
mRecyclerView.adapter = adapter
RECYCLERVIEW
holder.removeBtn{
mData.removeAt(position)
notifyItemRemoved(position)
}
Is there some sort of listener that I can put in my fragment to detect when the data changes? Or is there a way to send data from the recyclerview back to the fragment when removeBtn is clicked?
Create a interface in recyclerView class , and implement that interface in fragment . Once you click the remove button , call this interface . In Fragment class , update the text view with adapter.getItemCount.
In Adapter
interface ItemCallback{
void updateTextView();
}
This interface will be implemented in fragment class ,where you can update your textView with itemCount .
public class Fragment implements Adapter.ItemCallback{
#Override
public void updateTextView() {
tvTextView.setText(adapter.getItemCount()); // This will return the current item count of adapter
}
}
You can do this via call back. Make an interface and implement that on Fragment. And pass the reference of the interface to the adapter. When you trigger the event to delete item from recycler view call the interface method (That you implemented on Activity). And in that call back method, you need to set the value to your text view. To set value on TextView, you can call size() method and set the value whatever is returned from it. For example the List you are passing to the adapter is mData. When you get call back the call size method on the reference and set the value as mentioned below code.
titleText.setText(String.valueOf(mData.size())).
Hope this will help you.
NOTE: You need to call String.valueOf method because size method returns integer value so it needs to be cast.
"Or is there a way to send data from the recyclerview back to the fragment when removeBtn is clicked?" the answer is yes
One approach is to pass your adapter your own click listener (an interface), this way you can control the click from the fragment something like this,
A1) create an interface
public interface ItemTouchListener {
void onCardClick(View view, int position);
boolean onCardLongClick(View view, int position);
}
A2) implement this interface in your fragment
public class YourFragment extends Fragment implements ItemTouchListener
A3) implement the methods you created in your fragment
#Override
public void onCardClick(View view, int position) {
if (view.getId() == R.id.removeBtn){
//update your text
}else{
//other buttons
}
}
#Override
public boolean onCardLongClick(View view, int position) {
if (view.getId() == R.id.removeBtn){
//update your text
} else {
//other buttons
}
return true;
}
A4) create this interface in your adapter and set it to your button
private ItemTouchListener onItemTouchListener;
public YourAdapter(List<Object> list, ItemTouchListener onItemTouchListener) {
this.onItemTouchListener = onItemTouchListener;
this.list = list;
}
removeBtn = view.findViewById(R.id.removeBtn);
removeBtn.setOnClickListener(new View.OnClickListener() {
#Override
public void onClick(View v) {
onItemTouchListener.onCardClick(v, getAdapterPosition());
}
});
removeBtn.setOnLongClickListener(new View.OnLongClickListener() {
#Override
public boolean onLongClick(View view) {
onItemTouchListener.onCardLongClick(view, getAdapterPosition());
return true;
}
});
A5) and now when you create your adapter it will ask for the ItemTouchListener which you can give it by just passing 'this'
YourAdapter adapter = new YourAdapter(this);
A6) you may also want to give your adapter a method something like getCount which return the list size
public int getCount(){
list.size();
}
and then you can call this like myAdapter.getCount

RecyclerView ItemTouchHelper doesn't remove item

I'm trying to implement swipe to archive note in RecyclerView.
It was working fine but after I added these codes to refresh the RecyclerView from onResume(), Swiping although does archive the Note, but the item doesn't get removed and stays at a state you can see in image below:
This is what I do in onResume() :
#Override
protected void onResume() {
super.onResume();
notes = noteDAO.getAllNotes();
noteAdapter = new NoteAdapter(notes,this);
recyclerView.setAdapter(noteAdapter);
}
ItemTouchHelper onSwiped():
#Override
public void onSwiped(#NonNull RecyclerView.ViewHolder viewHolder, int direction) {
int position = viewHolder.getAdapterPosition();
noteAdapter.deleteItem(position,rv);
}
deleteItem method in Adapter:
public void deleteItem(int position, RecyclerView rv) {
noteDAO = DBInjector.provideNoteDao(context);
recentlyDeletedNote = notes.get(position);
recentlyDeletedNotePosition = position;
recentlyDeletedNote.setArchive(true);
notes.remove(position);
noteDAO.archiveNote(recentlyDeletedNote);
notifyItemRemoved(position);
}
I have tried a lot of solutions, the only reason why it is not updating the view is that on updating recycler views, views are getting updated especially when ItemTouchHelper is used. As I didn't have much choice I used
recreate()
function for refreshing the whole activity and the error went off.
PS: This is not the ideal solution, it is just workaround fix.

How to access view inside activity from adapter in android?

Does anyone knows how to access adapter imageView inside activity to hide the view. Please specify any example.
I hope this will work for you.
By using SharedPreferences we can easily hide the view from activity or fragment.
Save flag in SharedPreferences i.e true from activity.
If you are using Recyclerview then in onBindViewHolder method check condition
if(flag==true){
holder.yourView.setVisibility(View.GONE);
}else{
holder.yourView.setVisibility(View.VISIBLE);
}
Go to onBindViewHolder of the adapter and take the id of your imageview and code like this
holder.mImgVw.setVisibility(View.GONE);
You should not directly interact with the ImageView, instead you can use notifyItemChanged() to update the ImageView state in the Adapter. But, you need to slightly modify your Adapter code by adding a flag in your model data or using SparseBooleanArray as a mechanism to saving the ImageView state.
Here the example:
public class Adapter ... {
private SparseBooleanArray mSelectedItems;
private List<YourModel> mItems;
public Adapter(List<YourModel> items) {
mItems = items;
mSelectedItems = new SparseBooleanArray();
}
...
public void onBindViewHolder(....) {
int itemPosition = viewHolder.getAdapterPosition();
YourModel item = items.get(itemPosition);
boolean visible = mSelectedItems.get(itemPosition);
viewHolder.imageView.setVisibility(visible? View.VISIBLE: View.GONE);
...
}
public void setItemVisibilityByPosition(int position, boolean visible) {
mSelectedItems.put(position, visible);
notifyItemChanged(position);
}
}
You can change the image visibility with:
// Assume the mAdapter is your Adapter
mAdapter.setItemVisibilityByPosition(5, true);

Picasso + RecycleView insert image in wrong view

I have a simple adapter
public class ConversationListAdapter extends
RecyclerView.Adapter<Conversation.ViewHolder> {
private List<Conversation> items;
private Activity activity;
public ConversationListAdapter(Activity activity, List<Conversation> items) {
super();
this. items = items;
this.activity = activity;
}
#Override
public void onBindViewHolder(ViewHolder viewHolder, int i) {
Conversation conversation = mItems.get(i);
viewHolder.name.setText(conversation.getName());
if ( conversation.getUrl() != null) {
Picasso.with(activity.getApplicationContext()).load(conversation.getUrl())
.into(viewHolder.imageView);
}
}
and a basic
class ViewHolder extends RecyclerView.ViewHolder implements View.OnClickListener {..}
}
Now in the fragment as always:
mRecyclerView.setAdapter(new ConversationAdapter(getActivity(), mItems));
Now Im calling my rest api to get the data and the first time it works great everything is where it should be (let's say in c there is only 2 items and the order is conv1 and conv2)
private void handleResult(List<Conversation> c) {
items.clear()
items.addAll(c)
adapter.notifyDataSetChanged()
}
But... now if I refresh for example and the data in the List comes in a different order (conv2 and then conv1) after the adapter.notifyDataSetChanged() both of my imageView in the list have the same pictures.. ! But the textView however has the right text
This only happens with view filled with Picasso and cannot understand why
Could someone help me on this ?
you use if condition in any adapter you also have to set else part of it. i also don't know exactly why this is happen that if condition is given it takes same condition for child which are not match this, may be a bug in android. please try else part of it also. may be this work for you.
You have to replace your items in your adapter or create a new adapter with the new items
1st solution:
private void handleResult(List<Conversation> c) {
mRecyclerView.setAdapter(new ConversationAdapter(getActivity(), c));
}
2nd solution:
private void handleResult(List<Conversation> c) {
adapter.setList(c);
adapter.notifyDataSetChanged();
}
And don't to forget to create setList(List<Conversation> c) method in your Adapter

Categories

Resources