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

若是你去作任何一個項目,我相信你都會跟我有同樣的經歷,最最廣泛的就是列表顯示ListView,固然,寫N個自定義的適配器也是情理之中。雖然說程序員自己就是搬磚,作這些枯燥無味的重複的事情也是理所固然,但不得不說,誰都想作點高效率的事情的。java

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

 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 }

能夠堅決果斷地說這個東西我如今閉着眼睛都能流利地寫出來,可見少了數百次是難以作到的。git

有時候咱們也想盛點時間去打點小地主,撩下小妹子,若是要是能夠打造一個萬能的適配器就行了。程序員

 

仔細觀察上面的Adapter,的確是前三個方法同樣。咱們要是能夠所有抽出來就行了。因此能夠抽出來,寫一個泛型使其變成一個抽象的基類,繼承自BaseAdapter.其子類只須要去關心其getView方法。github

 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文件進來,再綁定下視圖,設置一個標記,不爲空的時候直接引用標記。app

或許這裏咱們能夠試一下在ViewHolder上作點什麼。框架

咱們要是想把ViewHolder提取出來,只能把每個Item都固定在ViewHolder裏面,而Item又不是固定的,怎麼辦?ide

要是咱們能夠把這個Item直接做爲參數傳進來就行了,但是傳控件好像不能區分,仔細一想,咱們能看到一個控件對應着一個id,這個好像能夠用HashMap的鍵值對處理。佈局

而鍵值因爲是Int型的,在新的java API中明確表示在鍵值爲Integer的HashMap中咱們要用SparseArray做代替,這樣不只簡單,並且性能更優。

咱們嘗試着封裝一下ViewHolder

 1 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加載框架,這個網上不少圖片加載框架,我前面博客也有不少Demo,你們能夠自行查找。

再看看咱們的萬能適配器,這裏咱們把它寫作一個抽象類,傳入一個泛型做爲參數。

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

再看看咱們主頁面怎麼調用的。

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

最後上我寫的兩個xml

一個是Activity_main.xml

 1 <?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>

這個你想怎麼定義就想定義了。

本人親測這個東西能夠用,還沒入坑的小夥伴趕快入坑吧。

明天我可能還會帶來萬能的RecyclerView的適配器,還請你們持續關注~~~~

 

 

———————————————————————————————————————————————————————————————————————————— 

7月30日補充:

今天在本身思考寫最近大火的RecyclerView的萬能適配器的時候參考這邊的時候,發如今MainActivity中居然不能設置其餘的佈局,才發現本身以前的邏輯有點問題,應該把Layout的ID也做爲參數傳到Adater去中。看來本身有時候想的也不是很周到,你們看的時候也要多加思考呀。

這是改動後的Adpter和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 
 9 import java.util.List;
10 
11 /**
12  * 打造ListView的萬能適配器
13  * Created by 南塵 on 16-7-28.
14  */
15 public abstract class CommonAdaper<T> extends BaseAdapter {
16     private Context context;
17     private List<T> list;
18     private LayoutInflater inflater;
19     private int itemLayoutId;
20 
21 
22     public CommonAdaper(Context context, List<T> list,int itemLayoutId) {
23         this.context = context;
24         this.list = list;
25         this.itemLayoutId = itemLayoutId;
26         inflater = LayoutInflater.from(context);
27     }
28 
29     @Override
30     public int getCount() {
31         return list == null ? 0 : list.size();
32     }
33 
34     @Override
35     public T getItem(int position) {
36         return list.get(position);
37     }
38 
39     @Override
40     public long getItemId(int position) {
41         return position;
42     }
43 
44     @Override
45     public View getView(int position, View convertView, ViewGroup parent) {
46         ViewHolder holder = getViewHolder(position,convertView,parent);
47         convert(holder,getItem(position));
48         return holder.getConvertView();
49     }
50 
51     public abstract void convert(ViewHolder holder,T item);
52 
53     private ViewHolder getViewHolder(int position,View convertView,ViewGroup parent){
54         return ViewHolder.get(context,convertView,parent,itemLayoutId,position);
55     }
56 
57 }

思想裏面應該很清楚了吧。

 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 View convertView;
22     private Context context;
23 
24     private ViewHolder(Context context,ViewGroup parent,int itemLayoutId,int position) {
25         this.context = context;
26         this.views = new SparseArray<>();
27         this.convertView = LayoutInflater.from(context).inflate(itemLayoutId,parent,false);
28         convertView.setTag(this);
29     }
30 
31     /**
32      * 拿到一個ViewHolder對象
33      */
34     public static ViewHolder get(Context context,View convertView, ViewGroup parent, int layoutId, int position) {
35         if (convertView == null) {
36             return new ViewHolder(context,parent, layoutId, position);
37         }
38         return (ViewHolder) convertView.getTag();
39     }
40 
41     /**
42      * 經過控件的Id獲取對於的控件,若是沒有則加入views
43      */
44     public <T extends View> T getView(int viewId) {
45         View view = views.get(viewId);
46         if (view == null) {
47             view = convertView.findViewById(viewId);
48             views.put(viewId, view);
49         }
50         return (T) view;
51     }
52 
53     public View getConvertView() {
54         return convertView;
55     }
56 
57     /**
58      * 設置字符串
59      */
60     public ViewHolder setText(int viewId,String text){
61         TextView tv = getView(viewId);
62         tv.setText(text);
63         return this;
64     }
65 
66     /**
67      * 設置圖片
68      */
69     public ViewHolder setImageResource(int viewId,int drawableId){
70         ImageView iv = getView(viewId);
71         iv.setImageResource(drawableId);
72         return this;
73     }
74 
75     /**
76      * 設置圖片
77      */
78     public ViewHolder setImageBitmap(int viewId, Bitmap bitmap){
79         ImageView iv = getView(viewId);
80         iv.setImageBitmap(bitmap);
81         return this;
82     }
83 
84     /**
85      * 設置圖片
86      */
87     public ViewHolder setImageByUrl(int viewId,String url){
88         Picasso.with(context).load(url).into((ImageView) getView(viewId));
89 //        ImageLoader.getInstance().init(ImageLoaderConfiguration.createDefault(context));
90 //        ImageLoader.getInstance().displayImage(url, (ImageView) getView(viewId));
91         return this;
92     }
93 }

同步從新同步至Github:https://github.com/nanchen2251/CommonAdapterListViewDemo

相關文章
相關標籤/搜索