【android】從源碼上分析ListView/GridView調(diào)用setEmptyView不起作用的原因及解決辦法
來源:程序員人生 發(fā)布時間:2015-05-19 07:54:31 閱讀次數(shù):5275次
當我們使用ListView或GridView的時候,當列表為空的時候,我們常常需要1個Loading或1段提示文字又或1個特殊的View來提示用戶操作,這個時候就用到了setEmptyView()方法。
setEmptyView()實際上是AdapterView的方法,而我們開發(fā)中經(jīng)常使用到的ListView, GridView, ExpandableListView等都是繼承于AdapterView的,所以可以直接調(diào)用這個方法。
但是問題來了,當你這個emptyview不在當前的View
hierarchy上,那末你直接調(diào)用setEmptyView(emptyview)是不會有任何效果的,為何呢?請看源碼:
/**
* Sets the view to show if the adapter is empty
*/
@android.view.RemotableViewMethod
public 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是不是為空,進而調(diào)用updateEmptyStatus(empty);更新視圖,下面再看看updateEmptyStatus(empty)的實現(xiàn):
/**
* 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 visible
setVisibility(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到當前的view
hierarchy上,而當前界面只是顯示當前的view
hierarchy的,所以如果這個emptyview不在當前的View
hierarchy上,那末你直接調(diào)用setEmptyView(emptyview)是不會有任何效果的。
問題本源我們找到了,那末我們該怎樣解決呢?那就是把這個emptyview加入到當前view
hierarchy上咯,對此有兩種方法可以實現(xiàn):
1. Empty View和ListView在同1個布局文件里
<FrameLayout xmlns:android="http://schemas.android.com/apk/res/android"
android:layout_width="match_parent"
android:layout_height="match_parent">
<ListView
android:layout_width="match_parent"
android:layout_height="match_parent"
android:id="@+id/list_view" />
<TextView
android:id="@+id/tv_empty"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_gravity="center"
android:text="Loading data..." />
</FrameLayout>
ListView listView = (ListView)findViewById(R.id.list_view);
listView.setEmptyView(findViewById(R.id.tv_empty));
2. Empty View在單獨的布局文件里,這類1般適用于比較復雜的View或打算在多個地方復用
setEmptyView()這個方法是有限制的,這個View必須在當前的View hierarchy的節(jié)點上,所以當我們寫在外面單獨的布局文件里時,需要把View添加到當前的View hierarchy的節(jié)點上。所以就需要下面的用法:
View emptyView = View.inflate(R.layout.empty_view, null);
((ViewGroup)list.getParent()).addView(emptyView);
ListView listView = (ListView)findViewById(R.id.list_view);
listView.setEmptyView(emptyView);
有些同學可能說,不對啊,我調(diào)用的setemptyview不用要求這個View必須在當前的View
hierarchy的節(jié)點上啊,為何我的運行好好的?
答:那你1定是使用了第3方的控件,它里面重寫了setemptyview。比如著名的PullToRefresh
中的PullToRefreshAdapterViewBase的setemptyView實現(xiàn)以下:
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 Events
newEmptyView.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
// FrameLayout
FrameLayout.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添加了進去。
參考http://stormzhang.com/android/2014/05/11/adapterview-setemptyview/
生活不易,碼農(nóng)辛苦
如果您覺得本網(wǎng)站對您的學習有所幫助,可以手機掃描二維碼進行捐贈