Android getView方法优化简记

来源:互联网 发布:剑三盾太捏脸数据 编辑:程序博客网 时间:2024/04/27 14:34

Android getView方法优化简记

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

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

@Overridepublic 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源码或是开发中应该这样写

@Overridepublic 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();        }    }

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

0 0