若是你去作任何一個項目,我相信你都會跟我有同樣的經歷,最最廣泛的就是列表顯示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