• Android之ListView/GridView 优化


    一、效率最低的getView实现

    我们知道,ListView和GridView的显示都是通过Adapter的getView实现的。

    ListView/GridView数据量较小时,我们的处理方式一般是这样的(效率最低的一种方式)

    1 public View getView(int position, View convertView, ViewGroup parent) {
    2      View item = mInflater.inflate(R.layout.list_item_icon_text, null);
    3      ((TextView) item.findViewById(R.id.text)).setText(DATA[position]);
    4      ((ImageView) item.findViewById(R.id.icon)).setImageBitmap(
    5      (position & 1) == 1 ? mIcon1 : mIcon2);
    6      return item;
    7 }

    当数据量非常大时,这样每一次getView都会去inflate布局,效率太差,这会让我们的程序卡顿,滑动多了还会OOM。

    二、利用Android已经提供了View缓存机制实现ViewHolder模式——实现View共享

    我们注意到getView的第二个参数convertView,这个是View缓存机制的关键。简单的说就两点:

    1.假设当前页面我们有7个item,那么前七次getView,这个convertView一开始都是null。因此我们需要inflate布局,这样我们得到7个convertView(convertView1——converView7)

    2.假设我们往上翻页,现在Item8进入页面,item1出页面,此时Item8对应的getView中的convertView就是convertView1。

    利用这个View缓存机制的特点,我们只需要将Item8的getView中convertView(即convertView1)的数据由item1更新为item8即可。也就是说item8和item1就实现了View的共享(共享convertView1)。(本来嘛,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来实现下载的操作

     1 /**
     2  * Created by xuqiang on 15-12-16.
     3  */
     4 public class ImageTask extends AsyncTask<String, Void, Bitmap> {
     5     private ImageView iv;
     6     String imgUrl;
     7     public ImageTask(ImageView iv){
     8         this.iv = iv;
     9     }
    10 
    11     @Override
    12     protected Bitmap doInBackground(String... param) {
    13         imgUrl = param[0];
    14         try {
    15             URL url = new URL(imgUrl);
    16             try {
    17                 HttpURLConnection conn = (HttpURLConnection) url.openConnection();
    18                 InputStream in = conn.getInputStream();
    19                 Bitmap bitmap = BitmapFactory.decodeStream(in);
    20                 if(bitmap!=null){
    21                     return bitmap;
    22                 }
    23             } catch (IOException e) {
    24                 // TODO Auto-generated catch block
    25                 e.printStackTrace();
    26             }
    27         } catch (MalformedURLException e) {
    28             // TODO Auto-generated catch block
    29             e.printStackTrace();
    30         }
    31         return null;
    32     }
    33 
    34     @Override
    35     protected void onPostExecute(Bitmap result) {
    36         super.onPostExecute(result);
    37         if (result != null) {
    38             // 通过 tag 来防止图片错位
    39             if (iv.getTag() != null && iv.getTag().equals(imgUrl)) {
    40                 iv.setImageBitmap(result);
    41             }
    42         }
    43     }
    44 
    45     @Override
    46     protected void onPreExecute() {
    47         super.onPreExecute();
    48     }
    49 }

    然后在getView中,我们只需要将原本对ViewHolder的操作,传参数给ImageTask来处理

                ImageTask imageTask = new ImageTask(viewHolder.image,position);
                imageTask.execute(imgUrls.get(position));

     四、ListView /GridView的异步加载中的错位问题解决

    刚才我们知道,利用的android的View缓存机制,我们可以使用ViewHolder优化ListView/GridView的效率。但是在异步加载图片的过程中,正是因为这个View共享的机制,会造成图片错位的情况。

    ——当快速滑动的情况下,会出现item1和item8都显示部分的情况。

    a.item1和item8共享一个convertView,而异步加载的情况下,是有可能会出现item8异步加载完毕准备显示时,item1还没有加载完毕,这样就会出现先convertView.setView(item8),然后convertView.setView(item1),这样原本应该显示item8的位置却显示了item1。

    b.即使是先加载完Item1,再加载完Item8,也会给用户一种原本应该显示item8的地方先显示item1后再正常显示item8的感觉。

    这个问题,我们如何解决。

    最简单的解决方法就是网上说的,给 ImageView 设置一个 tag。

    当 Item1 比 Item8 图片下载的快时, 你滚下去使 Item8 可见,这时 ImageView 的 tag 被设成了

    Item8 的 URL, 当 Item1 下载完时,由于 Item1 不可见现在的 tag 是 Item8 的 URL,所以不满足条件,

    虽然下载下来了但不会设置到 ImageView 上, tag 标识的永远是可见 view 中图片的 URL。

     我们最后将这个最终的改进方案写下来

    1.在主线程设置一个tag,

     1         @Override
     2         public View getView(int position, View convertView, ViewGroup arg2) {
     3             Log.i("xerrard", "getView " + position + " " + convertView);
     4             ViewHolder viewHolder = null;
     5             if(convertView == null){
     6                 viewHolder = new ViewHolder();
     7                 convertView = inflater.inflate(R.layout.item, null);
     8                 viewHolder.image = (ImageView) convertView.findViewById(R.id.img);
     9 
    10                 convertView.setTag(viewHolder);
    11             }else{
    12                 viewHolder = (ViewHolder) convertView.getTag();
    13             }
    14             viewHolder.image.setTag(imgUrls.get(position));
    15             ImageTask imageTask = new ImageTask(viewHolder.image);
    16             imageTask.execute(imgUrls.get(position));
    17             return convertView;
    18         }
    19         class ViewHolder{
    20             ImageView image;
    21         }
    22     }

    2.在异步的ImageTask设置图片的地方,判断Tag,确认Tag没有问题之后,再设置图片

     1     @Override
     2     protected void onPostExecute(Bitmap result) {
     3         super.onPostExecute(result);
     4         if (result != null) {
     5             // 通过 tag 来防止图片错位
     6             if (iv.getTag() != null && iv.getTag().equals(imgUrl)) {
     7                 iv.setImageBitmap(result);
     8             }
     9         }
    10     }
  • 相关阅读:
    重拾安卓_01_安卓开发环境搭建(eclipse)
    重拾安卓_01_安卓开发环境搭建(android studio)
    【BZOJ】1038: [ZJOI2008]瞭望塔
    【BZOJ】2178: 圆的面积并
    【UR #4】元旦三侠的游戏(博弈论+记忆化)
    【BZOJ】1027: [JSOI2007]合金(凸包+floyd)
    【POJ】1151 Atlantis(线段树)
    【POJ】1228 Grandpa's Estate(凸包)
    【POJ】1556 The Doors(计算几何基础+spfa)
    【POJ】1113 Wall(凸包)
  • 原文地址:https://www.cnblogs.com/xerrard/p/5052734.html
Copyright © 2020-2023  润新知