zoukankan      html  css  js  c++  java
  • $ListView的优化机制和滑动时数据错乱的讨论

      Refer:http://www.myexception.cn/mobile/1612364.html

      (一)Android ListView的基本用法

      1、创建一个实体类Person,为其添加Getter和Setter方法,作为ListView适配器的类型:

     1 public class Person {
     2     private int imageId;
     3     private String name;
     4     private int age;
     5 
     6     public Person(int imageId, String name, int age) {
     7         this.imageId = imageId;
     8         this.name = name;
     9         this.age = age;
    10     }
    11 
    12     public int getImageId() {
    13         return imageId;
    14     }
    15 
    16     public String getName() {
    17         return name;
    18     }
    19 
    20     public int getAge() {
    21         return age;
    22     }
    23 
    24     public void setImageId(int imageId) {
    25         this.imageId = imageId;
    26     }
    27 
    28     public void setName(String name) {
    29         this.name = name;
    30     }
    31 
    32     public void setAge(int age) {
    33         this.age = age;
    34     }
    35     
    36     
    37 
    38 }

      2、创建person_item.xml文件,其中包含一个ImageView和两个TextView:

     1 <?xml version="1.0" encoding="utf-8"?>
     2 <LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
     3     android:id="@+id/person_item_ll"
     4     android:layout_width="match_parent"
     5     android:layout_height="wrap_content"
     6     android:orientation="horizontal" >
     7 
     8     <ImageView
     9         android:id="@+id/image_iv"
    10         android:layout_width="wrap_content"
    11         android:layout_height="wrap_content"
    12         android:src="@drawable/img" />
    13 
    14     <LinearLayout
    15         android:layout_width="0dp"
    16         android:layout_height="match_parent"
    17         android:layout_weight="1"
    18         android:orientation="vertical"
    19         android:gravity="center" >
    20 
    21         <TextView
    22             android:id="@+id/name_tv"
    23             android:layout_width="match_parent"
    24             android:layout_height="wrap_content"
    25             android:gravity="center"
    26             android:text="Tom" />
    27 
    28         <TextView
    29             android:id="@+id/age_tv"
    30             android:layout_width="match_parent"
    31             android:layout_height="wrap_content"
    32             android:gravity="center"
    33             android:text="20" />
    34     </LinearLayout>
    35 
    36 </LinearLayout>

      3、创建自定义适配器类PersonAdapter,以Person类为泛型,继承自ArrayAdapter<Person>,重写父类的构造方法和getView方法,getView方法会在每个子项被滚动到屏幕内的时候调用:

     1 public class PersonAdapter extends ArrayAdapter<Person> {
     2     private int mResourceId;
     3 
     4     public PersonAdapter(Context context, int textViewResourceId,
     5             List<Person> objects) {
     6         super(context, textViewResourceId, objects);
     7         // textViewResourceId:ListView子项布局的id;objects:数据
     8         mResourceId = textViewResourceId;
     9     }
    10 
    11     @Override
    12     public View getView(int position, View convertView, ViewGroup parent) {
    13         // 1.获取当前项的Person实例
    14         Person person = getItem(position);
    15         
    16         // 2.为这个子项加载传入的布局
    17         View view = LayoutInflater.from(getContext()).inflate(mResourceId, null);
    18         
    19         // 3.用view的findViewById方法获取到子项布局控件的实例
    20         ImageView imgIv = (ImageView) view.findViewById(R.id.image_iv);
    21         TextView nameTv = (TextView) view.findViewById(R.id.name_tv);
    22         TextView ageTv = (TextView) view.findViewById(R.id.age_tv);
    23         
    24         // 4.设置相应控件的内容
    25         imgIv.setImageResource(person.getImageId());
    26         nameTv.setText(person.getName());
    27         ageTv.setText(person.getAge() + "");
    28         
    29         // 5.为imgIv设置点击事件,点击它的时候换图片
    30         final ImageView finalImgIv = imgIv;
    31         imgIv.setOnClickListener(new OnClickListener() {
    32             
    33             @Override
    34             public void onClick(View v) {
    35                 finalImgIv.setImageResource(R.drawable.another_img);
    36             }
    37         });
    38         
    39         // 6.返回view
    40         return view;
    41     }
    42 
    43 }

      4、activity_main.xml

     1 <LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
     2     android:layout_width="match_parent"
     3     android:layout_height="match_parent"
     4     android:orientation="vertical" >
     5 
     6     <ListView
     7         android:id="@+id/person_info_lv"
     8         android:layout_width="match_parent"
     9         android:layout_height="match_parent" >
    10     </ListView>
    11 
    12 </LinearLayout>

      5、MainActivity:

     1 public class MainActivity extends Activity {
     2 
     3     private ListView personInfoLv;
     4 
     5     private String[] names;
     6     private int[] ages;
     7     private List<Person> persons;
     8     private int imageId;
     9 
    10     @Override
    11     protected void onCreate(Bundle savedInstanceState) {
    12         super.onCreate(savedInstanceState);
    13         setContentView(R.layout.activity_main);
    14 
    15         personInfoLv = (ListView) findViewById(R.id.person_info_lv);
    16 
    17         names = new String[] { "AAA", "BBB", "CCC", "DDD", "EEE", "FFF", "GGG",
    18                 "HHH", "III", "JJJ", "KKK", "LLL", "MMM", "NNN", "OOO" };
    19         ages = new int[names.length];
    20         persons = new ArrayList<Person>();
    21         imageId = R.drawable.img;
    22 
    23         for (int i = 0; i < names.length; i++) {
    24             ages[i] = i + 1;
    25         }
    26         // 创建Person信息列表
    27         for (int i = 0; i < names.length; i++) {
    28             Person person = new Person(imageId, names[i], ages[i]);
    29             persons.add(person);
    30         }
    31         // 创建adapter
    32         PersonAdapter adapter = new PersonAdapter(MainActivity.this,
    33                 R.layout.person_item, persons);
    34         
    35         // 设置adapter
    36         personInfoLv.setAdapter(adapter);
    37 
    38     }
    39 }

      运行效果:

     

      (二)ListView的性能优化及滑动时数据显示错乱问题解决

      1、在adapter的getView方法中,每次都将布局重新加载一遍,当快速滚动屏幕时候就会带来性能问题;此外,View的findViewById方法对性能的影响也比较大。为此要做一些优化,主要使用缓存和ViewHolder两种策略。缓存机制如下图,可以实现item的复用(假设一屏可以容纳7个item)

      2、假设现在有两个新需求:一个是把列表的前三项的背景颜色设置成蓝色的,另一个是在每次点击每一item的图片时,不仅要修改图片,还要把修改后的图片id存到列表对象中去,这个可以用控件的setTag方法来实现。加上实现优化策略,最终修改原adapter如下:

     1 public class PersonAdapter extends ArrayAdapter<Person> {
     2     private int mResourceId;
     3 
     4     public PersonAdapter(Context context, int textViewResourceId,
     5             List<Person> objects) {
     6         super(context, textViewResourceId, objects);
     7         mResourceId = textViewResourceId;
     8     }
     9 
    10     @Override
    11     public View getView(int position, View convertView, ViewGroup parent) {
    12         Person person = getItem(position);
    13 
    14         View view;
    15         ViewHolder viewHolder;
    16 
    17         if (null == convertView) {
    18             view = LayoutInflater.from(getContext()).inflate(
    19                     R.layout.person_item, null);
    20 
    21             viewHolder = new ViewHolder();
    22             viewHolder.imageIv = (ImageView) view.findViewById(R.id.image_iv);
    23             viewHolder.nameTv = (TextView) view.findViewById(R.id.name_tv);
    24             viewHolder.ageTv = (TextView) view.findViewById(R.id.age_tv);
    25 
    26             // 点击图片的时候更换图片,并更改列表对象中的imageId的值
    27             final ViewHolder finalViewHolder = viewHolder;
    28             viewHolder.imageIv.setOnClickListener(new OnClickListener() {
    29 
    30                 @Override
    31                 public void onClick(View v) {
    32                     Person p = (Person) finalViewHolder.imageIv.getTag();
    33 
    34                     int currentImageId;
    35                     if (p.getImageId() == R.drawable.img) {
    36                         finalViewHolder.imageIv
    37                                 .setImageResource(R.drawable.another_img);
    38                         currentImageId = R.drawable.another_img;
    39                     } else {
    40                         finalViewHolder.imageIv
    41                                 .setImageResource(R.drawable.img);
    42                         currentImageId = R.drawable.img;
    43                     }
    44 
    45                     p.setImageId(currentImageId);
    46                 }
    47             });
    48 
    49             view.setTag(viewHolder);
    50             viewHolder.imageIv.setTag(person);
    51         } else {
    52             view = convertView;
    53             viewHolder = (ViewHolder) view.getTag();
    54             viewHolder.imageIv.setTag(person);
    55         }
    56 
    57         viewHolder.imageIv.setImageResource(person.getImageId());
    58         viewHolder.nameTv.setText(person.getName());
    59         viewHolder.ageTv.setText(person.getAge() + "");
    60 
    61         // 为前三个item设置背景颜色为蓝色
    62         if (position < 3) {
    63             view.setBackgroundColor(0xFF0000FF);
    64         } 
    65 
    66         return view;
    67     }
    68 
    69     class ViewHolder {
    70         ImageView imageIv;
    71         TextView nameTv;
    72         TextView ageTv;
    73     }
    74 
    75 }

      3、这时发现在滑动ListView后,不仅是前三个item的背景颜色是蓝色的,而且后面有些项的背景颜色也变成了蓝色的,而且毫无规律可循。滑动几次后的效果如下图所示:

      

      这主要是因为缓存复用引起的问题,只需要在原来代码的64行后面添加else判断即可,将不是前三行的item的背景颜色设置成默认的白色的。最终代码如下:

     1 public class PersonAdapter extends ArrayAdapter<Person> {
     2     private int mResourceId;
     3 
     4     public PersonAdapter(Context context, int textViewResourceId,
     5             List<Person> objects) {
     6         super(context, textViewResourceId, objects);
     7         mResourceId = textViewResourceId;
     8     }
     9 
    10     @Override
    11     public View getView(int position, View convertView, ViewGroup parent) {
    12         Person person = getItem(position);
    13 
    14         View view;
    15         ViewHolder viewHolder;
    16 
    17         if (null == convertView) {
    18             view = LayoutInflater.from(getContext()).inflate(
    19                     R.layout.person_item, null);
    20 
    21             viewHolder = new ViewHolder();
    22             viewHolder.imageIv = (ImageView) view.findViewById(R.id.image_iv);
    23             viewHolder.nameTv = (TextView) view.findViewById(R.id.name_tv);
    24             viewHolder.ageTv = (TextView) view.findViewById(R.id.age_tv);
    25 
    26             // 点击图片的时候更换图片,并更改列表对象中的imageId的值
    27             final ViewHolder finalViewHolder = viewHolder;
    28             viewHolder.imageIv.setOnClickListener(new OnClickListener() {
    29 
    30                 @Override
    31                 public void onClick(View v) {
    32                     Person p = (Person) finalViewHolder.imageIv.getTag();
    33 
    34                     int currentImageId;
    35                     if (p.getImageId() == R.drawable.img) {
    36                         finalViewHolder.imageIv
    37                                 .setImageResource(R.drawable.another_img);
    38                         currentImageId = R.drawable.another_img;
    39                     } else {
    40                         finalViewHolder.imageIv
    41                                 .setImageResource(R.drawable.img);
    42                         currentImageId = R.drawable.img;
    43                     }
    44 
    45                     p.setImageId(currentImageId);
    46                 }
    47             });
    48 
    49             view.setTag(viewHolder);
    50             viewHolder.imageIv.setTag(person);
    51         } else {
    52             view = convertView;
    53             viewHolder = (ViewHolder) view.getTag();
    54             viewHolder.imageIv.setTag(person);
    55         }
    56 
    57         viewHolder.imageIv.setImageResource(person.getImageId());
    58         viewHolder.nameTv.setText(person.getName());
    59         viewHolder.ageTv.setText(person.getAge() + "");
    60 
    61         // 为前三个item设置背景颜色为蓝色
    62         if (position < 3) {
    63             view.setBackgroundColor(0xFF0000FF);
    64         } else {
    65             view.setBackgroundColor(0xFFFFFFFF);
    66         }
    67 
    68         return view;
    69     }
    70 
    71     class ViewHolder {
    72         ImageView imageIv;
    73         TextView nameTv;
    74         TextView ageTv;
    75     }
    76 
    77 }

       总结:总之防止错乱关键就是一句话:哪里对控件有修改,另外的地方就要把它改回来。

  • 相关阅读:
    git stash回退
    基于git tag快速修复
    Go语言开发规范
    defer实现原理
    string数据结构
    关于const iota
    struct
    map数据结构底层详解
    go 函数进阶
    go 内存分配原理
  • 原文地址:https://www.cnblogs.com/jiayongji/p/5402980.html
Copyright © 2011-2022 走看看