关于ListView中getView被重复调用的问题
我用ListView显示数据时,自定义了一个适配器(extends ArrayAdapter),然后重写了getView方法,现在出现一个问题,就是这个getView()方法被重复调用了,比如我的_data中有两条数据,但是log.i("show",house.toString());这句却被执行了4次甚至更多,请问各位这是神马情况?
方法代码如下:
这是自定义的适配器:
package com.hb.puppet.utils; import java.util.List; import com.hb.puppet.activity.MetaData; import com.hb.puppet.activity.R; import com.hb.puppet.entity.House; import android.content.Context; import android.graphics.drawable.Drawable; import android.view.LayoutInflater; import android.view.View; import android.view.View.OnClickListener; import android.view.ViewGroup; import android.widget.ArrayAdapter; import android.widget.Button; import android.widget.ImageView; import android.widget.ListView; import android.widget.TextView; public class CustomCollectAdapter extends ArrayAdapter<House> { private static final String CLASSTAG = CustomCollectAdapter.class .getSimpleName(); private ListView _listView; private int _resource; private List<House> _data; private LayoutInflater _inflater; private AsyncLoadImageTask _asyncloader; public CustomCollectAdapter(Context context, ListView listView, List<House> data) { super(context, 0, data); _resource = R.layout.list_item_collect; _data = data; _inflater = (LayoutInflater) context .getSystemService(context.LAYOUT_INFLATER_SERVICE); _asyncloader = new AsyncLoadImageTask(); _listView = listView; } @Override public View getView(int position, View convertView, ViewGroup parent) { View view = convertView; CollectListItemViewCache viewCache = null; // final int index = position; // final ViewGroup p = parent; if (view != null) { viewCache = (CollectListItemViewCache) view.getTag(); } else { view = _inflater.inflate(_resource,null); viewCache = new CollectListItemViewCache(view); view.setTag(viewCache); } // 房源数据 House house = _data.get(position); System.out.println(house.toString()); if (house != null) { //http://www.xxx.com/xxx.jpg String imageUrl = MetaData.HOST + house.getTitlePic(); ImageView imageView = viewCache.getImageView(); imageView.setTag(imageUrl); //异步加载图片 new AsyncImageLoaderTask().execute(imageUrl,imageView); // 房源标题 TextView houseTitle = viewCache.getHouseTitle(); houseTitle.setText(house.getTitle()); // 房源单价 TextView housePrice = viewCache.getHousePrice(); housePrice.setText(house.getSinglePrice() + "元/㎡"); // 房源面积 TextView houseArea = viewCache.getHouseArea(); houseArea.setText(house.getArea() + "㎡"); // 房源户型 TextView houseUnit = viewCache.getHouseUnit(); houseUnit.setText(house.getUnits()); // 单项删除收藏房源 Button delButton = viewCache.getDelButton(); delButton.setOnClickListener(new OnClickListener() { public void onClick(View v) { System.out.println("clicked"); } }); } return view; } }
异步加载图片:
package com.hb.puppet.utils; import java.io.IOException; import java.io.InputStream; import java.lang.ref.SoftReference; import java.net.HttpURLConnection; import java.net.MalformedURLException; import java.net.URL; import java.util.HashMap; import android.graphics.Bitmap; import android.graphics.BitmapFactory; import android.os.AsyncTask; import android.util.Log; import android.widget.ImageView; public class AsyncImageLoaderTask extends AsyncTask<Object, Object, Bitmap>{ private String classTag = AsyncImageLoaderTask.class.getSimpleName(); private ImageView _view; private HashMap<String, SoftReference<Bitmap>> imageCache; public AsyncImageLoaderTask() { imageCache = new HashMap<String, SoftReference<Bitmap>>(); } @Override protected Bitmap doInBackground(Object... params) { Bitmap bitmap = null; String url = (String)params[0]; _view = (ImageView)params[1]; if(_view == null){ Log.e(classTag,classTag + " value of _view is not null"); return null; } if(url != null){ //if current url is int imageCache,get this drawable by it,then return the drawable if(imageCache.containsKey(url)){ SoftReference<Bitmap> mapSoft = imageCache.get(url); bitmap = mapSoft.get(); if(bitmap != null){ return bitmap; } } // URL fromUrl = null; try { fromUrl = new URL(url); HttpURLConnection conn = (HttpURLConnection)fromUrl.openConnection(); conn.setDoInput(true); conn.connect(); InputStream input = conn.getInputStream(); bitmap = BitmapFactory.decodeStream(input); input.close(); } catch (MalformedURLException e) { Log.e(classTag, classTag + " Method doInBackground -- " +e.getMessage()); } catch (IOException e) { Log.e(classTag, classTag + " Method doInBackground -- " +e.getMessage()); } catch(Exception e){ Log.e(classTag, classTag + " Method doInBackground -- " +e.getMessage()); } imageCache.put(url, new SoftReference<Bitmap>(bitmap)); } return bitmap; } @Override protected void onPostExecute(Bitmap bitmap) { if(bitmap != null){ System.out.println("onPostExecute"); this._view.setImageBitmap(bitmap); this._view = null; } } }
这是什么样的情况了,看了网上的资料以后我知道原来没有设置器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,这个过程会更加漫长。
所以,解决方法就是尽量避免自适应,除非是万不得已,固定大小或者填充的效果会比较好一些。