欢迎您访问程序员文章站本站旨在为大家提供分享程序员计算机编程知识!
您现在的位置是: 首页  >  移动技术

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

程序员文章站 2024-03-06 20:55:20
【引入】  我们一般编写listview的时候顺序是这样的:  •需要展示的数据集list  R...

【引入】 

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

其对应的布局效果如下:

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

(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);
 }

}

运行效果如下:

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

【工程文件】 
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>

其对应的布局效果如下:

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

(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);

 }
}

运行效果:

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

这样的话,以后每写个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复用导致内容错乱。

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