Skip to content
This repository has been archived by the owner. It is now read-only.

RecyclerView doesn't reflect move operation from an ObservableCollection #310

Open
tttyy opened this issue Oct 27, 2016 · 11 comments
Open

Comments

@tttyy
Copy link

tttyy commented Oct 27, 2016

Steps to reproduce

  1. Declare an ObservableCollection
    public ObservableCollection Collection {get;set;} = new ObservableCollection();

    // Initialize somewhere
    Collection.Add("1");
    Collection.Add("2");
    Collection.Add("3");

  2. Bind to a RecyclerView using local:MvxBind="ItemsSource Collection"

  3. Call Collection.Move(1, 0);

Expected behavior

Should visually see the second row move up.

Actual behavior

Nothing visually happens

Configuration

Version: 4.x

@kjeremy
Copy link
Contributor

kjeremy commented Oct 27, 2016

Is this with 4.3? Can you please upload a reproduction on github.

@tttyy
Copy link
Author

tttyy commented Oct 27, 2016

I can make one tonight if needed. But I kinda get the idea how it breaks.

The related code is in MvxRecyclerAdapter.cs NotifyDataSetChanged

case NotifyCollectionChangedAction.Move:
    for (int i = 0; i < e.NewItems.Count; i++)
    {
        var oldItem = e.OldItems[i];
        var newItem = e.NewItems[i];
        this.NotifyItemMoved(this.ItemsSource.GetPosition(oldItem), this.ItemsSource.GetPosition(newItem));
        this.RaiseDataSetChanged();
    }
    break;

By moving one entry, both e.NewItems and e.OldItems contains exact same entry. As a result, this.NotifyItemMoved is called with exact same numbers.

I think e.NewStartingIndex and e.OldStartingIndex should be utilized instead.

@kjeremy
Copy link
Contributor

kjeremy commented Oct 27, 2016

Hm. RaiseDataSetChanged isn't called there... are you looking at the right code?

@kjeremy
Copy link
Contributor

kjeremy commented Oct 27, 2016

It should probably be something like NotifyItemMoved(e.OldStartingIndex + i, e.NewStartingIndex + i)

@kjeremy
Copy link
Contributor

kjeremy commented Oct 27, 2016

Maybe our Replace is wrong too... according to https://msdn.microsoft.com/en-us/library/system.collections.specialized.notifycollectionchangedeventargs.olditems(v=vs.110).aspx it should be using OldItems

@tttyy
Copy link
Author

tttyy commented Oct 27, 2016

BTW I copied code from 4.2. But the main problematic part still keeps. And you get the idea.

Another thing to point out, I'm not sure if the Move operation can have multiple items in OldItems & NewItems. The new & old ranges can overlap which would make things more complicated.
But luckily ObeservableCollection seems to only allow moving one item at a time.

@kjeremy
Copy link
Contributor

kjeremy commented Oct 27, 2016

Ugh those indexes can be -1 in the case of single moves/replaces...

@kjeremy
Copy link
Contributor

kjeremy commented Oct 27, 2016

Can you create an adapter inheriting from MvxRecyclerAdapter and paste the following code into your adapter

public override void NotifyDataSetChanged(NotifyCollectionChangedEventArgs e)
        {
            try
            {
                switch (e.Action)
                {
                    case NotifyCollectionChangedAction.Add:
                        if (e.NewStartingIndex == -1)
                            goto case NotifyCollectionChangedAction.Reset;
                        NotifyItemRangeInserted(e.NewStartingIndex, e.NewItems.Count);
                        break;
                    case NotifyCollectionChangedAction.Move:
                        if (e.OldStartingIndex == -1 || e.NewStartingIndex == -1)
                            goto case NotifyCollectionChangedAction.Reset;
                        for (int i = 0; i < e.NewItems.Count; i++)
                        {
                            var oldPosition = e.OldStartingIndex + i;
                            var newPosition = e.NewStartingIndex + i;
                            NotifyItemMoved(oldPosition, newPosition);
                        }
                        break;
                    case NotifyCollectionChangedAction.Replace:
                        if (e.OldStartingIndex == -1)
                            goto case NotifyCollectionChangedAction.Reset;
                        NotifyItemRangeChanged(e.OldStartingIndex, e.OldItems.Count);
                        break;
                    case NotifyCollectionChangedAction.Remove:
                        if (e.OldStartingIndex == -1)
                            goto case NotifyCollectionChangedAction.Reset;
                        NotifyItemRangeRemoved(e.OldStartingIndex, e.OldItems.Count);
                        break;
                    case NotifyCollectionChangedAction.Reset:
                        NotifyDataSetChanged();
                        break;
                }
            }
            catch (Exception exception)
            {
                Mvx.Warning(
                    "Exception masked during Adapter RealNotifyDataSetChanged {0}. Are you trying to update your collection from a background task? See http://goo.gl/0nW0L6",
                    exception.ToLongString());
            }
        }

Then test your move and some other operations.

@tttyy
Copy link
Author

tttyy commented Oct 27, 2016

Yea I did similar workaround for move and it worked.

kjeremy referenced this issue in kjeremy/MvvmCross-AndroidSupport Oct 27, 2016
According to the MSDN documentation NewStartIndex and OldStartIndex
can be -1 for certain operations.  This is stupid.  When this happens
we need to behave as if a Reset occurred.

Fixes an issue with Replace where we need to use the old index/items.
Fixes an issue with Move where if one item was moved (the default with
ObservableCollection) OldItems and NewItems were equal so the new/old
positions were equivalent.

Fixes #310
@kjeremy
Copy link
Contributor

kjeremy commented Oct 27, 2016

Is that the fix though?

On Thu, Oct 27, 2016 at 4:52 PM, tttyy notifications@github.com wrote:

Yea I did similar workaround for move and it worked.


You are receiving this because you commented.
Reply to this email directly, view it on GitHub
#310 (comment),
or mute the thread
https://github.com/notifications/unsubscribe-auth/AEIBRM4wqX_a0qkB3KVSeY5WuUSJf-mRks5q4Q8YgaJpZM4Kio0Q
.

@tttyy
Copy link
Author

tttyy commented Oct 27, 2016

Looks good to me. Thanks!

Sign up for free to subscribe to this conversation on GitHub. Already have an account? Sign in.
Labels
None yet
Development

No branches or pull requests

2 participants