这篇“Android中如何提升ListView的性能”文章的知识点大部分人都不太理解,所以小编给大家总结了以下内容,内容详细,步骤清晰,具有一定的借鉴价值,希望大家阅读完这篇文章能有所收获,下面我们一起来看看这篇“Android中如何提升ListView的性能”文章吧。
ListView如何运作的?
ListView是设计应用于对可扩展性和高性能要求的地方。实际上,这就意味着ListView有以下2个要求:
尽可能少的创建View;
只是绘制和布局在屏幕上可见的子View。
理解***点很简单:通过布局xml文件在创建View并显示是很昂贵耗时耗资源的操作。尽管布局文件已经编译打包成了二进制形式以便于更高效的语法解析,但是创建View仍然需要通过一个特殊的XML树,并实例化所有需要响应的View。
ListView通过回收一些不可见的Views,通常在Android源码中称为“ScrapView(废弃的View)”来解决这个问题。这及意味着开发者只需要简单的更新每行的内容而不需要针对每个单独的行的布局来创建View。
为了实现第二点,在我们滑动屏幕时,ListView通过使用View回收器来增加低于或者高于当当前窗口的Views,并当前活动的Views移动到一个可回收池中。这样的话,ListView只需要在内存中保持足够多的Views去填充分配空间中的布局和一些额外的可回收Views,即使当你的Adapter有上百个items的适合。它会使用不同的方法去填充行之间的空间,从顶部或者底部等等,具体取决于窗口是如何变化的。
下面这个图很直观的展示了当你按下ListView的时候发生了什么:
通过上述介绍,相比我们已经熟悉了ListView的这种机制,让我们继续前往技巧部分。正如上述介绍的,在滑动时,ListView通过动态的创建和回收很多View,实现了尽可能地让Adapter的getView()轻量。所有的技巧都是通过多种方法让getView()更快。
View的回收
当ListView每次需要在屏幕上显示新的一行的时候,会从其Adapter中调用getView()的方法。众所周知,getView()方法有3个参数:行的位置, convertView以及父ViewGroup。
参数convertView说穿来就是之前讲述的ScrapView。当ListView要求更新一行的布局时,convertView是一个非空值。因此,当convertView值非空时,你仅仅需要更新内容即可,而不需要重新一个新行的布局。getView()在Adapter中一般是如下的形式:
public View getView(int position, View convertView, ViewGroup parent) { if (convertView == null) { convertView = mInflater.inflate(R.layout.your_layout, null); } TextView text = (TextView) convertView.findViewById(R.id.text); text.setText("Position " + position); return convertView; }
View Holder如何写的模板
Android很常见的一个操作就是在布局文件中找到一个内部的View。通常是使用一个findViewById()的View方法来实现的。这个findViewById()方法在View树中,根据一个View ID,会递归的被调用来找到其子树。虽然在静态UI布局中使用findViewById()是完全正常的。但是,在滑动时,ListView调用其Adapter中的getView()是非常频繁的。findViewById()可能会影响ListView滑动时的性能,尤其是你的行布局是很复杂的时候。
寻找一个充气布局内的内部观点是在Android上最常用的操作之一。这通常是通过一个名为findViewById(查看方法完成)。此方法将递 归经过视图树寻找一个孩子用给定的ID码。静态的UI布局使用findViewById()是完全正常,但正如你所看到的,ListView中滚动时调用 适配器的getView()非常频繁。 findViewById()可能perceivably击中ListViews,尤其是滚动的性能,如果你行的布局是不平凡的。
View Holder的模式就是减少在Adapter中getView()方法中调用findViewById()次数。实际上,View Holder是一个轻量级的内部类,用于直接引用到所有内部views。在创建View之后,你可以在每行的View存储为一个标签。通过这种方法,只需要在初次创建布局的时候调用findViewById()。下面是一个使用上述方法的View Holder模板的代码示例:
public View getView(int position, View convertView, ViewGroup parent) { ViewHolder holder; if (convertView == null) { convertView = mInflater.inflate(R.layout.your_layout, null); holder = new ViewHolder(); holder.text = (TextView) convertView.findViewById(R.id.text); convertView.setTag(holder); } else { holder = convertView.getTag(); } holder.text.setText("Position " + position); return convertView; } private static class ViewHolder { public TextView text; }
异步加载
很多时候,Android应用在ListView每行中显示一些多媒体内容,比如图片等。在Adapter中的getView()使用应用内置的图片资源还是不会出什么问题的,因为可以存储在Android的高速缓存中。但当你想多态的显示来自本地磁盘或网络的内容时,例如缩略图,简历图片等。在这种情况下,你可能不希望直接在Adapter中的getView()加载它们,因为IO进程会阻塞UI线程。如果这样做的话,ListView就看起来非常卡顿。
在一个单独的线程,如果想要运行的所有行的IO操作或任何高负载CPU限制的异步操作。其中的技巧就是要做到符合ListView的回收行为。例如,如果在Adapter中的getView()中,使用AsyncTask的加载去加载资料图片,在AsyncTask完成之前,你正在加载的图片View就有可能被回收用于其他地方。所以,一旦异步操作完成的同时,需要一种机制来知道如果相应的View有没有被回收。
一个简单的方法来实现这一目标是通过附加一些标识该行与它相关的View的信息。然后,当异步操作完成的适合,检查目标行的View和标识的View是否一致。实现这一目标的方法很多。下面是实现这种方法的一个很简单的示例:
public View getView(int position, View convertView, ViewGroup parent) { ViewHolder holder; ... holder.position = position; new ThumbnailTask(position, holder) .executeOnExecutor(AsyncTask.THREAD_POOL_EXECUTOR, null); return convertView; } private static class ThumbnailTask extends AsyncTask { private int mPosition; private ViewHolder mHolder; public ThumbnailTask(int position, ViewHolder holder) { mPosition = position; mHolder = holder; } @Override protected Cursor doInBackground(Void... arg0) { // Download bitmap here } @Override protected void onPostExecute(Bitmap bitmap) { if (mHolder.position == mPosition) { mHolder.thumbnail.setImageBitmap(bitmap); } } } private static class ViewHolder { public ImageView thumbnail; public int position; }
以上就是关于“Android中如何提升ListView的性能”这篇文章的内容,相信大家都有了一定的了解,希望小编分享的内容对大家有帮助,若想了解更多相关的知识内容,请关注亿速云行业资讯频道。
免责声明:本站发布的内容(图片、视频和文字)以原创、转载和分享为主,文章观点不代表本网站立场,如果涉及侵权请联系站长邮箱:is@yisu.com进行举报,并提供相关证据,一经查实,将立刻删除涉嫌侵权内容。