我們一向寫的自定義適配器,無非就是繼承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>
完事,有問題留言交流~