Android 必知必会 - EmptyView

关键词:GridView ListView EmptyView SwipeRefreshLayout

在使用 GridView、ListView 时经常需要处理无数据的情况,给用户一些必要的提示。而 GridView 和 ListView 可以使用 setEmptyView() 方法来设置无数据时展示的 View 。

本文讲一下 EmptyView 的基本用法以及如何配合 SwipeRefreshLayout 使用。

背景知识点

FrameLayout 布局方式的特点

FrameLayout 是对其子 View 约束最少最简单的布局,所有放在 FrameLayout 里的控件,都按照层次堆叠在屏幕的左上角。后加进来的控件覆盖前面的控件。

嗯,就像喷漆,后面喷的总是覆盖在之前喷的上面。

setEmptyView() 都做了什么?

setEmptyView(View emptyView)AdapterView 的一个方法,ListView、GridView、Spinner 和 Gallery 都是 AdapterView 的子类,那么理论上来说,本文讲解的针对 ListView、GridView 设置 EmptyView 的方法对于 Spinner、Gallery 应该同样适用。

下面简单看下这个方法的源码:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
public void setEmptyView(View emptyView) {
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);
}

/**
* 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);
}
}

基本就是:

  • 数据为空
    • setVisibility(View.GONE); 隐藏自身
    • mEmptyView.setVisibility(View.VISIBLE); 显示 EmptyView
  • 数据不为空
    • mEmptyView.setVisibility(View.GONE); 隐藏 EmptyView
    • setVisibility(View.VISIBLE); 显示自身

当然,中间还有一些非空判断等。

用法

这里需要说明一点,EmptyView 只要是一个 View 即可,所以除了基本控件(TextView|Button 等)外,也可以是嵌套的布局(LinearLayout|RelativeLayout 等)。

基础用法

布局:

  • LinearLayout|FrameLayout|RelativeLayout
    • GridView|ListView (id = dataView)
    • View( id = mEmptyView ,任意 View,可嵌套)

关键代码:

1
2
3
4
5
6
7
8
9
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.xxx);
mDataView = (GridView|ListView) findViewById(R.id.dataView);
adapter = xxxx;
mDataView.setAdapter(adapter);
mDataView.setEmptyView(findViewById(R.id.mEmptyView));
}

PS:务必在 setAdapter() 之后调用 setEmptyView()

配合 SwipeRefreshLayout 使用

配合 SwipeRefreshLayout 使用和基础用法类似,唯一需要注意的是布局的嵌套关系。

布局:

  • FrameLayout
    • SwipeRefreshLayout
      • GridView|ListView (id = dataView)
    • View( id = mEmptyView ,任意 View,可嵌套)

关键代码没有变化。这里 GridView|ListViewSwipeRefreshLayout 内部,SwipeRefreshLayoutEmptyView 需要同级,且最好在 FrameLayout 内部。

总结

本文简单总结了两种最基本的用法,其他更高级更复杂嵌套的情况都可以参考这两种基本用法。

有什么建议或者问题可以随时联系我,共同探讨学习: