ListView是开发过程中经常打交道的控件之一,但是使用过程中需要主要很多事项。譬如,如果ListView的高度不是设置为match_parent或者高度值确定的话,那么在getView()方法你会发现,getView()方法被调用多轮而且所以的ListView item都会跑到不管是否正在屏幕上显示。此时我们所使用的ListView缓存机制感觉就是无效的。自己查了资料发现,如果ListView的高度不确定的话,其实系统是无法很快确定有多少item显示与屏幕上。只有不断的通过measure才能确定,这也导致了getview方法执行多次。所以在使用ListView过程中尽量确定其高度(同时包括其结点上的Parent View),不然对性能是很大的消耗。
知道了上面所提到的问题,我们就知道了为什么Google不建议ScrollView中嵌套ListView了,因为ScrollView的child View只能是warp_content。当然网上针对嵌套问题也给出了某些解决方案。例如:
public class MyListView extends ListView {
private boolean isOnMeasure = false;
public MyListView(Context context, AttributeSet attrs) {
super(context, attrs);
// TODO Auto-generated constructor stub
}
@Override
protected void onMeasure(int widthMeasureSpec, int heightMeasureSpec) {
// TODO Auto-generated method stub
isOnMeasure = true;
super.onMeasure(widthMeasureSpec, heightMeasureSpec);
}
@Override
protected void onLayout(boolean changed, int l, int t, int r, int b) {
// TODO Auto-generated method stub
isOnMeasure = false;
super.onLayout(changed, l, t, r, b);
}
public boolean isOnMeasure() {
return isOnMeasure;
}
}
接着只需要在你的Adapter中getView方法调用就行例如:
@Override
public View getView(int position, View convertView, ViewGroup parent) {
// TODO Auto-generated method stub
Holder holder = null;
if (convertView == null) {
holder = new Holder();
convertView = LayoutInflater.from(mContext).inflate(R.layout.listview_item, null);
..........
convertView.setTag(holder);
}
else {
holder = (Holder)convertView.getTag();
}
//一定要在完成convertView初始化之后调用
if ( ((ListView)parent).isOnMeasure()) {
return convertView;
}
......
}
这种方法,我在ListView中嵌套ListView(将ListView添加到另外ListView的headerView中)测试了下,当每个item的高度是相同的没有问题。如果不同的话就会出现内容显示不全的bug。
所以这种这种方案也不是完美方案。如果需要在ListView的头或尾添加其他View,推荐使用ListView的addHeaderView()方法和BaseAdapter中getItemViewType()方法结合使用。关于BaseAdapter中getItemViewType中网上有很多教程,主要作用是使ListView中的item填充不同的布局。
最后提醒下,对于ListView的嵌套使用一定要慎重,因为坑很多!