使用Adapter
可能需要在一个item
里包含ListView
控件动态展示多个TextView
。普通的ListView
会出现所有的TextView
只显示第一行而不是多行内容的问题。
问题的根源是ListView
没有正确计算出每个TextView
文字需要的高度,仅显示一行文字的高度。
重写的的类中,把计算高度的onItemsChanged()
通过DataSetObserver
绑定到数据对应的adapter
,且重写onMeasure(int, int)
。数据改变时回调DataSetObserver
,新数据对应的TextView
高度得到计算。
DataSetObserver
在初始化ListView
的时候已经创建完成,在setAdapter(ListAdapter adapter)
中绑定到adapter
。
经过上面的处理,令HeightListView
与ListView
用法无异。
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
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
public final class HeightListView extends ListView {
private DataSetObserver mDataSetObserver; // The custom data set observer.
public HeightListView(Context context) {
this(context, null);
}
public HeightListView(Context context, AttributeSet attrs) {
super(context, attrs);
createObserver();
}
public HeightListView(Context context, AttributeSet attrs, int defStyleAttr) {
super(context, attrs, defStyleAttr);
createObserver();
}
@Override
protected void onMeasure(int widthMeasureSpec, int heightMeasureSpec) {
int spec = MeasureSpec.makeMeasureSpec(Integer.MAX_VALUE >> 2, MeasureSpec.AT_MOST);
super.onMeasure(widthMeasureSpec, spec);
}
@Override
public void setAdapter(ListAdapter adapter) {
if (adapter == null) throw new NullPointerException();
if (getAdapter() != null) {
getAdapter().unregisterDataSetObserver(mDataSetObserver);
}
adapter.registerDataSetObserver(mDataSetObserver);
super.setAdapter(adapter);
}
// Creates a custom data set change observer.
private void createObserver() {
if (mDataSetObserver != null) return;
mDataSetObserver = new DataSetObserver() {
@Override
public void onChanged() {
super.onChanged();
onItemsChanged();
}
};
}
// Calculates the height of all sub-items, includes divider height of each item.
private void onItemsChanged() {
int count = getAdapter().getCount();
if (count == 0) return;
int height = 0;
for (int i = 0; i < count; i++) {
View v = getAdapter().getView(i, null, this);
v.measure(0, 0);
height += v.getMeasuredHeight();
}
ViewGroup.LayoutParams params = getLayoutParams();
params.height = height + getDividerHeight() * (count - 1);
setLayoutParams(params);
}
@Override
public boolean isClickable() {
return super.isClickable();
}
@Override
public boolean isEnabled() {
return super.isEnabled();
}
}
最后还把isClickable()
和isEnabled()
显式调用父类方法:item
中ListView
展示的所有subitem
都是可以点击的。要subitem
把点击事件传递给item
处理,则需要重写或设置上述两个方法的值为false
。