我已经用它的
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
。
那么有人可以帮助我了解这些东西到底是如何工作的吗?
如果你查看 RecyclerView setAdapter 调用的源代码,你会发现一个方法
setAdapterInternal(adapter, false, true);
负责
用新适配器替换当前适配器并触发侦听器。
此方法负责将旧适配器替换为新适配器,并且在内部它还注册自定义的 Data Observer。这就是您将标志设置为 true
的原因根据我对您的代码的了解,我想说您的
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);
}
我的问题是它如何工作得很好,即使我从未在适配器上调用过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();
}
编辑 - 我看到你有自己的 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);
输出 - [一、二]
这就是您的情况。
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);
adapter.registerAdapterDataObserver(object : RecyclerView.AdapterDataObserver() {
override fun onChanged() {
super.onChanged()
//your logic
}
})