为RecyclerView设置emptyView
2016-01-24 00:19
501 查看
RecyclerView不像ListView,它没有提供emptyView的支持,但我们可以自己来实现这个功能。
但我试了之后,发现只要你的
再继续跟踪下去:
看到这里,已经很明显了,这就是一个观察者模式!
RecyclerView内部定义了一个观察者:
然后当我们调用
在这里注册后,当数据变化的时候
所以我们要做的就是继承
解决思路
通过监听Adapter中数据的变化,当数据为空时让我们自定义的emptyView为可见的。方法一
看到一篇文章说可以通过多布局来实现:private static final int VIEW_TYPE_EMPTY_LIST_PLACEHOLDER = 0; private static final int VIEW_TYPE_OBJECT_VIEW = 1; private List<Object> myData; @Override public int getItemViewType(int position) { if (myData.isEmpty()) { return VIEW_TYPE_EMPTY_LIST_PLACEHOLDER; } else { return VIEW_TYPE_OBJECT_VIEW; } } @Override public RecyclerView.ViewHolder onCreateViewHolder(ViewGroup parent, int viewType) { switch(viewType) { case VIEW_TYPE_EMPTY_LIST_PLACEHOLDER: // return view holder for your placeholder break; case VIEW_TYPE_OBJECT_VIEW: // return view holder for your normal list item break; } }
但我试了之后,发现只要你的
getItemCount返回0,就不会再执行下去了,所以这种方法行不通。
方法二
回想一下,当我们的数据变化时,我们会去调用哪些方法。没错,就是notify*这一系列的方法了。看一下其中
notifyItemInserted的实现:
public final void notifyItemInserted(int position) { mObservable.notifyItemRangeInserted(position, 1); }
再继续跟踪下去:
public void notifyItemRangeInserted(int positionStart, int itemCount) { // since onItemRangeInserted() is implemented by the app, it could do anything, // including removing itself from {@link mObservers} - and that could cause problems if // an iterator is used on the ArrayList {@link mObservers}. // to avoid such problems, just march thru the list in the reverse order. for (int i = mObservers.size() - 1; i >= 0; i--) { mObservers.get(i).onItemRangeInserted(positionStart, itemCount); } }
看到这里,已经很明显了,这就是一个观察者模式!
static class AdapterDataObservable extends Observable<AdapterDataObserver> {
public boolean hasObservers() {
return !mObservers.isEmpty();
}
public void notifyChanged() {
// since onChanged() is implemented by the app, it could do anything, including
// removing itself from {@link mObservers} - and that could cause problems if
// an iterator is used on the ArrayList {@link mObservers}.
// to avoid such problems, just march thru the list in the reverse order.
for (int i = mObservers.size() - 1; i >= 0; i--) {
mObservers.get(i).onChanged();
}
}
public void notifyItemRangeChanged(int positionStart, int itemCount) {
notifyItemRangeChanged(positionStart, itemCount, null);
}
public void notifyItemRangeChanged(int positionStart, int itemCount, Object payload) {
// since onItemRangeChanged() is implemented by the app, it could do anything, including
// removing itself from {@link mObservers} - and that could cause problems if
// an iterator is used on the ArrayList {@link mObservers}.
// to avoid such problems, just march thru the list in the reverse order.
for (int i = mObservers.size() - 1; i >= 0; i--) {
mObservers.get(i).onItemRangeChanged(positionStart, itemCount, payload);
}
}
public void notifyItemRangeInserted(int positionStart, int itemCount) { // since onItemRangeInserted() is implemented by the app, it could do anything, // including removing itself from {@link mObservers} - and that could cause problems if // an iterator is used on the ArrayList {@link mObservers}. // to avoid such problems, just march thru the list in the reverse order. for (int i = mObservers.size() - 1; i >= 0; i--) { mObservers.get(i).onItemRangeInserted(positionStart, itemCount); } }
public void notifyItemRangeRemoved(int positionStart, int itemCount) {
// since onItemRangeRemo
4000
ved() is implemented by the app, it could do anything, including
// removing itself from {@link mObservers} - and that could cause problems if
// an iterator is used on the ArrayList {@link mObservers}.
// to avoid such problems, just march thru the list in the reverse order.
for (int i = mObservers.size() - 1; i >= 0; i--) {
mObservers.get(i).onItemRangeRemoved(positionStart, itemCount);
}
}
public void notifyItemMoved(int fromPosition, int toPosition) {
for (int i = mObservers.size() - 1; i >= 0; i--) {
mObservers.get(i).onItemRangeMoved(fromPosition, toPosition, 1);
}
}
}
public static abstract class AdapterDataObserver {
public void onChanged() {
// Do nothing
}
public void onItemRangeChanged(int positionStart, int itemCount) {
// do nothing
}
public void onItemRangeChanged(int positionStart, int itemCount, Object payload) {
// fallback to onItemRangeChanged(positionStart, itemCount) if app
// does not override this method.
onItemRangeChanged(positionStart, itemCount);
}
public void onItemRangeInserted(int positionStart, int itemCount) {
// do nothing
}
public void onItemRangeRemoved(int positionStart, int itemCount) {
// do nothing
}
public void onItemRangeMoved(int fromPosition, int toPosition, int itemCount) {
// do nothing
}
}
RecyclerView内部定义了一个观察者:
private class RecyclerViewDataObserver extends AdapterDataObserver { @Override public void onChanged() { assertNotInLayoutOrScroll(null); if (mAdapter.hasStableIds()) { // TODO Determine what actually changed. // This is more important to implement now since this callback will disable all // animations because we cannot rely on positions. mState.mStructureChanged = true; setDataSetChangedAfterLayout(); } else { mState.mStructureChanged = true; setDataSetChangedAfterLayout(); } if (!mAdapterHelper.hasPendingUpdates()) { requestLayout(); } } @Override public void onItemRangeChanged(int positionStart, int itemCount, Object payload) { assertNotInLayoutOrScroll(null); if (mAdapterHelper.onItemRangeChanged(positionStart, itemCount, payload)) { triggerUpdateProcessor(); } } @Override public void onItemRangeInserted(int positionStart, int itemCount) { assertNotInLayoutOrScroll(null); if (mAdapterHelper.onItemRangeInserted(positionStart, itemCount)) { triggerUpdateProcessor(); } } @Override public void onItemRangeRemoved(int positionStart, int itemCount) { assertNotInLayoutOrScroll(null); if (mAdapterHelper.onItemRangeRemoved(positionStart, itemCount)) { triggerUpdateProcessor(); } } @Override public void onItemRangeMoved(int fromPosition, int toPosition, int itemCount) { assertNotInLayoutOrScroll(null); if (mAdapterHelper.onItemRangeMoved(fromPosition, toPosition, itemCount)) { triggerUpdateProcessor(); } } void triggerUpdateProcessor() { if (mPostUpdatesOnAnimation && mHasFixedSize && mIsAttached) { ViewCompat.postOnAnimation(RecyclerView.this, mUpdateChildViewsRunnable); } else { mAdapterUpdateDuringMeasure = true; requestLayout(); } } }
然后当我们调用
setAdapter方法的时候,它会调用
setAdapterInternal方法,里面有这样的一个片段:
private void setAdapterInternal(Adapter adapter, boolean compatibleWithPrevious, boolean removeAndRecycleViews) { if (mAdapter != null) { mAdapter.unregisterAdapterDataObserver(mObserver); mAdapter.onDetachedFromRecyclerView(this); } ... ... if (adapter != null) { adapter.registerAdapterDataObserver(mObserver); adapter.onAttachedToRecyclerView(this); } }
在这里注册后,当数据变化的时候
RecyclerViewDataObserver就会收到相关的信息,从而完成相应的工作。
所以我们要做的就是继承
AdapterDataObserver,然后重写相应的方法,在里面判断数据是否为空,为空则显示emptyView,然后让我们自定义的Adapter去注册它,这样就能实现emptyView了。
方法三
其实,方法三跟方法二是一样的,如果你觉得每写一个adapter都要注册一个观察者太麻烦的话,可以自定义一个RecyclerView,然后把这部分工作放到里面去。具体可以看看别人的写法,也是一样的原理。点我点我相关文章推荐
- 使用C++实现JNI接口需要注意的事项
- Android IPC进程间通讯机制
- Android Manifest 用法
- [转载]Activity中ConfigChanges属性的用法
- Android之获取手机上的图片和视频缩略图thumbnails
- Android之使用Http协议实现文件上传功能
- Android学习笔记(二九):嵌入浏览器
- android string.xml文件中的整型和string型代替
- i-jetty环境搭配与编译
- android之定时器AlarmManager
- android wifi 无线调试
- Android Native 绘图方法
- Android java 与 javascript互访(相互调用)的方法例子
- android 代码实现控件之间的间距
- android FragmentPagerAdapter的“标准”配置
- Android"解决"onTouch和onClick的冲突问题
- android:installLocation简析
- android searchView的关闭事件
- SourceProvider.getJniDirectories