question

njsokalski avatar image
0 Votes"
njsokalski asked ·

Calling NotifyAll to Update RecyclerView from External Method

I have a RecyclerView (with an Adapter, ViewHolder, etc.), and I need to update the data (the IEnumerable passed to the Adapter). In order for this to be updated in the RecyclerView, I need to call NotifyAll(). However, this gives me the following message:

Java.Lang.IllegalMonitorStateException: 'object not locked by thread before notify()'

I have seen stuff about Java requiring synchronized locks & other stuff for this. I am not familiar with Java, and I am using C# for this Xamarin.Android app. This is my first Xamarin.Android app, so I am new to RecyclerView and Android. Can anybody help my figure out how to update my RecyclerView from an external method? Thanks.

dotnet-android
10 |1000 characters needed characters left characters exceeded

Up to 10 attachments (including images) can be used with a maximum of 3.0 MiB each and 30.0 MiB total.

1 Answer

JessieZhang-2116 avatar image
0 Votes"
JessieZhang-2116 answered ·

Hello,


Welcome to our Microsoft Q&A platform!

You can add a function in the Adapter of your RecyclerView (e.g. UpDateData) and call NotifyDataSetChanged in your Adapter .

 public void UpDateData(List<Photo> addedPhotos) {
  
 mPhotoAlbum.AddRange(addedPhotos);
 NotifyDataSetChanged();
  }

You can refer to the full code of amy demo:
1.my adapter PhotoAlbumAdapter:

 public class PhotoAlbumAdapter: RecyclerView.Adapter
 {
     public event EventHandler<int> ItemClick;
     public static List<Photo> mPhotoAlbum = new List<Photo>();
    
     public static RecyclerView.Adapter adapter;
    
     public PhotoAlbumAdapter(List<Photo> branchesList)
     {
         adapter = this;
         mPhotoAlbum = branchesList;
     }
   //  add function `UpDateData` here and call `NotifyDataSetChanged`
     public void UpDateData(List<Photo> addedPhotos) {
    
         mPhotoAlbum.AddRange(addedPhotos);
         NotifyDataSetChanged();
     }
    
     public override RecyclerView.ViewHolder OnCreateViewHolder(ViewGroup parent, int viewType)
     {
         View itemView = LayoutInflater.From(parent.Context).Inflate(Resource.Layout.PhotoCardView, parent, false);
    
         PhotoViewHolder vh = new PhotoViewHolder(itemView, OnClick);
           
         return vh;
     }
    
     public override void OnBindViewHolder(RecyclerView.ViewHolder holder, int position)
     {
         PhotoViewHolder vh = holder as PhotoViewHolder;
         //***********************
         Photo item=   mPhotoAlbum[position];
    
         vh.Caption.Text = item.Caption;
    
         vh.MyCheckBox.SetOnCheckedChangeListener(null);
         vh.MyCheckBox.SetOnCheckedChangeListener(new MyListener(item));
         vh.MyCheckBox.Checked = item.isChecked;
    
         vh.DeleteButton.SetOnClickListener(new MyRemoveItem(item,position));
    
     }
    
    
     class MyRemoveItem : Java.Lang.Object, View.IOnClickListener
     {
         Photo photo;
         int position;
         public MyRemoveItem(Photo item,int position) {
             this.photo = item;
             this.position = position;
         }
    
         public void OnClick(View v)
         {
             mPhotoAlbum.Remove(photo);
    
             adapter.NotifyDataSetChanged();
         }
     }
    
    
    
     class MyListener : Java.Lang.Object, CompoundButton.IOnCheckedChangeListener
     {
         Photo photo;
    
         public MyListener( Photo item)
         {
             this.photo = item;
         }
    
         public void OnCheckedChanged(CompoundButton buttonView, bool isChecked)
         {
             photo.isChecked = isChecked;
         }
     }
    
     public override int ItemCount
     {
         get { return mPhotoAlbum.Count; }
     }
    
     // Raise an event when the item-click takes place:
     void OnClick(int position)
     {
         if (ItemClick != null)
             ItemClick(this, position);
     }
 }

2.when we click a button ,we can add some new data to the exsited list:

           refreshBtn = FindViewById<Button>(Resource.Id.refreshBtn);
         refreshBtn.Click += delegate
         {
             List<Photo> photos = new List<Photo>();
             photos.Add(new Photo{mPhotoID = Resource.Drawable.before_mobile_phones,mCaption = "Before mobile phones",isChecked=false });
             photos.Add(new Photo {mPhotoID = Resource.Drawable.eurostar,mCaption = "Eurostar Train",isChecked=false });
             mAdapter.UpDateData(photos);
          };

Update:

You can also try to put your code into the RunOnUiThread and change this.NotifyAll(); to NotifyDataSetChanged(); ,you can refer to the following code:

         refreshBtn.Click += delegate
         {
             Task.Run(() =>
             {
                 // Perform a task
                 RunOnUiThread(() =>
                 {
                    // manipulate UI controls
                    ((PhotoAlbumAdapter)this.mRecyclerView.GetAdapter()).ResetValues();
                 });
             });
         };

And in your Adapter

     public void ResetValues()
     {
         mPhotoAlbum.ForEach((td) => { td.mCaption = "updated data"; });
         //this.NotifyAll();
         NotifyDataSetChanged();
     }


Best Regards,

Jessie Zhang


If the response is helpful, please click "Accept Answer" and upvote it.


Note: Please follow the steps in our [documentation][3] to enable e-mail notifications if you want to receive the related email notification for this thread. [3]: https://docs.microsoft.com/en-us/answers/articles/67444/email-notifications.html

· 3 ·
10 |1000 characters needed characters left characters exceeded

Up to 10 attachments (including images) can be used with a maximum of 3.0 MiB each and 30.0 MiB total.

I tried adding the following to my adapter:
public void ResetValues()

 {
     this.Trackers.ForEach((td) => { td.Value = this.StartValue; });
     this.NotifyAll();
 }

And I have the following line in the Click event handler for a Button in MainActivity.cs:

 ((TrackerAdapter)this.rvTrackers.GetAdapter()).ResetValues();

I still get the same error when calling this.NotifyAll(). Am I not allowed to use GetAdapter() (if I am required to keep a local variable from the beginning instead, I can do that, but I would prefer not to)? Am I forgetting something else? Thanks.

0 Votes 0 ·

I have updated my answer, you can check the updated answer at the bottom of my answer. Hope it can help you.

0 Votes 0 ·
njsokalski avatar image njsokalski JessieZhang-2116 ·

Thanks! I don't know if or exactly how it was related to my problem, but is it possible that the fact that I was trying to use NotifyAll() instead of NotifyDataSetChanged() part of the problem? Your code works, so I'm going to stick with that right now, but why does NotifyAll() cause a problem and NotifyDataSetChanged() doesn't? Anyway, your code works for me and fixed the problem, so I think I can say Problem solved! Thanks again!

0 Votes 0 ·