RecyclerView和适配器数据更新

时间:2016-11-09 13:11:26

标签: android android-recyclerview recycler-adapter

对于了解其机制或愿意深入了解源代码的人来说,这是一个关于RecyclerView内部行为的问题。我希望通过对来源的引用来支持答案。

原始问题

(向下滚动到'换句话说'以获得更集中的问题)

我需要了解notify*行为(例如notifyItemInserted())如何排队。想象一下,我有一个由此列表备份的适配器:

ArrayList<String> list = Arrays.asList("one", "three", "four");

我想添加缺少的值zerotwo

示例1

list.add(1, "two");
// notify the view
adapter.notifyItemInserted(1);


// Seconds later, I go on with zero
list.add(0, "zero");
// notify the view
adapter.notifyItemInserted(0);

这非常简单明了,无需说明。

示例2

但是如果两个动作彼此非常接近,并且两者之间没有布局传递怎么办?

list.add(1, "two");
list.add(0, "zero”);

我现在该怎么办?

adapter.notifyItemInserted(1);
adapter.notifyItemInserted(0);

或者

adapter.notifyItemInserted(2);
adapter.notifyItemInserted(0);

?从适配器的角度来看,列表立即从one, three, four切换到zero, one, two, three, four,因此第二个选项似乎更合理。

示例3

list.add(0, “zero”);
adapter.notifyItemInserted(0);
list.add(2, “two”);
adapter.notifyItemInserted(...)

现在怎么样? 12?该列表在之后立即更新,但我确信它们之间没有布局传递。

问题

你遇到了主要问题,我想知道在这些情况下我应该怎么做。真实情况是我有多个异步任务以insert()方法结束。我可以排队他们的行动,但是:

  1. 如果已有内部队列,我肯定不想这样做,肯定是
  2. 我不知道如果两个动作之间没有布局传递会发生什么,请参阅示例3。
  3. 换句话说

    要更新回收商,必须执行4项操作:

    1. 我实际上改变了数据模型(例如,将某些东西插入到支持数组中)
    2. 我致电adapter.notify*()
    3. Recycler接到电话
    4. Recycler 执行操作(例如,在适配器上调用getItem*()onBind())并列出更改。
    5. 当没有并发性时,很容易理解这一点,并且它们按顺序发生:

      1. => 2. => 3. => 4. => (new update) 1. => 2. => 3. => 4. ...
      

      让我们看看步骤之间会发生什么。

      • 1。 2之间。:我会说开发人员有责任在更改数据后立即调用notify()。那没关系。
      • 2。 3之间。:这种情况立即发生,此处没有问题。
      • 3。 4之间。:这会立即发生!据我所知。因此,完全有可能在步骤 3 4 1 和 2 ) >之前的更新。

      我想了解在这种情况下会发生什么。 我们该怎么做? 我应该确保在插入新内容之前确实已执行上一次更新的 4 步骤吗?如果是这样的话?

4 个答案:

答案 0 :(得分:7)

我之前想过类似的问题,我决定:

  1. 如果我想直接在列表末尾插入1个以上项目 想要为所有人制作动画,我应该:

    list.add("0");
    list.add("1");
    adapter.notifyItemRangeInserted(5, 2); // Suppose there were 5 items before so "0" has index of 5 and we want to insert 2 items.
    
  2. 如果我想直接在列表末尾插入1个以上的项目,但是 想要为每个插入的项目分离动画,我应该:

    list.add("0");
    list.add("1");
    adapter.notifyItemInserted(0);
    mRecyclerView.postDelayed(new Runnable() {
        @Override
        public void run() {
            // before this happens, Be careful to call other notify* methods. Never call notifyDataSetChanged.
            adapter.notifyItemInserted(1); 
        }
    }, mRecyclerView.getItemAnimator().getAddDuration());
    
  3. 如果我想在列表的不同位置插入多个项目, 类似于2.
  4. 希望这可以提供帮助。

答案 1 :(得分:4)

因此,让我们从简介介绍到RecyclerView与通知项目一起使用。并且使用其他已保存的ViewGroup项列表(前面的ListView)

非常简单

RecyclerView具有已绘制的查看项目队列。并且不会调用notify(...)方法,也不知道您的任何更新。当您添加新项目并通知RecyclerView时,它会开始循环以逐个检查所有视图。

RecyclerView contains and drawn next objects
View view-0 (position 0), view-1 (position 1), View-2 (position 2)

// Here is changes after updating
You added Item View view-new into (position 1) and Notify
RecyclerView starts loop to check changes
RecyclerView received unmodified view-0(position-0) and left them;
RecyclerView found new item view-new(position 1)
RecyclerView removing old item view-1(position 1)
RecyclerView drawing new item view-new(position 1)

// In RecyclerView queue in position-2 was item view-2, 
// But now we replacing previous item to this position
RecyclerView found new item view-1 (new position-2)
RecyclerView removing old item view-2(position 2)
RecyclerView drawing new item view-1(position 2)

// And again same behavior 
RecyclerView found new item view-3 (new position-3)
RecyclerView drawing new item view-1(position 2)

// And after all changes new RecyclerView would be
RecyclerView contains and drawn next objects
View view-0 (position 0), view-new (position 1) view-1 (position 2), View-2 (position 3)

它只是工作通知函数的主要流程,但应该知道所有这些操作都发生在UI线程,主线程上,甚至可以从异步任务调用更新。并回答你2问题 - 你可以根据需要调用通知到RecyclerView,并确保你的行动将在正确的队列上。

RecyclerView在任何用途中都能正常工作,更复杂的问题将适用于您的适配器工作。首先,您需要同步适配器操作,例如添加删除项,以及完全拒绝索引使用。例如,对于示例3来说,它会更好

Item firstItem = new Item(0, “zero”);
list.add(firstItem);
adapter.notifyItemInserted(list.indexOf(firstItem));
//Other action...
Item nextItem = new Item(2, “two”);
list.add(nextItem);
adapter.notifyItemInserted(list.indexOf(nextItem))
//Other actions

更新|

RecyclerView.Adapter Doc相关,您可以在其中看到与notifyDataSetChanged()相同的功能。如果此RecyclerView.Adapter调用包含android.database.Observable个扩展名的子项,请参阅更多About Observable。对此Observable Holder的访问是同步的,直到RecyclerView中的View Element释放使用。

另请参阅支持库版本25.0中的RecyclerView 9934行 - 9988;

答案 2 :(得分:2)

如果在布局过程之间进行多次更新,那应该不是问题。 RecyclerView旨在处理(并优化)此案例:

  

RecyclerView在。之间引入了额外的抽象层次   RecyclerView.Adapter和RecyclerView.LayoutManager能够   在布局计算期间批量检测数据集更改。 [...]   RecyclerView中有两种与位置相关的方法:

     
      
  • 布局位置:项目在最新布局计算中的位置。这是LayoutManager的位置   透视图。
  •   
  • 适配器位置:项目在适配器中的位置。这是从Adapter的角度来看的位置。
  •   
     

除调度之间的时间外,这两个位置相同   adapter.notify *事件和计算更新的布局

在您的情况下,步骤是:

  1. 您更新数据层

  2. 您致电adapter.notify*()

  3. recyclerview记录更改(如果我正确理解代码,则在AdapterHelper.mPendingUpdates中)。此更改将反映在ViewHolder.getAdapterPosition()中,但尚未显示在ViewHolder.getLayoutPosition()中。

  4. 在某些时候,recyclerView应用记录的更改,基本上它将布局的视角与适配器的视角进行协调。看来这可能发生在布局通过之前。
  5. 1。 2。 3。序列可以发生任意次,只要 2。紧跟 1。(两者都发生在主线程上)。

    (1. => 2. => 3.) ... (1. => 2. => 3.) ... 4. 
    

答案 3 :(得分:-2)

Item firstItem = new Item(0, “zero”);

list.add(firstItem);

adapter.notifyItemInserted(list.indexOf(firstItem));

//Other action...

Item nextItem = new Item(2, “two”);

list.add(nextItem);

adapter.notifyItemInserted(list.indexOf(nextItem))