【android】从源码上分析ListView/GridView调用setEmptyView不起

当我们使用ListView或GridView的时候,当列表为空的时候,我们往往需要一个Loading或者一段提示文字又或者一个特殊的View来提示用户操作,这个时候就用到了setEmptyView()方法。

setEmptyView()其实是AdapterView的方法,而我们开发中常用到的ListView, GridView, ExpandableListView等都是继承于AdapterView的,所以可以直接调用这个方法。

但是问题来了,当你这个emptyview不在当前的View hierarchy上,那么你直接调用setEmptyView(emptyview)是不会有任何效果的,为什么呢?请看源码:

/*** Sets the view to show if the adapter is empty*/@android.view.RemotableViewMethodpublic void setEmptyView(View emptyView) {//这里把emptyView赋值到成员变量mEmptyView里mEmptyView = emptyView;// If not explicitly specified this view is important for accessibility.if (emptyView != null&& emptyView.getImportantForAccessibility() == IMPORTANT_FOR_ACCESSIBILITY_AUTO) {emptyView.setImportantForAccessibility(IMPORTANT_FOR_ACCESSIBILITY_YES);}final T adapter = getAdapter();final boolean empty = ((adapter == null) || adapter.isEmpty());updateEmptyStatus(empty);}

由上面看到,setEmptyView只是把emptyView赋值到成员变量mEmptyView里,并判断adpater是否为空,进而调用updateEmptyStatus(empty);更新视图,下面再看看updateEmptyStatus(empty)的实现:

/*** Update the status of the list based on the empty parameter. If empty is true and* we have an empty view, display it. In all the other cases, make sure that the listview* is VISIBLE and that the empty view is GONE (if it's not null).*/private void updateEmptyStatus(boolean empty) {if (isInFilterMode()) {empty = false;}if (empty) {if (mEmptyView != null) {mEmptyView.setVisibility(View.VISIBLE);setVisibility(View.GONE);} else {// If the caller just removed our empty view, make sure the list view is visiblesetVisibility(View.VISIBLE);}// We are now GONE, so pending layouts will not be dispatched.// Force one here to make sure that the state of the list matches// the state of the adapter.if (mDataChanged) {this.onLayout(false, mLeft, mTop, mRight, mBottom);}} else {if (mEmptyView != null) mEmptyView.setVisibility(View.GONE);setVisibility(View.VISIBLE);}}这里大概的逻辑就是判断如果adapter为空,则把mEmptyView的visible属性改为显示,,否则把listview显示。从这里看出来,setemptyview不会把emptyviw add到当前的viewhierarchy上,而当前界面只是显示当前的view hierarchy的,所以如果这个emptyview不在当前的View hierarchy上,那么你直接调用setEmptyView(emptyview)是不会有任何效果的。

问题根源我们找到了,那么我们该怎么解决呢?那就是把这个emptyview加入到当前view hierarchy上咯,对此有两种方法可以实现:

1. Empty View和ListView在同一个布局文件里2. Empty View在单独的布局文件里,这种一般适用于比较复杂的View或者打算在多个地方复用

setEmptyView()这个方法是有限制的,这个View必须在当前的View hierarchy的节点上,所以当我们写在外面单独的布局文件里时,需要把View添加到当前的View hierarchy的节点上。所以就需要下面的用法:

有些同学可能说,不对啊,我调用的setemptyview不用要求这个View必须在当前的View hierarchy的节点上啊,为什么我的运行好好的?

答:那你一定是使用了第三方的控件,它里面重写了setemptyview。比如著名的PullToRefresh 中的PullToRefreshAdapterViewBase的setemptyView实现如下:

public final void setEmptyView(View newEmptyView) {FrameLayout refreshableViewWrapper = getRefreshableViewWrapper();if (null != newEmptyView) {// New view needs to be clickable so that Android recognizes it as a// target for Touch EventsnewEmptyView.setClickable(true);ViewParent newEmptyViewParent = newEmptyView.getParent();if (null != newEmptyViewParent && newEmptyViewParent instanceof ViewGroup) {((ViewGroup) newEmptyViewParent).removeView(newEmptyView);}// We need to convert any LayoutParams so that it works in our// FrameLayoutFrameLayout.LayoutParams lp = convertEmptyViewLayoutParams(newEmptyView.getLayoutParams());if (null != lp) {refreshableViewWrapper.addView(newEmptyView, lp);} else {refreshableViewWrapper.addView(newEmptyView);}}if (mRefreshableView instanceof EmptyViewMethodAccessor) {((EmptyViewMethodAccessor) mRefreshableView).setEmptyViewInternal(newEmptyView);} else {mRefreshableView.setEmptyView(newEmptyView);}mEmptyView = newEmptyView;}注意refreshableViewWrapper.addView(。。)就把emptyview添加了进去。

参考

如果可以,我真想和你一直旅行。

【android】从源码上分析ListView/GridView调用setEmptyView不起

相关文章:

你感兴趣的文章:

标签云: