Android getView方法优化简记

Android getView方法优化简记

学习Android差不多半年,现在看来以前学习的终究太浅,重量不中质。看书也是囫囵吞枣,总想着看完再说,却没有想想自己究竟真正的掌握了什么。最近一直在反思之前的学习历程,这个getView方法的优化让我印象颇深,网上的资料数不胜数,说是优化,不如说是一种应该如此的学法。虽然只是入门的知识,但是很多基础教程上都没有提及。把它写下来,就是给自己提个醒,也算值得。

这里以ListView的getView()为例,刚学习Android的时候,重写ListView的Adapter时getView方法是这样写的:

@Override
		public View getView(int position, View convertView, ViewGroup parent) {
			View view = View.inflate(ctx, R.layout.layout_item, null);
			TextView tv1 = (TextView) view.findViewById(R.id.tv_1st);
			TextView tv2 = (TextView) view.findViewById(R.id.tv_2ed);
			tv1.setText(position + "");
			tv2.setText(strs[new Random().nextInt(strs.length)]);
			return view;
		}

这种写法就是一般的Android入门书里的学法,当ListView显示的数据较多时,在手机配置较差或模拟器中快速滑动列表时,特别容易出现内存溢出的错误,导致应用程序崩溃。


查看log:


在Android源码或是开发中应该这样写

		@Override
		public View getView(int position, View convertView, ViewGroup parent) {
			View view = null;
			ViewHolder holder;
			if (convertView != null) {
                                //复用控件
				view = convertView;
				holder = (ViewHolder) view.getTag();
			} else {
                                //构造列表项控件,用ItemView进行缓存
				view = View.inflate(ctx, R.layout.layout_item, null);
				holder = new ViewHolder();
				holder.tv1 = (TextView) view.findViewById(R.id.tv_1st);
				holder.tv2 = (TextView) view.findViewById(R.id.tv_2ed);
				view.setTag(holder);
			}
			//绑定数据
			holder.tv1.setText("ID: " + position);
			holder.tv2.setText(strs[new Random().nextInt(strs.length)]);
			
			return view;
		}
	}
        
        //缓存数据项控件对象
	static class ViewHolder {
		TextView tv1;
		TextView tv2;
	}

这种写法为每个类表象都inflate了一个View对象,当列表项很多时会导致子控件的数量急剧膨胀,耗费大量的内存资源,甚至导致应用崩溃。Android的适配器在设计时,充分考虑了处理大规模数据的场景,为开发者提供了解决策略。在Adapter.getView方法中,有一个输入参数convertView,用于缓存最近一个失去可视状态的列表控件对象。当用户滚动列表时,处于可视状态的列表项会变成不可视状态,而不可视状态的列表则可能会变成可视状态。convertView便是用于缓存失去可视状态的列表项控件对象,通过Adapter.getVie方法传回开发者手中,开发者可以复用这个控件对象重新绑定即将可视的列表项数据,从而避免了构造新列表控件的开销。

另外,在使用convertView的时候,对列表项控件的子控件进行缓存可以节省列表项数据项绑定的开销,View.setTag方法可以将缓存放在对应的列表项控件中,节省调用View.findViewById方法造成的浪费。

通过列表项复用,可以有效地提高列表项的性能,节约内存开销,但同时它也增加了编程的复杂度,尤其是存在多种表项样式时,这种复杂度的增加也尤为明显。开发者需要根据列表项的数量和特征,选择合适的构造方法。

以上三段摘自范怀宇. "Android 开发精要." (2012).

再看一下Android源码中的示例:

android/packages/apps/settings/src/com/android/settings
        @Override
        public View getView(int position, View convertView, ViewGroup parent) {
            // A ViewHolder keeps references to children views to avoid unnecessary calls
            // to findViewById() on each row.
            AppViewHolder holder = AppViewHolder.createOrRecycle(mInflater, convertView);
            convertView = holder.rootView;
            MyApplicationInfo info = getItem(position);
            holder.appName.setText(info.label);
            if (info.info != null) {
                holder.appIcon.setImageDrawable(info.info.loadIcon(getPackageManager()));
                holder.appSize.setText(info.info.packageName);
            } else {
                holder.appIcon.setImageDrawable(null);
                holder.appSize.setText("");
            }
            holder.disabled.setVisibility(View.GONE);
            holder.checkBox.setVisibility(View.GONE);
            return convertView;
        }
    }

public class AppViewHolder {
    public ApplicationsState.AppEntry entry;
    public View rootView;
    public TextView appName;
    public ImageView appIcon;
    public TextView appSize;
    public TextView disabled;
    public CheckBox checkBox;

    static public AppViewHolder createOrRecycle(LayoutInflater inflater, View convertView) {
        if (convertView == null) {
            convertView = inflater.inflate(R.layout.manage_applications_item, null);

            // Creates a ViewHolder and store references to the two children views
            // we want to bind data to.
            AppViewHolder holder = new AppViewHolder();
            holder.rootView = convertView;
            holder.appName = (TextView) convertView.findViewById(R.id.app_name);
            holder.appIcon = (ImageView) convertView.findViewById(R.id.app_icon);
            holder.appSize = (TextView) convertView.findViewById(R.id.app_size);
            holder.disabled = (TextView) convertView.findViewById(R.id.app_disabled);
            holder.checkBox = (CheckBox) convertView.findViewById(R.id.app_on_sdcard);
            convertView.setTag(holder);
            return holder;
        } else {
            // Get the ViewHolder back to get fast access to the TextView
            // and the ImageView.
            return (AppViewHolder)convertView.getTag();
        }
    }

之前也曾经看过相关内容,可是却由于思考上的惰性,导致之后的写法还是刚接触时的样子。学习不能只是被动接受,学而不思则罔,古人说的太对了。

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值