$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 }

   總結:總之防止錯亂關鍵就是一句話:哪里對控件有修改,另外的地方就要把它改回來。

 


免責聲明!

本站轉載的文章為個人學習借鑒使用,本站對版權不負任何法律責任。如果侵犯了您的隱私權益,請聯系本站郵箱yoyou2525@163.com刪除。



 
粵ICP備18138465號   © 2018-2025 CODEPRJ.COM