当前位置: 移动技术网 > 移动技术>移动开发>Android > Android开发中ListView自定义adapter的封装

Android开发中ListView自定义adapter的封装

2019年07月24日  | 移动技术网移动技术  | 我要评论

【引入】 

我们一般编写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);
 }

}

运行效果如下:

 

【工程文件】 

二、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就行了。 

【工程文件】

三、常见问题: 

1、item控件抢占焦点: 

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

办法1:为该checkbox设置属性:android:focusable = "false"
办法2:为该item设置属性:android:descendantfocusability = "blocksdescendants" 

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

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

以上就是本文的全部内容,希望对大家的学习有所帮助,也希望大家多多支持移动技术网。

如对本文有疑问, 点击进行留言回复!!

相关文章:

验证码:
移动技术网