Android开发中ListView自定义adapter的封装
【引入】
我们一般编写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的布局文件
<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" android:padding="10dp"> <textview android:id="@+id/titletv" android:layout_width="wrap_content" android:layout_height="wrap_content" android:singleline="true" android:text="android新技能" android:textcolor="#444" android:textsize="16sp" /> <textview android:id="@+id/desctv" android:layout_width="match_parent" android:layout_height="wrap_content" android:layout_below="@+id/titletv" android:layout_margintop="10dp" android:maxlines="2" android:minlines="1" android:text="android为listview和gridview打造万能适配器" android:textcolor="#898989" android:textsize="16sp" /> <textview android:id="@+id/timetv" android:paddingtop="3dp" android:layout_width="wrap_content" android:layout_height="wrap_content" android:layout_below="@+id/desctv" android:layout_margintop="10dp" android:text="2015-05-04" android:textcolor="#898989" android:textsize="12sp" /> <textview android:padding="2dp" android:id="@+id/phonetv" android:gravity="center" android:layout_width="wrap_content" android:layout_height="wrap_content" android:layout_below="@+id/desctv" android:layout_margintop="10dp" android:background="#2ed667" android:drawableleft="@mipmap/phone" android:drawablepadding="5dp" android:text="10086" android:textcolor="#ffffff" android:textsize="12sp" android:layout_alignparentright="true" /> </relativelayout>
其对应的布局效果如下:
(3)bean.java:listview的数据集
package com.smyhvae.baseadapter.entities; /** * created by smyhvae on 2015/5/4. */ public class bean { private string title; private string desc; private string time; private string phone; public bean() { } public bean(string title, string desc, string time, string phone) { this.title = title; this.desc = desc; this.time = time; this.phone = phone; } public string gettitle() { return title; } public void settitle(string title) { this.title = title; } public string getdesc() { return desc; } public void setdesc(string desc) { this.desc = desc; } public string gettime() { return time; } public void settime(string time) { this.time = time; } public string getphone() { return phone; } public void setphone(string phone) { this.phone = phone; } }
(4)myadapter.java:自定义适配器,继承自baseadapter
package com.smyhvae.baseadapter; import android.content.context; import android.view.layoutinflater; import android.view.view; import android.view.viewgroup; import android.widget.baseadapter; import android.widget.textview; import com.smyhvae.baseadapter.entities.bean; import java.util.list; /** * created by smyhvae on 2015/5/4. */ public class myadapter extends baseadapter { private layoutinflater minflater; private list<bean> mdatas; //myadapter需要一个context,通过context获得layout.inflater,然后通过inflater加载item的布局 public myadapter(context context, list<bean> datas) { minflater = layoutinflater.from(context); mdatas = datas; } //返回数据集的长度 @override public int getcount() { return mdatas.size(); } @override public object getitem(int position) { return mdatas.get(position); } @override public long getitemid(int position) { return position; } //这个方法才是重点,我们要为它编写一个viewholder @override public view getview(int position, view convertview, viewgroup parent) { viewholder holder = null; if (convertview == null) { convertview = minflater.inflate(r.layout.item_listview, parent, false); //加载布局 holder = new viewholder(); holder.titletv = (textview) convertview.findviewbyid(r.id.titletv); holder.desctv = (textview) convertview.findviewbyid(r.id.desctv); holder.timetv = (textview) convertview.findviewbyid(r.id.timetv); holder.phonetv = (textview) convertview.findviewbyid(r.id.phonetv); convertview.settag(holder); } else { //else里面说明,convertview已经被复用了,说明convertview中已经设置过tag了,即holder holder = (viewholder) convertview.gettag(); } bean bean = mdatas.get(position); holder.titletv.settext(bean.gettitle()); holder.desctv.settext(bean.getdesc()); holder.timetv.settext(bean.gettime()); holder.phonetv.settext(bean.getphone()); return convertview; } //这个viewholder只能服务于当前这个特定的adapter,因为viewholder里会指定item的控件,不同的listview,item可能不同,所以viewholder写成一个私有的类 private class viewholder { textview titletv; textview desctv; textview timetv; textview phonetv; } }
(5)mainactivity.java:
package com.smyhvae.baseadapter; import android.app.activity; import android.os.bundle; import android.view.menu; import android.view.menuitem; import android.widget.listview; import com.smyhvae.baseadapter.entities.bean; import java.util.arraylist; import java.util.list; public class mainactivity extends activity { private listview listview; private list<bean> mdatas; private myadapter madapter; @override protected void oncreate(bundle savedinstancestate) { super.oncreate(savedinstancestate); setcontentview(r.layout.activity_main); initview(); initdata(); } //方法:初始化view private void initview() { listview = (listview) findviewbyid(r.id.listview); } //方法;初始化data private void initdata() { mdatas = new arraylist<bean>(); //将数据装到集合中去 bean bean = new bean("android新技能1", "android为listview和gridview打造万能适配器", "2015-05-04", "10086"); mdatas.add(bean); bean = new bean("android新技能2", "android为listview和gridview打造万能适配器", "2015-05-04", "10086"); mdatas.add(bean); bean = new bean("android新技能3", "android为listview和gridview打造万能适配器", "2015-05-04", "10086"); mdatas.add(bean); bean = new bean("android新技能4", "android为listview和gridview打造万能适配器", "2015-05-04", "10086"); mdatas.add(bean); //为数据绑定适配器 madapter = new myadapter(this,mdatas); listview.setadapter(madapter); } }
运行效果如下:
【工程文件】
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的布局)
<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" android:padding="10dp"> <textview android:id="@+id/titletv" android:layout_width="wrap_content" android:layout_height="wrap_content" android:singleline="true" android:text="android新技能" android:textcolor="#444" android:textsize="16sp" /> <textview android:id="@+id/desctv" android:layout_width="match_parent" android:layout_height="wrap_content" android:layout_below="@+id/titletv" android:layout_margintop="10dp" android:maxlines="2" android:minlines="1" android:text="android为listview和gridview打造万能适配器" android:textcolor="#898989" android:textsize="16sp" /> <textview android:id="@+id/timetv" android:paddingtop="3dp" android:layout_width="wrap_content" android:layout_height="wrap_content" android:layout_below="@+id/desctv" android:layout_margintop="10dp" android:text="2015-05-04" android:textcolor="#898989" android:textsize="12sp" /> <textview android:padding="2dp" android:id="@+id/phonetv" android:gravity="center" android:layout_width="wrap_content" android:layout_height="wrap_content" android:layout_below="@+id/desctv" android:layout_margintop="10dp" android:background="#2ed667" android:drawableleft="@mipmap/phone" android:drawablepadding="5dp" android:text="10086" android:textcolor="#ffffff" android:textsize="12sp" android:layout_alignparentright="true" /> </relativelayout>
其对应的布局效果如下:
(3)bean.java:数据集
package com.smyhvae.baseadapter.entities; /** * created by smyhvae on 2015/5/4. */ public class bean { private string title; private string desc; private string time; private string phone; public bean() { } public bean(string title, string desc, string time, string phone) { this.title = title; this.desc = desc; this.time = time; this.phone = phone; } public string gettitle() { return title; } public void settitle(string title) { this.title = title; } public string getdesc() { return desc; } public void setdesc(string desc) { this.desc = desc; } public string gettime() { return time; } public void settime(string time) { this.time = time; } public string getphone() { return phone; } public void setphone(string phone) { this.phone = phone; } }
(4)【可复用的代码】viewholder.java:
package com.smyhvae.baseadapter.utils; import android.content.context; import android.util.sparsearray; import android.view.layoutinflater; import android.view.view; import android.view.viewgroup; /** * created by smyhvae on 2015/5/4. */ public class viewholder { private sparsearray<view> mviews; private int mposition; private view mconvertview; public viewholder(context context, viewgroup parent, int layoutid, int position) { this.mposition = position; this.mviews = new sparsearray<view>(); mconvertview = layoutinflater.from(context).inflate(layoutid, parent, false); mconvertview.settag(this); } public static viewholder get(context context, view convertview, viewgroup parent, int layoutid, int position) { if (convertview == null) { return new viewholder(context, parent, layoutid, position); } else { viewholder holder = (viewholder) convertview.gettag(); holder.mposition = position; //即使viewholder是复用的,但是position记得更新一下 return holder; } } /* 通过viewid获取控件 */ //使用的是泛型t,返回的是view的子类 public <t extends view> t getview(int viewid) { view view = mviews.get(viewid); if (view == null) { view = mconvertview.findviewbyid(viewid); mviews.put(viewid, view); } return (t) view; } public view getconvertview() { return mconvertview; } }
(5)【可复用的代码】listviewadapter.java:自定义的通用适配器,继承自baseadapter。以后如果是自定义listview的adapter,继承它就行了
package com.smyhvae.baseadapter.utils; import android.content.context; import android.view.layoutinflater; import android.view.view; import android.view.viewgroup; import android.widget.baseadapter; import java.util.list; /** * created by smyhvae on 2015/5/4. * 通用的listview的baseadapter,所有的listview的自定义adapter都可以继承这个类哦 */ public abstract class listviewadapter<t> extends baseadapter { //为了让子类访问,于是将属性设置为protected protected context mcontext; protected list<t> mdatas; protected layoutinflater minflater; private int layoutid; //不同的listview的item布局肯能不同,所以要把布局单独提取出来 public listviewadapter(context context, list<t> datas, int layoutid) { this.mcontext = context; minflater = layoutinflater.from(context); this.mdatas = datas; this.layoutid = layoutid; } @override public int getcount() { return mdatas.size(); } @override public t getitem(int position) { return mdatas.get(position); } @override public long getitemid(int position) { return position; } @override public view getview(int position, view convertview, viewgroup parent) { //初始化viewholder,使用通用的viewholder,一行代码就搞定viewholder的初始化咯 viewholder holder = viewholder.get(mcontext, convertview, parent, layoutid, position);//layoutid就是单个item的布局 convert(holder, getitem(position)); return holder.getconvertview(); //这一行的代码要注意了 } //将convert方法公布出去 public abstract void convert(viewholder holder, t t); }
(6)listviewadapterwithviewholder.java:继承自listviewadapter
package com.smyhvae.baseadapter; import android.content.context; import android.widget.textview; import com.smyhvae.baseadapter.entities.bean; import com.smyhvae.baseadapter.utils.listviewadapter; import com.smyhvae.baseadapter.utils.viewholder; import java.util.list; /** * created by smyhvae on 2015/5/4. */ public class listviewadapterwithviewholder extends listviewadapter<bean> { //myadapter需要一个context,通过context获得layout.inflater,然后通过inflater加载item的布局 public listviewadapterwithviewholder(context context, list<bean> datas) { super(context, datas, r.layout.item_listview); } @override public void convert(viewholder holder, bean bean) { ((textview) holder.getview(r.id.titletv)).settext(bean.gettitle()); ((textview) holder.getview(r.id.desctv)).settext(bean.getdesc()); ((textview) holder.getview(r.id.timetv)).settext(bean.gettime()); ((textview) holder.getview(r.id.phonetv)).settext(bean.getphone()); /* textview tv = holder.getview(r.id.titletv); tv.settext(...); imageview view = getview(viewid); imageloader.getinstance().loadimag(view.url); */ } }
(7)mainactivity.java:
package com.smyhvae.baseadapter; import android.app.activity; import android.os.bundle; import android.widget.listview; import com.smyhvae.baseadapter.entities.bean; import java.util.arraylist; import java.util.list; public class mainactivity extends activity { private listview listview; private list<bean> mdatas; private listviewadapterwithviewholder listviewadapterwithviewholder; @override protected void oncreate(bundle savedinstancestate) { super.oncreate(savedinstancestate); setcontentview(r.layout.activity_main); initview(); initdata(); } //方法:初始化view private void initview() { listview = (listview) findviewbyid(r.id.listview); } //方法;初始化data private void initdata() { mdatas = new arraylist<bean>(); //将数据装到集合中去 bean bean = new bean("android新技能1", "android为listview和gridview打造万能适配器", "2015-05-04", "10086"); mdatas.add(bean); bean = new bean("android新技能2", "android为listview和gridview打造万能适配器", "2015-05-04", "10086"); mdatas.add(bean); bean = new bean("android新技能3", "android为listview和gridview打造万能适配器", "2015-05-04", "10086"); mdatas.add(bean); bean = new bean("android新技能4", "android为listview和gridview打造万能适配器", "2015-05-04", "10086"); mdatas.add(bean); //为数据绑定适配器 listviewadapterwithviewholder = new listviewadapterwithviewholder(this, mdatas); listview.setadapter(listviewadapterwithviewholder); } }
运行效果:
这样的话,以后每写个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"
办法2:为该item设置属性:android:descendantfocusability = "blocksdescendants"
不让这个item的焦点从上往下传。
2、listview复用导致内容错乱。
以上就是本文的全部内容,希望对大家的学习有所帮助,也希望大家多多支持。
下一篇: java微信红包实现算法
推荐阅读
-
Android开发中ListView自定义adapter的封装
-
Android开发中ListView自定义adapter的封装
-
Android应用开发中自定义ViewGroup的究极攻略
-
Android App开发中自定义View和ViewGroup的实例教程
-
Android应用开发中自定义ViewGroup视图容器的教程
-
Android应用开发中自定义ViewGroup的究极攻略
-
Android App开发中自定义View和ViewGroup的实例教程
-
Android应用开发中自定义ViewGroup视图容器的教程
-
实例讲解Android app开发中ListView的基本使用及优化
-
浅谈Android开发中ListView控件性能的一些优化方法