RecyclerView.Adapter 中是否有编写观察者来了解数据集是否已更改?

问题描述 投票:0回答:5

我已经用它的

RecyclerView
实现了我的
Custom Adapter
,如下

全球声明如下

private LinearLayoutManager linearLayoutManager;
private int pastVisibleItems, visibleItemCount, totalItemCount;
private CustomRecyclerViewAdapter customRecyclerViewAdapter;

首先,我在

onCreate()
方法中创建了适配器实例,该方法内部有空
Array
并将其设置为
recyclerView

linearLayoutManager = new LinearLayoutManager(getActivity());
recyclerView.setLayoutManager(linearLayoutManager);
DividerItemDecoration dividerItemDecoration = new DividerItemDecoration(
    Utility.ItemDecorationConst);
recyclerView.addItemDecoration(dividerItemDecoration);
customRecyclerViewAdapter = new CustomRecyclerViewAdapter(getActivity());

recyclerView.setAdapter(customRecyclerViewAdapter);
recyclerView.addOnScrollListener(new RecyclerView.OnScrollListener() {
@Override
public void onScrolled(RecyclerView recyclerView, int dx, int dy) {

    visibleItemCount = linearLayoutManager.getChildCount();
    totalItemCount = linearLayoutManager.getItemCount();
    pastVisibleItems = linearLayoutManager.findFirstVisibleItemPosition();
    if (loading) {
        if ((visibleItemCount + pastVisibleItems) >= totalItemCount) {
            loading = false;
            customRecyclerViewAdapter.addProgressBarEntry();
            controller.getNextPage(PublisherAppContainerFragment.this);
        }
    }
}
});

渲染完整视图后,当我从

AsyncTask
获取数据以填充
recyclerView

我调用

Adapter
的以下方法来填充数据

customRecyclerViewAdapter.addAll(myArray);

注意:addAll() 不是任何重写方法

以下是我的代码

CustomRecyclerViewAdapter

class CustomRecyclerViewAdapter extends RecyclerView.Adapter<CustomRecyclerViewAdapter.ViewHolder> {
    ArrayList<MyModel> arrayList = new ArrayList<>();
    Context context;

    public CustomRecyclerViewAdapter(Context context) {
        this.context = context;
    }

    @Override
    public ViewHolder onCreateViewHolder(ViewGroup parent, int viewType) {
        ViewHolder viewHolder = null;
        //inflated some view
        return viewHolder;
    }

    @Override
    public void onBindViewHolder(ViewHolder holder, int position) {
        //binded data to holder
    }

    @Override
    public int getItemCount() {
        return arrayList.size();
    }

    public void addAll(ArrayList myArray) {
        this.arrayList.addAll(myArray)
    }

    public void clear() {
        arrayList.clear();
    }
    public class ViewHolder extends RecyclerView.ViewHolder implements View.OnClickListener {
        public CardView cardView;

        public ViewHolder(View view) {
        super(view);
            this.cardView = (CardView) view.findViewById(R.id.card_view);
            this.cardView.setOnClickListener(this);
        }

        @Override
        public void onClick(View view) {
        //handle operations
        }
    }
}

因此,每当我从

AsynTask
获取数据时,我都会调用方法
addAll()
,而
recyclerView
就像魅力一样。

现在,我的问题是它如何运作得很好,即使我从未在

notifyDataSetChanged()
上拨打过
adapter
。该适配器是否有任何先前注册的观察者?谁观察
public int getItemCount()
中返回的数据集是否发生了变化?

正如我读到的那样

documentation

无效notifyDataSetChanged()

通知所有已注册的观察者数据集已更改。

这意味着即使有一些

observers
已注册,您也需要使用
notify
notifyDataSetChanged()
它们。对吗?

我也打过电话

boolean flag = customRecyclerViewAdapter.hasObservers();

了解是否有观察员注册?

Flag
True

那么有人可以帮助我了解这些东西到底是如何工作的吗?

android android-recyclerview observers notifydatasetchanged
5个回答
5
投票

如果你查看 RecyclerView setAdapter 调用的源代码,你会发现一个方法

setAdapterInternal(adapter, false, true);
负责

用新适配器替换当前适配器并触发侦听器。

此方法负责将旧适配器替换为新适配器,并且在内部它还注册自定义的 Data Observer。这就是您将标志设置为 true

的原因

2
投票

根据我对您的代码的了解,我想说您的

RecyclerView
上没有任何观察者正在获取更改并保持列表更新。更有可能的是,您只是“幸运”,因为当您滚动列表时,布局管理器不断调用适配器上的
getItemCount()
以确定是否应该显示更多项目。每当您调用
addAll()
时,您都会默默地更新项目计数,而观察者恰好会收到有关更改的通知。

这绝对是一个错误,如果您依赖特定观察者来监视列表的某些方面,或者做的不仅仅是将新项目附加到底部(例如更改或插入现有项目之间)。正如您所指出的,正确的实现是在列表更新时调用

notifyDataSetChanged()
,或者如果可以的话,最好更具体地说明更改的内容。例如,您可以使用:

public void addAll(ArrayList myArray) {
    int positionStart = getItemCount() - 1;
    this.arrayList.addAll(myArray);
    notifyItemRangeInserted(positionStart, myArray.size());
}

public void clear() {
    int oldSize = getItemCount();
    arrayList.clear();
    notifyItemRangeRemoved(0, oldSize);
}

2
投票

我的问题是它如何工作得很好,即使我从未在适配器上调用过notifyDataSetChanged()

这是因为addAll方法默认调用的是

notifyDataSetChanged()

public void addAll(T ... items) {
        synchronized (mLock) {
            if (mOriginalValues != null) {
                Collections.addAll(mOriginalValues, items);
            } else {
                Collections.addAll(mObjects, items);
            }
        }
        if (mNotifyOnChange) notifyDataSetChanged();
    }

还有

public void addAll(@NonNull Collection<? extends T> collection) {
        synchronized (mLock) {
            if (mOriginalValues != null) {
                mOriginalValues.addAll(collection);
            } else {
                mObjects.addAll(collection);
            }
        }
        if (mNotifyOnChange) notifyDataSetChanged();
    }

这是链接 - https://github.com/android/platform_frameworks_base/blob/master/core/java/android/widget/ArrayAdapter.java

编辑 - 我看到你有自己的 addAll 方法,它正在调用 ArrayList 的 addAll 方法。 这就是 addAll 方法的工作原理 -

 private ArrayList<String> ex1 = new ArrayList();
 private ArrayList<String> ex2 = new ArrayList();
 private ArrayList<String> ex3 = new ArrayList();


    ex1.add("one");
    ex2.add("two");
    ex3.addAll(ex1);
    ex3.addAll(ex2);

    System.out.println(ex3);

输出 - [一、二]

这就是您的情况。


1
投票

I have shown progress bar and once I fetch data I hide the progress bar and make recyclerView visible
- 如果在布局或代码中设置
RecyclerView
可见性
GONE
那么布局将不会发生,这就是为什么
Adapter.getItemsCount()
不会被调用。因此,如果您获取数据并用它填充适配器数组,然后将
RecyclerView
可见性从
GONE
更改为
VISIBLE
,它将触发更新。

如果您不致电

notifyDataSetChanged()
RecyclerView
将不知道更新。我猜你的代码中还有其他东西会触发
RecyclerView
更新。为了澄清这种行为,让我们使用一些虚拟适配器:

private class DummyViewHolder extends RecyclerView.ViewHolder {
    public DummyViewHolder (View itemView) {
        super(itemView);
    }
}

private class Adapter extends RecyclerView.Adapter<DummyViewHolder> {
    private int mDummySize = 5;
    @Override
    public DummyViewHolder onCreateViewHolder(ViewGroup parent, int viewType) {
        View view = LayoutInflater.from(parent.getContext()).inflate(R.layout.dummy_view, parent, false);
        return new DummyViewHolder(view);
    }

    @Override
    public void onBindViewHolder(DummyViewHolder holder, int position) {

    }

    void setSize(int size) { this.mDummySize = size; }

    @Override
    public int getItemCount() {
        return mDummySize;
    }
}

onCraete()

    ViewHolder v = ...
    final Adapter adapter = ..
    ...
    //postpone adapter update
    (new Handler()).postDelayed(new Runnable() {
       @Override
       public void run() {
           adapter.setSize(10);//and nothing happend only 5 items on screen
       }
    }, 5000);

0
投票
adapter.registerAdapterDataObserver(object : RecyclerView.AdapterDataObserver() {
        override fun onChanged() {
            super.onChanged()
            //your logic
        }
    })
© www.soinside.com 2019 - 2024. All rights reserved.