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 }
總結:總之防止錯亂關鍵就是一句話:哪里對控件有修改,另外的地方就要把它改回來。