Android之ListView/GridView 优化
2015-12-17 15:12
543 查看
一、效率最低的getView实现
我们知道,ListView和GridView的显示都是通过Adapter的getView实现的。ListView/GridView数据量较小时,我们的处理方式一般是这样的(效率最低的一种方式)
public View getView(int position, View convertView, ViewGroup parent) { View item = mInflater.inflate(R.layout.list_item_icon_text, null); ((TextView) item.findViewById(R.id.text)).setText(DATA[position]); ((ImageView) item.findViewById(R.id.icon)).setImageBitmap( (position & 1) == 1 ? mIcon1 : mIcon2); return item; }
当数据量非常大时,这样每一次getView都会去inflate布局,效率太差,这会让我们的程序卡顿,滑动多了还会OOM。
二、利用Android已经提供了View缓存机制实现ViewHolder模式——实现View共享
我们注意到getView的第二个参数convertView,这个是View缓存机制的关键。简单的说就两点:
1.假设当前页面我们有7个item,那么前七次getView,这个convertView都是null
2.假设我们往上翻页,现在Item8进入页面,item1出页面,此时Item8对应的getView中的convertView就是item1
利用这个View缓存机制的特点,我们只需要将Item8的getView中convertView的数据由item1更新为item8即可。也就是说item8和item1就实现了View的共享。(本来嘛,item8和item1永远不会在一个界面中一起出现,确实应该共享一些资源)
而这就是所谓的ViewHolder模式
@Override public View getView(int position, View convertView, ViewGroup arg2) { List Log.i("xerrard", "getView " + position + " " + convertView); ViewHolder viewHolder = null; if(convertView == null){ viewHolder = new ViewHolder(); convertView = inflater.inflate(R.layout.item, null); viewHolder.image = (ImageView) convertView.findViewById(R.id.img); viewHolder.text = (TextView) convertView.findViewById(R.id.txt); convertView.setTag(viewHolder); }else{ viewHolder = (ViewHolder) convertView.getTag(); } viewHolder.image.setImageBitmap(imgs.get(position)); viewHolder.text.setText(texts.get(position)); return convertView; } class ViewHolder{ ImageView image; TextView text; }
三、ListView /GridView的异步加载。
很多时候我们会需要再网上下载一些图片来进行显示,如果在主线程做下载的操作,这样很可能会造成ANR,所以需要子线程来实现。(即使是本地存储中的图片,也建议用子线程来实现,本地IO操作有时也会造成ANR)我们使用android中的AsyncTask来实现下载的操作
/** * Created by xuqiang on 15-12-16. */ public class ImageTask extends AsyncTask<String, Void, Bitmap> { private ImageView iv; String imgUrl; public ImageTask(ImageView iv){ this.iv = iv; } @Override protected Bitmap doInBackground(String... param) { imgUrl = param[0]; try { URL url = new URL(imgUrl); try { HttpURLConnection conn = (HttpURLConnection) url.openConnection(); InputStream in = conn.getInputStream(); Bitmap bitmap = BitmapFactory.decodeStream(in); if(bitmap!=null){ return bitmap; } } catch (IOException e) { // TODO Auto-generated catch block e.printStackTrace(); } } catch (MalformedURLException e) { // TODO Auto-generated catch block e.printStackTrace(); } return null; } @Override protected void onPostExecute(Bitmap result) { super.onPostExecute(result); if (result != null) { // 通过 tag 来防止图片错位 if (iv.getTag() != null && iv.getTag().equals(imgUrl)) { iv.setImageBitmap(result); } } } @Override protected void onPreExecute() { super.onPreExecute(); } }
然后在getView中,我们只需要将原本对ViewHolder的操作,传参数给ImageTask来处理
ImageTask imageTask = new ImageTask(viewHolder.image,position); imageTask.execute(imgUrls.get(position));
四、ListView /GridView的异步加载中的错位问题解决
刚才我们知道,利用的android的View缓存机制,我们可以使用ViewHolder优化ListView/GridView的效率。但是在异步加载图片的过程中,正是因为这个View共享的机制,会造成图片错位的情况。这个问题,我们如何解决。
最简单的解决方法就是网上说的,给 ImageView 设置一个 tag。
当 Item1 比 Item8 图片下载的快时, 你滚下去使 Item8 可见,这时 ImageView 的 tag 被设成了
Item8 的 URL, 当 Item1 下载完时,由于 Item1 不可见现在的 tag 是 Item8 的 URL,所以不满足条件,
虽然下载下来了但不会设置到 ImageView 上, tag 标识的永远是可见 view 中图片的 URL。
我们最后将这个最终的改进方案写下来
1.在主线程设置一个tag,
@Override public View getView(int position, View convertView, ViewGroup arg2) { Log.i("xerrard", "getView " + position + " " + convertView); ViewHolder viewHolder = null; if(convertView == null){ viewHolder = new ViewHolder(); convertView = inflater.inflate(R.layout.item, null); viewHolder.image = (ImageView) convertView.findViewById(R.id.img); convertView.setTag(viewHolder); }else{ viewHolder = (ViewHolder) convertView.getTag(); } viewHolder.image.setTag(imgUrls.get(position)); ImageTask imageTask = new ImageTask(viewHolder.image); imageTask.execute(imgUrls.get(position)); return convertView; } class ViewHolder{ ImageView image; } }
2.在异步的ImageTask设置图片的地方,判断Tag,确认Tag没有问题之后,再设置图片
@Override protected void onPostExecute(Bitmap result) { super.onPostExecute(result); if (result != null) { // 通过 tag 来防止图片错位 if (iv.getTag() != null && iv.getTag().equals(imgUrl)) { iv.setImageBitmap(result); } } }
相关文章推荐
- Android 源码中修改某些文件的时间戳方法
- Android官方文档翻译 十七 4.1Starting an Activity
- Android开发之定制自己的日志工具类
- Android 用户使用崩溃处理
- android适配解决方案
- 页面未随软键盘上升及android隐藏软键盘总结
- Android自定义View之吃豆人动画(一)
- 【Android开发基础】应用界面主题Theme使用方法
- 获取android版本号
- Android Root权限静默安装
- Android 解析doc、excel
- Android系统音量取消关联
- Android sqlite版本更新大致方案
- Android存储之SQLite数据库
- Android存储之SQLite数据库
- Android 尺寸单位转换和屏幕适配相关
- android常用应用的包名和startAcitivity名
- AndroidManifest.xml文件解析
- android 仿淘宝京东购物车 ListView嵌套CheckBox
- 浅谈android自定义view