您的位置:首页 > 移动开发 > Android开发

为RecyclerView设置emptyView

2016-01-24 00:19 501 查看
RecyclerView不像ListView,它没有提供emptyView的支持,但我们可以自己来实现这个功能。

解决思路

通过监听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,然后把这部分工作放到里面去。具体可以看看别人的写法,也是一样的原理。点我点我
内容来自用户分享和网络整理,不保证内容的准确性,如有侵权内容,可联系管理员处理 点击这里给我发消息
标签:  android emptyview listview