android listView 重写Adapter方法getView的性能优化问题

1.为什么要重写Adapter 的getView方法

     为什么要重写getView方法 可以看这里  android listView滚动时checkBox的状态不会保存的问题   

2.在哪个地方会有性能问题

拿出  android listView滚动时checkBox的状态不会保存的问题  这里的getView方法里的代码看下就会明白了

 @Override  
    public View getView(final int position, View convertView, ViewGroup parent) {  
        // TODO Auto-generated method stub  
  
        LayoutInflater mInflater = LayoutInflater.from(context);  
        //每次调用 都实例化layout 效率非常低  ,而且每次在下面都创建了好几个View
        convertView = mInflater.inflate(R.layout.user_list, null);  

        ImageView imgUserImg = (ImageView) convertView.findViewById(R.id.imgUserImg);  
        imgUserImg.setBackgroundResource((Integer) listData.get(position).get("imgUserImg"));            
        TextView tvUserName = (TextView) convertView.findViewById(R.id.tvUserName);  
        tvUserName.setText((String)listData.get(position).get("tvUserName"));  
          
        Log.d("getView", (String)listData.get(position).get("tvUserName"));  
          
        TextView tvUserMoney = (TextView) convertView.findViewById(R.id.tvUserMoney);  
        tvUserMoney.setText(String.valueOf(listData.get(position).get("tvUserMoney")));  
          
        CheckBox check = (CheckBox) convertView.findViewById(R.id.ckUser);  
         。。。。。。

以上代码你会发现:每次加载item的时候,都要加载一下布局文件,然后生成一个新的convertView View对象,然后通过View找到对应的ImageView和TextView,正如我们所知道的那样,加载布局文件时很耗时的,特别是在操作比较频繁 情况下,这是不可忍受的,所以会导致ANR现象。

3.listView工作原理

ListView的工作原理如下

    ListView 针对每个item,要求 adapter “返回一个视图” (getView),也就是说ListView在开始绘制的时候,系统首先调用getCount()函数,根据他的返回值得到ListView的长度,然后根据这个长度,调用getView()一行一行的绘制ListView的每一项。如果你的getCount()返回值是0的话,列表一行都不会显示,如果返回1,就只显示一行。返回几则显示几行。如果我们有几千几万甚至更多的item要显示怎么办?为每个Item创建一个新的View?不可能!!!实际上Android早已经缓存了这些视图,大家可以看下下面这个截图来理解下,这个图是解释ListView工作原理的最经典的图了大家可以收藏下,不懂的时候拿来看看,加深理解,其实Android中有个叫做Recycler的构件,顺带列举下与Recycler相关的已经由Google做过N多优化过的东东比如:AbsListView.RecyclerListener、ViewDebug.RecyclerTraceType等等,要了解的朋友自己查下,不难理解,下图是ListView加载数据的工作原理:

下面简单说下上图的原理:

  1. 如果你有几千几万甚至更多的选项(item)时,其中只有可见的项目存在内存(内存内存哦,说的优化就是说在内存中的优化!!!)中,其他的在Recycler中
  2. ListView先请求一个type1视图(getView)然后请求其他可见的项目。convertView(convertView是getView的第2个参数)在getView中是空(null)的
  3. 当item1滚出屏幕,并且一个新的项目从屏幕低端上来时,ListView再请求一个type1视图。convertView此时不是空值了,它的值是item1。你只需设定新的数据然后返回convertView,不必重新创建一个视图
来看下实现测试的结果吧:我们在getView方法里打印出convertView的值

Log.d(TAG,"getView " + position + " " + convertView

            

第一次刚进来的时(没有去滚动)结果如上面的右图所示(这里有个地方需要注意:listView的

    android:layout_width="fill_parent"    android:layout_height="fill_parent"  设为fill_parent)

然后往下划 如下图

这时convertView不是空值了!item1离开屏幕到Recycler中去了,然后item11被创建,再滚动下:

此时的convertView非空了,在item11离开屏幕之后,它的视图(…7d0)作为convertView容纳item12了.

4.优化代码

了解完原理后就可以开始我们的优化工作了 先上代码吧:(就拷上核心代码,这里需要定义一个静态内部类来保存convertView)

 

public class myAdapter extends BaseAdapter {
......
//定义一个静态内部类 用来临时保存item上每行的数据
	static class ViewHolder{
		TextView tvUserName;
		TextView tvUserMoney;
		CheckBox check;
	}
	@Override
	public View getView(final int position, View convertView, ViewGroup parent) {
		Log.i("myAdapter", "getView -- " + position + " -- " + convertView);
		ViewHolder holder;
		if(convertView==null){
			convertView = mLayoutInflater.inflate(R.layout.user_list, null);
			holder = new ViewHolder();
			holder.tvUserName = (TextView)convertView.findViewById(R.id.tvUserName);
			holder.tvUserMoney = (TextView)convertView.findViewById(R.id.tvUserMoney);
			holder.check = (CheckBox)convertView.findViewById(R.id.ckUser);
			convertView.setTag(holder);
		}else{
			holder = (ViewHolder)convertView.getTag();
		}
		//重新赋值
		holder.tvUserName.setText((String)listData.get(position).get("tvUserName"));
		holder.tvUserMoney.setText(String.valueOf(listData.get(position).get("tvUserMoney")));
		flag = Boolean.parseBoolean((String)listData.get(position).get("ckUser"));
		holder.check.setOnCheckedChangeListener(new OnCheckedChangeListener() {
			@Override
			public void onCheckedChanged(CompoundButton buttonView,
					boolean isChecked) {
				flag = false;//如果有改变事件已改变事项为准
				if (isChecked) {
					state.put(position, isChecked);
					listData.get(position).put("ckUser","true");
				} else {
					state.remove(position);
					listData.get(position).put("ckUser","false");
				}
			}
		});
		holder.check.setChecked(flag?true:(state.get(position)==null?false:true));
                return convertView;
    }

......

}

里的

setTag 和 getTag方法 这里就不解释了可以去google下
简单说setTag就是保存一些数据 供getTag方法使用
我们这里先调用 setTag 保存 holder 然后调用getTag取出holder进行设值



发布了33 篇原创文章 · 获赞 3 · 访问量 19万+
展开阅读全文

没有更多推荐了,返回首页

©️2019 CSDN 皮肤主题: 大白 设计师: CSDN官方博客

分享到微信朋友圈

×

扫一扫,手机浏览