listview<一> 自定义适配器

来源:互联网 发布:华为5a切换数据流量 编辑:程序博客网 时间:2024/06/15 23:21

【正文】

【引入】

我们一般编写listView的时候顺序是这样的:

  • 需要展示的数据集List<T>
  • 为这个数据集编写一个ListView
  • 为这个ListView编写一个Adapter,一般继承自BaseAdapter
  • 在BaseAdapter内部编写一个ViewHolder类,对应ListView里面的item控件,提高控件的查询效率

分析:

List<T>:ListView --> Adapter extends BaseAdapter --> ViewHolder

一般情况下,一个ListView对应一个Adapter类,对应一个ViewHolder类,那如果一个app中有20个ListView,我们岂不是要写20遍?所以的做法是:

  • 抽取ViewHolder,作为公共的类。
  • 将Adapter封装成CommonAdapter,作为公共的类。

 

一、传统方式编写适配器:

(1)activity_main.xml:

复制代码
<RelativeLayout xmlns:android="http://schemas.android.com/apk/res/android"    xmlns:tools="http://schemas.android.com/tools"    android:layout_width="match_parent"    android:layout_height="match_parent"><ListView    android:id="@+id/listView"    android:layout_width="match_parent"    android:layout_height="match_parent"></ListView></RelativeLayout>
复制代码

(2)item_listview.xml:单个item的布局文件

复制代码
 1 <RelativeLayout xmlns:android="http://schemas.android.com/apk/res/android" 2     xmlns:tools="http://schemas.android.com/tools" 3     android:layout_width="match_parent" 4     android:layout_height="match_parent" 5     android:padding="10dp"> 6  7     <TextView 8         android:id="@+id/titleTv" 9         android:layout_width="wrap_content"10         android:layout_height="wrap_content"11         android:singleLine="true"12         android:text="Android新技能"13         android:textColor="#444"14         android:textSize="16sp" />15 16     <TextView17         android:id="@+id/descTv"18         android:layout_width="match_parent"19         android:layout_height="wrap_content"20         android:layout_below="@+id/titleTv"21         android:layout_marginTop="10dp"22         android:maxLines="2"23         android:minLines="1"24         android:text="Android为ListView和GridView打造万能适配器"25         android:textColor="#898989"26         android:textSize="16sp" />27 28     <TextView29         android:id="@+id/timeTv"30         android:paddingTop="3dp"31         android:layout_width="wrap_content"32         android:layout_height="wrap_content"33         android:layout_below="@+id/descTv"34         android:layout_marginTop="10dp"35         android:text="2015-05-04"36         android:textColor="#898989"37         android:textSize="12sp" />38 39     <TextView40         android:padding="2dp"41         android:id="@+id/phoneTv"42         android:gravity="center"43         android:layout_width="wrap_content"44         android:layout_height="wrap_content"45         android:layout_below="@+id/descTv"46         android:layout_marginTop="10dp"47         android:background="#2ED667"48         android:drawableLeft="@mipmap/phone"49         android:drawablePadding="5dp"50         android:text="10086"51         android:textColor="#ffffff"52         android:textSize="12sp"53         android:layout_alignParentRight="true" />54 55 56 </RelativeLayout>
复制代码

其对应的布局效果如下:

b7f05a6b-da8f-48d5-8058-5fa959eac13d 

(3)Bean.java:ListView的数据集

复制代码
 1 package com.smyhvae.baseadapter.entities; 2  3 /** 4  * Created by smyhvae on 2015/5/4. 5  */ 6 public class Bean { 7     private String title; 8     private String desc; 9     private String time;10     private String phone;11 12     public Bean() {13     }14 15     public Bean(String title, String desc, String time, String phone) {16         this.title = title;17         18         this.desc = desc;19         this.time = time;20         this.phone = phone;21     }22 23     public String getTitle() {24         return title;25     }26 27     public void setTitle(String title) {28         this.title = title;29     }30 31     public String getDesc() {32         return desc;33     }34 35     public void setDesc(String desc) {36         this.desc = desc;37     }38 39     public String getTime() {40         return time;41     }42 43     public void setTime(String time) {44         this.time = time;45     }46 47     public String getPhone() {48         return phone;49     }50 51     public void setPhone(String phone) {52         this.phone = phone;53     }54 }
复制代码

(4)MyAdapter.java:自定义适配器,继承自BaseAdapter

复制代码
 1 package com.smyhvae.baseadapter; 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.TextView; 9 10 import com.smyhvae.baseadapter.entities.Bean;11 12 import java.util.List;13 14 /**15  * Created by smyhvae on 2015/5/4.16  */17 public class MyAdapter extends BaseAdapter {18     private LayoutInflater mInflater;19     private List<Bean> mDatas;20 21     //MyAdapter需要一个Context,通过Context获得Layout.inflater,然后通过inflater加载item的布局22     public MyAdapter(Context context, List<Bean> datas) {23 24         mInflater = LayoutInflater.from(context);25         mDatas = datas;26     }27 28     //返回数据集的长度29     @Override30     public int getCount() {31         return mDatas.size();32     }33 34     @Override35     public Object getItem(int position) {36         return mDatas.get(position);37     }38 39     @Override40     public long getItemId(int position) {41         return position;42     }43 44     //这个方法才是重点,我们要为它编写一个ViewHolder45     @Override46     public View getView(int position, View convertView, ViewGroup parent) {47         ViewHolder holder = null;48         if (convertView == null) {49             convertView = mInflater.inflate(R.layout.item_listview, parent, false); //加载布局50             holder = new ViewHolder();51 52             holder.titleTv = (TextView) convertView.findViewById(R.id.titleTv);53             holder.descTv = (TextView) convertView.findViewById(R.id.descTv);54             holder.timeTv = (TextView) convertView.findViewById(R.id.timeTv);55             holder.phoneTv = (TextView) convertView.findViewById(R.id.phoneTv);56 57             convertView.setTag(holder);58         } else {   //else里面说明,convertView已经被复用了,说明convertView中已经设置过tag了,即holder59             holder = (ViewHolder) convertView.getTag();60         }61 62         Bean bean = mDatas.get(position);63         holder.titleTv.setText(bean.getTitle());64         holder.descTv.setText(bean.getDesc());65         holder.timeTv.setText(bean.getTime());66         holder.phoneTv.setText(bean.getPhone());67 68         return convertView;69     }70 71     //这个ViewHolder只能服务于当前这个特定的adapter,因为ViewHolder里会指定item的控件,不同的ListView,item可能不同,所以ViewHolder写成一个私有的类72     private class ViewHolder {73         TextView titleTv;74         TextView descTv;75         TextView timeTv;76         TextView phoneTv;77     }78 79 }
复制代码

(5)MainActivity.java:

复制代码
 1 package com.smyhvae.baseadapter; 2  3 import android.app.Activity; 4 import android.os.Bundle; 5 import android.view.Menu; 6 import android.view.MenuItem; 7 import android.widget.ListView; 8  9 import com.smyhvae.baseadapter.entities.Bean;10 11 import java.util.ArrayList;12 import java.util.List;13 14 public class MainActivity extends Activity {15 16     private ListView listView;17     private List<Bean> mDatas;18     private MyAdapter mAdapter;19 20     @Override21     protected void onCreate(Bundle savedInstanceState) {22         super.onCreate(savedInstanceState);23         setContentView(R.layout.activity_main);24 25         initView();26         initData();27 28     }29 30     //方法:初始化View31     private void initView() {32         listView = (ListView) findViewById(R.id.listView);33     }34 35     //方法;初始化Data36     private void initData() {37         mDatas = new ArrayList<Bean>();38 39         //将数据装到集合中去40         Bean bean = new Bean("Android新技能1", "Android为ListView和GridView打造万能适配器", "2015-05-04", "10086");41         mDatas.add(bean);42 43         bean = new Bean("Android新技能2", "Android为ListView和GridView打造万能适配器", "2015-05-04", "10086");44         mDatas.add(bean);45 46         bean = new Bean("Android新技能3", "Android为ListView和GridView打造万能适配器", "2015-05-04", "10086");47         mDatas.add(bean);48 49         bean = new Bean("Android新技能4", "Android为ListView和GridView打造万能适配器", "2015-05-04", "10086");50         mDatas.add(bean);51 52         //为数据绑定适配器53         mAdapter = new MyAdapter(this,mDatas);54 55         listView.setAdapter(mAdapter);56     }57 58 }
复制代码

运行效果如下:

448a2ee2-df07-4699-a510-b8bc1fcf2a95

【工程文件】

2015-05-04-BaseAdapter的传统写法.rar

 

二、ListView中自定义adapter的封装(万能的写法来编写适配器):

完整版代码如下:

(1)activity_main.xml:

复制代码
<RelativeLayout xmlns:android="http://schemas.android.com/apk/res/android"    xmlns:tools="http://schemas.android.com/tools"    android:layout_width="match_parent"    android:layout_height="match_parent"><ListView    android:id="@+id/listView"    android:layout_width="match_parent"    android:layout_height="match_parent"></ListView></RelativeLayout>
复制代码

(2)item_listview.xml.xml:(ListView中单个item的布局)

复制代码
 1 <RelativeLayout xmlns:android="http://schemas.android.com/apk/res/android" 2     xmlns:tools="http://schemas.android.com/tools" 3     android:layout_width="match_parent" 4     android:layout_height="match_parent" 5     android:padding="10dp"> 6  7     <TextView 8         android:id="@+id/titleTv" 9         android:layout_width="wrap_content"10         android:layout_height="wrap_content"11         android:singleLine="true"12         android:text="Android新技能"13         android:textColor="#444"14         android:textSize="16sp" />15 16     <TextView17         android:id="@+id/descTv"18         android:layout_width="match_parent"19         android:layout_height="wrap_content"20         android:layout_below="@+id/titleTv"21         android:layout_marginTop="10dp"22         android:maxLines="2"23         android:minLines="1"24         android:text="Android为ListView和GridView打造万能适配器"25         android:textColor="#898989"26         android:textSize="16sp" />27 28     <TextView29         android:id="@+id/timeTv"30         android:paddingTop="3dp"31         android:layout_width="wrap_content"32         android:layout_height="wrap_content"33         android:layout_below="@+id/descTv"34         android:layout_marginTop="10dp"35         android:text="2015-05-04"36         android:textColor="#898989"37         android:textSize="12sp" />38 39     <TextView40         android:padding="2dp"41         android:id="@+id/phoneTv"42         android:gravity="center"43         android:layout_width="wrap_content"44         android:layout_height="wrap_content"45         android:layout_below="@+id/descTv"46         android:layout_marginTop="10dp"47         android:background="#2ED667"48         android:drawableLeft="@mipmap/phone"49         android:drawablePadding="5dp"50         android:text="10086"51         android:textColor="#ffffff"52         android:textSize="12sp"53         android:layout_alignParentRight="true" />54 55 56 </RelativeLayout>
复制代码

其对应的布局效果如下:

b7f05a6b-da8f-48d5-8058-5fa959eac13d[1]

(3)Bean.java:数据集

复制代码
 1 package com.smyhvae.baseadapter.entities; 2  3 /** 4  * Created by smyhvae on 2015/5/4. 5  */ 6 public class Bean { 7     private String title; 8     private String desc; 9     private String time;10     private String phone;11 12     public Bean() {13     }14 15     public Bean(String title, String desc, String time, String phone) {16         this.title = title;17 18         this.desc = desc;19         this.time = time;20         this.phone = phone;21     }22 23     public String getTitle() {24         return title;25     }26 27     public void setTitle(String title) {28         this.title = title;29     }30 31     public String getDesc() {32         return desc;33     }34 35     public void setDesc(String desc) {36         this.desc = desc;37     }38 39     public String getTime() {40         return time;41     }42 43     public void setTime(String time) {44         this.time = time;45     }46 47     public String getPhone() {48         return phone;49     }50 51     public void setPhone(String phone) {52         this.phone = phone;53     }54 }
复制代码

(4)【可复用的代码】ViewHolder.java:

复制代码
 1 package com.smyhvae.baseadapter.utils; 2  3 import android.content.Context; 4 import android.util.SparseArray; 5 import android.view.LayoutInflater; 6 import android.view.View; 7 import android.view.ViewGroup; 8  9 /**10  * Created by smyhvae on 2015/5/4.11  */12 public class ViewHolder {13 14     private SparseArray<View> mViews;15     private int mPosition;16     private View mConvertView;17 18     public ViewHolder(Context context, ViewGroup parent, int layoutId, int position) {19         this.mPosition = position;20         this.mViews = new SparseArray<View>();21 22         mConvertView = LayoutInflater.from(context).inflate(layoutId, parent, false);23 24         mConvertView.setTag(this);25 26     }27 28     public static ViewHolder get(Context context, View convertView, ViewGroup parent, int layoutId, int position) {29         if (convertView == null) {30             return new ViewHolder(context, parent, layoutId, position);31         } else {32             ViewHolder holder = (ViewHolder) convertView.getTag();33             holder.mPosition = position; //即使ViewHolder是复用的,但是position记得更新一下34             return holder;35         }36     }37 38     /*39     通过viewId获取控件40      */41     //使用的是泛型T,返回的是View的子类42     public <T extends View> T getView(int viewId) {43         View view = mViews.get(viewId);44 45         if (view == null) {46             view = mConvertView.findViewById(viewId);47             mViews.put(viewId, view);48         }49 50         return (T) view;51     }52 53     public View getConvertView() {54         return mConvertView;55     }56 57 }
复制代码

(5)【可复用的代码】ListViewAdapter.java:自定义的通用适配器,继承自BaseAdapter。以后如果是自定义ListView的adapter,继承它就行了

复制代码
 1 package com.smyhvae.baseadapter.utils; 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  * Created by smyhvae on 2015/5/4.13  * 通用的ListView的BaseAdapter,所有的ListView的自定义adapter都可以继承这个类哦14  */15 public abstract class ListViewAdapter<T> extends BaseAdapter {16 17     //为了让子类访问,于是将属性设置为protected18     protected Context mContext;19     protected List<T> mDatas;20     protected LayoutInflater mInflater;21     private int layoutId; //不同的ListView的item布局肯能不同,所以要把布局单独提取出来22 23     public ListViewAdapter(Context context, List<T> datas, int layoutId) {24         this.mContext = context;25         mInflater = LayoutInflater.from(context);26         this.mDatas = datas;27         this.layoutId = layoutId;28     }29 30     @Override31     public int getCount() {32         return mDatas.size();33     }34 35     @Override36     public T getItem(int position) {37         return mDatas.get(position);38     }39 40     @Override41     public long getItemId(int position) {42         return position;43     }44 45     @Override46     public View getView(int position, View convertView, ViewGroup parent) {47         //初始化ViewHolder,使用通用的ViewHolder,一行代码就搞定ViewHolder的初始化咯48         ViewHolder holder = ViewHolder.get(mContext, convertView, parent, layoutId, position);//layoutId就是单个item的布局49 50         convert(holder, getItem(position));51         return holder.getConvertView(); //这一行的代码要注意了52     }53 54     //将convert方法公布出去55     public abstract void convert(ViewHolder holder, T t);56 57 }
复制代码

(6)ListViewAdapterWithViewHolder.java:继承自ListViewAdapter

复制代码
 1 package com.smyhvae.baseadapter; 2  3 import android.content.Context; 4 import android.widget.TextView; 5  6 import com.smyhvae.baseadapter.entities.Bean; 7 import com.smyhvae.baseadapter.utils.ListViewAdapter; 8 import com.smyhvae.baseadapter.utils.ViewHolder; 9 10 import java.util.List;11 12 /**13  * Created by smyhvae on 2015/5/4.14  */15 public class ListViewAdapterWithViewHolder extends ListViewAdapter<Bean> {16 17     //MyAdapter需要一个Context,通过Context获得Layout.inflater,然后通过inflater加载item的布局18     public ListViewAdapterWithViewHolder(Context context, List<Bean> datas) {19         super(context, datas, R.layout.item_listview);20     }21 22     @Override23     public void convert(ViewHolder holder, Bean bean) {24 25         ((TextView) holder.getView(R.id.titleTv)).setText(bean.getTitle());26         ((TextView) holder.getView(R.id.descTv)).setText(bean.getDesc());27         ((TextView) holder.getView(R.id.timeTv)).setText(bean.getTime());28         ((TextView) holder.getView(R.id.phoneTv)).setText(bean.getPhone());29 30 /*31         TextView tv = holder.getView(R.id.titleTv);32         tv.setText(...);33 34        ImageView view = getView(viewId);35        Imageloader.getInstance().loadImag(view.url);36 */37     }38 }
复制代码

(7)MainActivity.java:

复制代码
 1 package com.smyhvae.baseadapter; 2  3 import android.app.Activity; 4 import android.os.Bundle; 5 import android.widget.ListView; 6  7 import com.smyhvae.baseadapter.entities.Bean; 8  9 import java.util.ArrayList;10 import java.util.List;11 12 public class MainActivity extends Activity {13 14     private ListView listView;15     private List<Bean> mDatas;16 17     private ListViewAdapterWithViewHolder listViewAdapterWithViewHolder;18 19     @Override20     protected void onCreate(Bundle savedInstanceState) {21         super.onCreate(savedInstanceState);22         setContentView(R.layout.activity_main);23 24         initView();25         initData();26     }27 28 29     //方法:初始化View30     private void initView() {31         listView = (ListView) findViewById(R.id.listView);32     }33 34     //方法;初始化Data35     private void initData() {36         mDatas = new ArrayList<Bean>();37 38         //将数据装到集合中去39         Bean bean = new Bean("Android新技能1", "Android为ListView和GridView打造万能适配器", "2015-05-04", "10086");40         mDatas.add(bean);41 42         bean = new Bean("Android新技能2", "Android为ListView和GridView打造万能适配器", "2015-05-04", "10086");43         mDatas.add(bean);44 45         bean = new Bean("Android新技能3", "Android为ListView和GridView打造万能适配器", "2015-05-04", "10086");46         mDatas.add(bean);47 48         bean = new Bean("Android新技能4", "Android为ListView和GridView打造万能适配器", "2015-05-04", "10086");49         mDatas.add(bean);50 51         //为数据绑定适配器52         listViewAdapterWithViewHolder = new ListViewAdapterWithViewHolder(this, mDatas);53 54         listView.setAdapter(listViewAdapterWithViewHolder);55 56     }57 } 
复制代码

运行效果:

62fa7b24-4db6-4d6b-8540-6f427c261508

这样的话,以后每写个ListView,就这么做:直接导入ViewHolder.java和ListViewAdapter,然后写一个自定义adapter继承自ListViewAdapter就行了。

【工程文件】

2015-05-04-BaseAdapter的封装.rar

 

三、常见问题:

1、item控件抢占焦点:

假设item里有一个checkbox,那运行程序之后,发现只有checkBox能被点击,而item中的其他位置不能被点击(包括点击整个item也没有反应),这是由于checkbox抢占了整个item的焦点。办法是::

办法1:为该checkBox设置属性:

android:focusable = "false"

办法二:为该item设置属性:

android:descendantFocusability = "blocksDescendants" 

不让这个item的焦点从上往下传。

2、ListView复用导致内容错乱。


























本文转自:http://www.cnblogs.com/smyhvae/p/4477079.html



原创粉丝点击