ListView可以说是安卓开发中经常用到的了。常常想让ListView的每个item中显示的东西多一点,比如说显示图片、文字、时间等等,这就需要我们自定义adapter,重写getView方法。本文主要是总结一下adapter中getView方法的优化。
以下是本链接中的内容,也是最常见的getView优化方案。
1. ListView需要设置adapter,它的item是通过adapter的方法getView(int position, View convertView, ViewGroup parent)获得的。
public View getView(int position, View convertView, ViewGroup parent) {
ViewHolder holder;
if (convertView == null) {
convertView = mInflater.inflate(R.layout.list_item_icon_text, null);
holder = new ViewHolder();
holder.icon1 = (ImageView) convertView.findViewById(R.id.icon1);
holder.text1 = (TextView) convertView.findViewById(R.id.text1);
convertView.setTag(holder);
}
else{
holder = (ViewHolder)convertView.getTag();
}
holder.icon1.setImageResource(R.drawable.icon);
holder.text1.setText(mData[position]);
return holder;
}
static class ViewHolder {
TextView text1;
ImageView icon1;
}
这是什么样的情况了,看了网上的资料以后我知道原来没有设置器listview 的布局方式不是fill-parent,而是wrap-content,会计算父控件的高度所以造成了一种反复调用情况,从而次数不确定。更深层次的解释为:
View在Draw的时候分成两个阶段:measure和layout,在measure阶段时主要就是为了计算两个参数:height和width。而且要注意的是,这是个递归的过程,从顶向下,DecorView开始依次调用自己子元素的measure。计算完成这两个参数后就开始layout,最后再是draw的调用。
对于ListView,当然每一个Item都会被调用measure方法,而在这个过程中getView和getCount会被调用,而且看用户的需求,可能会有很多次调用。
而为什么会有很多组次调用呢?
问题就在于在layout中的决定ListView或者它的父元素的height和width属性的定义了。fill_parent会好一点,计算方法会比较简单,只要跟父元素的大小相似就行,但是即使是fill_parent,也不能给View当饭吃,还是要计算出来具体的dip,所以measure还是会被调用,只是可能比wrap_content的少一点。至于自适应的它会一直考量它的宽和高,根据内容(也就是它的子Item)计算宽高。可能这个measure过程会反复执行,如果父元素也是wrap_content,这个过程会更加漫长。
所以,解决方法就是尽量避免自适应,除非是万不得已,固定大小或者填充的效果会比较好一些。
以上是网上经常所说的解决getView重复调用的方法,ListView中尽量采用fill-parent或者固定大小。除此之外,还想到了另外一种方法,既然重复调用是由于onMeasure和onLayout的调用引起,那么我们在adapt里面的getview中,判断是否在onmeasure里,如果在,那么仅仅mInflater.inflate(R.layout.XXX),然后立刻返回这个convertView;如果不在onmeasure里,那么再去真正的onlayout。
具体的就是,首先自定义要用到的view:
@Override
protected void onMeasure(int widthMeasureSpec, int heightMeasureSpec) {
Log.d("onMeasure", "onMeasure");
isOnMeasure = true;
super.onMeasure(widthMeasureSpec, heightMeasureSpec);
}
@Override
protected void onLayout(boolean changed, int l, int t, int r, int b) {
Log.d("onLayout", "onLayout");
isOnMeasure = false;
super.onLayout(changed, l, t, r, b);
}
在自定义的类中写一个isOnMeasure()方法返回
isOnMeasure变量。
再然后:
public View getView(int position, View convertView, ViewGroup parent) {
...
if(parent instanceof YourView){
if(((YourView) parent).isOnMeasure()){
return convertView;
}
}
...
这样就可以了。