打造android偷懶神器———ListView的萬能適配器

我們一向寫的自定義適配器,無非就是繼承ArrayAdapter,或者繼承自BaseAdapter,然後重寫4個方法,前三個方法基本相同,不同在於getView方法,getView裏面爲了減少綁定和View的重建,又會引入一個靜態類ViewHolder,我相信下面這段代碼你一點見過不少。


 1 package com.example.nanchen.commonadapterforlistviewdemo;
 2 
 3 import android.content.Context;
 4 import android.view.LayoutInflater;
 5 import android.view.View;
 6 import android.view.ViewGroup;
 7 import android.widget.BaseAdapter;
 8 import android.widget.ImageView;
 9 import android.widget.TextView;
10 
11 import java.util.List;
12 
13 /**
14  * 常見的ListView的Adapter適配器
15  * Created by 南塵 on 16-7-28.
16  */
17 public class MyListAdapter extends BaseAdapter {
18     private Context context;
19     private List<Data> list;
20 
21     public MyListAdapter(Context context, List<Data> list) {
22         this.context = context;
23         this.list = list;
24     }
25 
26     @Override
27     public int getCount() {
28         return list == null ? 0 : list.size();
29     }
30 
31     @Override
32     public Object getItem(int position) {
33         return list.get(position);
34     }
35 
36     @Override
37     public long getItemId(int position) {
38         return position;
39     }
40 
41     @Override
42     public View getView(int position, View convertView, ViewGroup viewGroup) {
43         MyViewHolder holder = null;
44         if (convertView == null) {
45             convertView = LayoutInflater.from(context).inflate(R.layout.list_item,viewGroup,false);
46             holder = new MyViewHolder();
47             holder.iv = (ImageView) convertView.findViewById(R.id.item_image);
48             holder.tv = (TextView) convertView.findViewById(R.id.item_text);
49             convertView.setTag(holder);
50         } else {
51             holder = (MyViewHolder) convertView.getTag();
52         }
53         Data data = (Data) getItem(position);
54         holder.iv.setImageResource(data.getImageId());
55         holder.tv.setText(data.getText());
56         return convertView;
57     }
58 
59     public static class MyViewHolder {
60         ImageView iv;
61         TextView tv;
62     }
63 }

上面是大家最常用,見到的適配器的寫法,如果咱們項目中需要很多的適配器的話,咱們一個個寫,想想就很浪費時間,因此咱們可以自己封裝一個
仔細觀察上面的Adapter,的確是前三個方法一樣。我們要是可以全部抽出來就好了。所以可以抽出來,寫一個泛型使其變成一個抽象的基類,繼承自BaseAdapter.其子類只需要去關心其getView方法

1 public abstract class MyListAdapter<T> extends BaseAdapter {
 2     private Context context;
 3     private List<T> list;
 4 
 5     public MyListAdapter(Context context, List<T> list) {
 6         this.context = context;
 7         this.list = list;
 8     }
 9 
10     @Override
11     public int getCount() {
12         return list == null ? 0 : list.size();
13     }
14 
15     @Override
16     public Object getItem(int position) {
17         return list.get(position);
18     }
19 
20     @Override
21     public long getItemId(int position) {
22         return position;
23     }
24 }

好像沒什麼不對,但是這也沒解決多少問題呀,要是我們在寫大項目的時候還可以抽點時間出來打LOL拿個首勝什麼的就更好了。
再來看看getView方法,基本都是先判斷ViewHolder是否爲空,爲空則去Inflate一個xml文件進來,再綁定下視圖,設置一個標記,不爲空的時候直接引用標記。
或許這裏我們可以試一下在ViewHolder上做點什麼。
我們要是想把ViewHolder提取出來,只能把每一個Item都固定在ViewHolder裏面,而Item又不是固定的,怎麼辦?
要是我們可以把這個Item直接作爲參數傳進來就好了,可是傳控件好像不能區分,仔細一想,我們能看到一個控件對應着一個id,這個好像可以用HashMap的鍵值對處理。
而鍵值由於是Int型的,在新的java API中明確表示在鍵值爲Integer的HashMap中我們要用SparseArray作代替,這樣不僅簡單,而且性能更優。
我們嘗試着封裝一下ViewHolder:

 public class ViewHolder {
 2     //現在對於int作爲鍵的官方推薦用SparseArray替代HashMap
 3     private final SparseArray<View> views;
 4     private int position;
 5     private View convertView;
 6     private Context context;
 7 
 8     private ViewHolder(Context context,ViewGroup parent, int layoutId, int position) {
 9         this.context = context;
10         this.views = new SparseArray<>();
11         this.convertView = LayoutInflater.from(parent.getContext()).inflate(layoutId, parent, false);
12         convertView.setTag(this);
13     }
14 
15     /**
16      * 拿到一個ViewHolder對象
17      */
18     public static ViewHolder get(View convertView, ViewGroup parent, int layoutId, int position) {
19         if (convertView == null) {
20             return new ViewHolder(parent.getContext(),parent, layoutId, position);
21         }
22         return (ViewHolder) convertView.getTag();
23     }
24 
25     /**
26      * 通過控件的Id獲取對於的控件,如果沒有則加入views
27      */
28     public <T extends View> T getView(int viewId) {
29         View view = views.get(viewId);
30         if (view == null) {
31             view = convertView.findViewById(viewId);
32             views.put(viewId, view);
33         }
34         return (T) view;
35     }
36 }

這樣的話我們的getView可能會變成這樣。

1 @Override  
2     public View getView(int position, View convertView, ViewGroup parent){  
3         ViewHolder viewHolder = ViewHolder.get(convertView, parent,  
4                 R.layout.list_item, position);  
5         TextView mTitle = viewHolder.getView(R.id.id_tv_title);  
6         mTitle.setText(((Data) list.get(position)).getText());  
7         //這裏就不設置ImageView了
8         return viewHolder.getConvertView();  
9     } 

好吧。與其這樣。我們不如直接寫在Activity中。
並且如果我們想設置東西也許可以在Holder裏面設置,我們可以試一試。
封裝後的ViewHolder是這樣。

1 package com.example.nanchen.commonadapterforlistviewdemo;
 2 
 3 import android.content.Context;
 4 import android.graphics.Bitmap;
 5 import android.util.SparseArray;
 6 import android.view.LayoutInflater;
 7 import android.view.View;
 8 import android.view.ViewGroup;
 9 import android.widget.ImageView;
10 import android.widget.TextView;
11 
12 import com.squareup.picasso.Picasso;
13 
14 /**
15  * 萬能適配器的ViewHolder
16  * Created by 南塵 on 16-7-28.
17  */
18 public class ViewHolder {
19     //現在對於int作爲鍵的官方推薦用SparseArray替代HashMap
20     private final SparseArray<View> views;
21     private int position;
22     private View convertView;
23     private Context context;
24 
25     private ViewHolder(Context context,ViewGroup parent, int layoutId, int position) {
26         this.context = context;
27         this.views = new SparseArray<>();
28         this.convertView = LayoutInflater.from(parent.getContext()).inflate(layoutId, parent, false);
29         convertView.setTag(this);
30     }
31 
32     /**
33      * 拿到一個ViewHolder對象
34      */
35     public static ViewHolder get(View convertView, ViewGroup parent, int layoutId, int position) {
36         if (convertView == null) {
37             return new ViewHolder(parent.getContext(),parent, layoutId, position);
38         }
39         return (ViewHolder) convertView.getTag();
40     }
41 
42     /**
43      * 通過控件的Id獲取對於的控件,如果沒有則加入views
44      */
45     public <T extends View> T getView(int viewId) {
46         View view = views.get(viewId);
47         if (view == null) {
48             view = convertView.findViewById(viewId);
49             views.put(viewId, view);
50         }
51         return (T) view;
52     }
53 
54     public View getConvertView() {
55         return convertView;
56     }
57 
58     /**
59      * 設置字符串
60      */
61     public ViewHolder setText(int viewId,String text){
62         TextView tv = getView(viewId);
63         tv.setText(text);
64         return this;
65     }
66 
67     /**
68      * 設置圖片
69      */
70     public ViewHolder setImageResource(int viewId,int drawableId){
71         ImageView iv = getView(viewId);
72         iv.setImageResource(drawableId);
73         return this;
74     }
75 
76     /**
77      * 設置圖片
78      */
79     public ViewHolder setImageBitmap(int viewId, Bitmap bitmap){
80         ImageView iv = getView(viewId);
81         iv.setImageBitmap(bitmap);
82         return this;
83     }
84 
85     /**
86      * 設置圖片
87      */
88     public ViewHolder setImageByUrl(int viewId,String url){
89         Picasso.with(context).load(url).into((ImageView) getView(viewId));
90 //        ImageLoader.getInstance().init(ImageLoaderConfiguration.createDefault(context));
91 //        ImageLoader.getInstance().displayImage(url, (ImageView) getView(viewId));
92         return this;
93     }
94 
95     public int getPosition(){
96         return position;
97     }
98 }

上面的圖片網絡加載我用Picasso加載框架,這個網上很多圖片加載框架
再看看我們的萬能適配器,這裏我們把它寫做一個抽象類,傳入一個泛型作爲參數。

package com.example.nanchen.commonadapterforlistviewdemo;
 2 
 3 import android.content.Context;
 4 import android.view.View;
 5 import android.view.ViewGroup;
 6 import android.widget.BaseAdapter;
 7 
 8 import java.util.List;
 9 
10 /**
11  * 打造ListView的萬能適配器
12  * Created by 南塵 on 16-7-28.
13  */
14 public abstract class CommonAdaper<T> extends BaseAdapter {
15     private Context context;
16     private List<T> list;
17 
18 
19     public CommonAdaper(Context context, List<T> list) {
20         this.context = context;
21         this.list = list;
22     }
23 
24     @Override
25     public int getCount() {
26         return list == null ? 0 : list.size();
27     }
28 
29     @Override
30     public T getItem(int position) {
31         return list.get(position);
32     }
33 
34     @Override
35     public long getItemId(int position) {
36         return position;
37     }
38 
39     @Override
40     public View getView(int i, View view, ViewGroup viewGroup) {
41         ViewHolder holder = ViewHolder.get(view,viewGroup,R.layout.list_item,i);
42         convert(holder,getItem(i));
43         return holder.getConvertView();
44     }
45 
46     public abstract void convert(ViewHolder holder,T item);
47 
48 }

用法的話就更簡單了

package com.example.nanchen.commonadapterforlistviewdemo;
 2 
 3 import android.support.v7.app.AppCompatActivity;
 4 import android.os.Bundle;
 5 import android.widget.ListView;
 6 
 7 import java.util.ArrayList;
 8 import java.util.List;
 9 
10 public class MainActivity extends AppCompatActivity {
11 
12     private ListView listView;
13     private List<Data> list;
14 
15     @Override
16     protected void onCreate(Bundle savedInstanceState) {
17         super.onCreate(savedInstanceState);
18         setContentView(R.layout.activity_main);
19 
20         listView = (ListView) findViewById(R.id.main_lv);
21         initList();
22 
23         listView.setAdapter(new CommonAdaper<Data>(this,list) {
24             @Override
25             public void convert(ViewHolder holder, Data item) {
26                 holder.setText(R.id.item_text,item.getText());
27                 if (item.getImageUrl() != null){
28                     holder.setImageByUrl(R.id.item_image,item.getImageUrl());
29                 }else {
30                     holder.setImageResource(R.id.item_image,item.getImageId());
31                 }
32             }
33         });
34     }
35 
36     private void initList() {
37         list = new ArrayList<>();
38         for (int i = 0; i < 5; i++) {
39             list.add(new Data("本地 "+i,R.mipmap.ic_launcher));
40         }
41 
42         for (int i = 0; i < 5; i++) {
43             list.add(new Data("網絡 "+i,"http://pic.cnblogs.com/face/845964/20160301162812.png"));
44         }
45     }
46 }

itme的xml佈局文件
一個是Activity_main.xml

 <?xml version="1.0" encoding="utf-8"?>
 2 <RelativeLayout
 3     xmlns:android="http://schemas.android.com/apk/res/android"
 4     xmlns:tools="http://schemas.android.com/tools"
 5     android:layout_width="match_parent"
 6     android:layout_height="match_parent"
 7     tools:context="com.example.nanchen.commonadapterforlistviewdemo.MainActivity">
 8 
 9 
10     <ListView
11         android:layout_width="match_parent"
12         android:layout_height="match_parent"
13         android:id="@+id/main_lv"/>
14 </RelativeLayout>

list_item.xml

<?xml version="1.0" encoding="utf-8"?>
<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
              android:layout_width="match_parent"
              android:layout_height="wrap_content"
              android:orientation="horizontal">

    <ImageView
        android:id="@+id/item_image"
        android:layout_width="60dp"
        android:layout_height="60dp"
        android:src="@mipmap/ic_launcher"/>


    <TextView
        android:id="@+id/item_text"
        android:layout_width="0dp"
        android:layout_height="match_parent"
        android:layout_weight="1"
        android:gravity="center"
        android:text="內容"/>

</LinearLayout>

完事,有問題留言交流~

發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章