在Android开发中,我们经常会用到ListView 这个组件,为了将ListView 的内容展示出来,我们会去实现一个Adapter来适配,将Layout中的布局以列表的形式展现到组件中。

    比如,像 GGTalk 安卓版的查找用户功能,会把符合条件的用户都列在下面:

为了达到这个效果,我们需要实现一个自定义的Adapter,而其它地方的ListView也要实现一个Adapter,这些Adapter会有很多重复的代码,非常繁琐,现在我就将重复代码封装到了一个通用的适配器CommonAdapter中,这样,在使用时只要继承CommonAdapter就可以了,如此就避免了大段代码的重复,让代码更简洁易懂。我们先来看看CommonAdapter的定义。  

一.CommonAdapter 实现

public abstract class CommonAdapter<T> extends BaseAdapter {

    private List<T> dataList;
protected Context context;
protected int item_layoutId=0;
protected HashMap<Integer,Integer> layoutIdMap; //多种itemView时用到。 第一个int对应type类型,第二个int对应 itemlayoutId /**
* 设置单个子模版VIew,
* @param context
* @param datas 绑定的对象集合
* @param item_layoutId 被绑定的视图layoutID
* */
public CommonAdapter(Context context, List<T> datas, int item_layoutId)
{
this.context=context;
this.dataList=datas;
this.item_layoutId=item_layoutId;
} /**
*设置多个子模版VIew, 并配合重写 getItemViewType(int position)来确定是设置哪个模版
* @param context
* @param datas 绑定的对象集合
* @param layoutIdMap 多种itemViewID Map 第一个int对应type类型,第二个int对应 itemlayoutId
*/
public CommonAdapter(Context context, List<T> datas, HashMap<Integer,Integer> layoutIdMap)
{
this.context=context;
this.dataList=datas;
this.layoutIdMap=layoutIdMap;
} @Override
public int getViewTypeCount() {
if(this.layoutIdMap==null)
{
return 1;
}
return this.layoutIdMap.size();
} @Override
public int getCount() {
return this.dataList.size();
} @Override
public Object getItem(int position) {
return this.dataList.get(position);
} @Override
public long getItemId(int position) {
return position;
} @Override
public View getView(int position, View convertView, ViewGroup parent) {
int type = getItemViewType(position);
return getConvertView(position,convertView,type);
} private View getConvertView(int position,View convertView,int itemViewType)
{
if(convertView==null)
{
int layoutId =0;
if(this.item_layoutId!=0)
{
layoutId=this.item_layoutId;
}
if (this.layoutIdMap != null) {
layoutId = this.layoutIdMap.get(itemViewType);
}
convertView = LayoutInflater.from(context).inflate(layoutId, null);
}
T t = this.dataList.get(position);
this.setConvertView(convertView,t,itemViewType);
return convertView;
} /**
* 局部更新数据,调用一次getView()方法;Google推荐的做法
*
* @param parent 要更新的listview
* @param position 要更新的位置
*/
public void onOneItemChanged(ListView parent, int position) {
/**第一个可见的位置**/
int firstVisiblePosition = parent.getFirstVisiblePosition();
/**最后一个可见的位置**/
int lastVisiblePosition = parent.getLastVisiblePosition(); /**在看见范围内才更新,不可见的滑动后自动会调用getView方法更新**/
if ((position >= firstVisiblePosition && position <= lastVisiblePosition)) { /**获取指定位置view对象**/
View view = parent.getChildAt(position - firstVisiblePosition);
getView(position, view, parent);
}
} /**
* 需要去实现的对item中的view的设置操作
*
* @param convertView 转换后的试图
* @param t Model对象
* @param itemViewType 试图类型。对应layoutIdMap中的key
*/
protected abstract void setConvertView(View convertView, T t,int itemViewType);
}

说明如下:

(1)泛型参数<T>就是我们要绑定数据Model对象的class类型。

(2)getViewTypeCount()方法会根据HashMap是否为空来识别是传入的单一模版还是多模版.

(3)CommonAdapter还重写了getCount(),getItem(int position),getView,getConvertView等BaseAdapter 的方法,这些方法的代码都基本每个Adapter都是一样的,我们只需关心 setConvertView 这个抽象方法,在子类中重写它,将具体的逻辑和数据的绑定在此即可。

二.使用CommonAdapter

  1. 先建一个类继承CommonAdapter,在构造函数中调用父类的构造方法以初始化数据。实现 setConvertView 方法,将对象的数据绑定到模版上 即可

    public class SearchFriendAdapter extends CommonAdapter<OrayUser> {
       /**
    * 设置单个子模版VIew,
    * @param context
    * @param datas 绑定的对象集合
    * @param item_layoutId 被绑定的视图layoutID
    * */
    public SearchFriendAdapter(Context context,List<OrayUser> datas,int item_layoutId)
    {
    super(context,datas,item_layoutId);
    }
      @Override
      protected void setConvertView(View view, OrayUser orayUser,int itemViewType)
      {
        ...具体将对象的数据绑定到模版上
      }
    } 
  2. Activity中使用时new 一个Adapter,然后将adapter绑定到 ListView
    adapter = new SearchFriendAdapter(this,this.userList,R.layout.friend_child);
    listView.setAdapter(adapter);
  3. 更新数据源中的数据同步到View
    //有2种方式将数据源同步到View
    adapter.notifyDataSetChanged();//全部刷新
    adapter.onOneItemChanged(ListView parent, int position);//刷新指定位置的数据 

三. 让CommonAdapter支持多模板

当然CommonAdapter还支持多模版的应用,我们只需将第一步中的Adapter实现稍稍做改动即可

public class ListViewAdapter extends CommonAdapter<ListViewItemModel> {
/**
*设置多个子模版VIew, 并配合重写 getItemViewType(int position)来确定是设置哪个模版
* @param context
* @param datas 绑定的对象集合
* @param layoutIdMap 多种itemViewID Map 第一个int对应type类型,第二个int对应 itemlayoutId
*/
public ListViewAdapter(Context context, List<ListViewItemModel> datas, HashMap<Integer,Integer> layoutIdMap)
{
super(context,datas,layoutIdMap);
}
@Override
public int getItemViewType(int position)
{
ListViewItemModel model= (ListViewItemModel)super.getItem(position);
if(model.floatRight)
{
return ;
}
return ;
}
@Override
protected void setConvertView(View view, ListViewItemModel listViewItemModel,int itemViewType) {
//根据itemViewType 的值来识别是哪个模版,以对应给模版绑定数据  
}
}

如上图我们将构造函数的最后一个参数从单一的layoutID 换成了 模版集合的HashMap,再就是多加了 getItemViewType(int position) 方法来返回 具体Model对应的是哪一个模版类型(即hashMap 中的key值),使用时更新数据源中的数据同步到View和上面的单一模版一样使用。(同上2种更新方法)

        HashMap<Integer,Integer> layoutMap=new HashMap<>();
layoutMap.put(0,R.layout.listview_item);//key值最好从0开始向上递增,否则可能会找不到key的BUG
layoutMap.put(1,R.layout.listview_right_item);
adapter=new ListViewAdapter(this,models,layoutMap);
listView.setAdapter(adapter);

四.综合实例

最后我们还是以本文开头的查找用户的例子,来更全面地说明CommonAdapter的使用。页面截图如下:

 1.模版布局

<?xml version="1.0" encoding="utf-8"?>
<RelativeLayout xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:app="http://schemas.android.com/apk/res-auto"
android:id="@+id/layout"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:background="@drawable/selector_item"
android:descendantFocusability="blocksDescendants"
android:gravity="center_vertical"
android:orientation="horizontal"
android:padding="4dp"> <ImageView
android:id="@+id/ct_photo"
android:layout_width="@dimen/headImageSize"
android:layout_height="@dimen/headImageSize"
android:layout_margin="5dp"
android:src="@drawable/plus"
/> <ImageView
android:id="@+id/ct_status"
android:layout_width="11dip"
android:layout_height="11dip"
android:layout_marginLeft="@dimen/headImageSize"
android:layout_marginTop="@dimen/headImageSize"
android:src='@drawable/status_2' /> <LinearLayout
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:gravity="center_vertical"
android:layout_alignTop="@id/ct_photo"
android:layout_toRightOf="@id/ct_photo"
> <TextView
android:id="@+id/ct_name"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:paddingLeft="5dp"
android:paddingRight="5dp"
android:paddingBottom="5dp"
android:paddingTop="0dp"
android:text="name"
android:textColor="@color/dimgrey"
android:textSize="16sp"
android:textStyle="bold" /> <TextView
android:id="@+id/ct_describe"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_marginLeft="10dp"
android:layout_toRightOf="@id/ct_name"
android:paddingLeft="5dp"
android:paddingRight="5dp"
android:paddingBottom="5dp"
android:paddingTop="0dp"
android:text="describe"
android:textColor="@color/dimgrey"
android:textSize="14sp"
android:visibility="gone" />
</LinearLayout>
<TextView
android:id="@+id/ct_sign"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_alignBottom="@id/ct_photo"
android:layout_toRightOf="@id/ct_photo"
android:paddingLeft="5dp"
android:text="sign"
android:singleLine="true"
android:ellipsize="start"
android:textColor="@color/dimgrey"
android:textSize="12sp" /> </RelativeLayout>

2.具体继承CommonAdapter实现子类

public class SearchFriendAdapter extends CommonAdapter<OrayUser> {
public SearchFriendAdapter(Context context,List<OrayUser> datas,int item_layoutId)
{
super(context,datas,item_layoutId);
} @Override
protected void setConvertView(View view, OrayUser orayUser,int itemViewType) {
try{
SearchFriendAdapter.ViewHolder holder;
if(view.getTag()==null)
{
holder = new SearchFriendAdapter.ViewHolder(view);
view.setTag(holder);
}
else {
holder = (SearchFriendAdapter.ViewHolder) view.getTag();
}
HeadImgHelper.setUserHeadImg(holder.headImg,orayUser);
if(orayUser.getName().equals(orayUser.getCommentName()))
{
holder.userName.setText(orayUser.getName());
}
else {
holder.userName.setText(orayUser.getCommentName()+"("+ orayUser.getName()+")");
}
String catalogName= ClientGlobalCache.getInstance().getCurrentUser().getCatalogNameByFriend(orayUser.getUserID());
if(!StringHelper.isNullOrEmpty(catalogName))
{
holder.describe.setText("[ "+ catalogName+" ]");
holder.describe.setVisibility(View.VISIBLE);
}
else
{
holder.describe.setText("");
holder.describe.setVisibility(View.GONE);
}
holder.orgName.setText(orayUser.getUserID());
}catch (Exception ex){ex.printStackTrace();}
} private class ViewHolder
{
public ImageView headImg;
public TextView userName;
public TextView describe;
public TextView orgName; public ViewHolder(View view)
{
this.headImg = (ImageView) view.findViewById(R.id.ct_photo);
ImageView statusImg=(ImageView) view.findViewById(R.id.ct_status);
statusImg.setVisibility(View.GONE);
this.userName=(TextView) view.findViewById(R.id.ct_name);
this.orgName=(TextView) view.findViewById(R.id.ct_sign);
this.describe=(TextView) view.findViewById(R.id.ct_describe);
}
}
}

3. Activity类 核心代码

public class SearchFriendActivity extends Activity implements TextView.OnEditorActionListener{
private DrawableEditText input;
private ListView listView;
private TextView search_resultStr;
private List<OrayUser> userList=new ArrayList<OrayUser>() ;
private SearchFriendAdapter adapter; @Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_search_friend);
this.initView();
} private void initView()
{
TextView pageTitle=(TextView)findViewById(R.id.headerText);
pageTitle.setText("查找用户");
search_resultStr =(TextView) findViewById(R.id.search_resultStr);
listView=(ListView)findViewById(R.id.listview_user);
listView.setOnItemClickListener(this);
adapter = new SearchFriendAdapter(this,this.userList,R.layout.friend_child);
listView.setAdapter(adapter); } /**
* 执行点击搜索指令
* */
private void action_search(String idOrName)
{
List<OrayUser> temp_users=Manager.getInstance().doSearchUser(idOrName);
search_resultStr.setText("没有找到符合条件的用户或群组");
this.setSearchResult(temp_users);
} private void setSearchResult(List<OrayUser> temp_users)
{
this.userList.clear();
if(temp_users==null||temp_users.size()==)
{
search_resultStr.setVisibility(View.VISIBLE);
}
else
{
for (OrayUser orayUser :temp_users) {
if(orayUser.getUserID().equals(ClientGlobalCache.getInstance().getCurrentUser().getUserID()))
{
temp_users.remove(orayUser);
break;
}
}
this.userList.addAll(temp_users) ;
search_resultStr.setVisibility(View.GONE);
}
this.adapter.notifyDataSetChanged();
}
}

Android开发进阶 -- 通用适配器 CommonAdapter的更多相关文章

  1. 《android开发进阶从小工到专家》读书笔记--HTTP网络请求

    No1: 客户端与服务器的交互流程: 1)客户端执行网络请求,从URL中解析出服务器的主机名 2)将服务器的主机名转换成服务器的IP地址 3)将端口号从URL中解析出来 4)建立一条从客户端与Web服 ...

  2. 推荐扔物线的HenCoder Android 开发进阶系列 后期接着更新

    官网地址:http://hencoder.com/ 我来做一次辛勤的搬运工 HenCoder:给高级 Android 工程师的进阶手册 HenCoder Android 开发进阶: 自定义 View ...

  3. android开发进阶学习博客资源

    Android开发者博客推荐 Android入门级 - 罗宪明 http://blog.csdn.net/wdaming1986 Android入门级 - 魏祝林 http://blog.csdn.n ...

  4. Android开发进阶:如何读写Android文件

    Android主要有四大主要组件组成:Activity.ContentProvider.Service.Intent组成.Android文件的运行主要需要读写四大组件的文件.本文将介绍如何读写Andr ...

  5. Androidproject师进阶之路 :《Android开发进阶:从小工到专家》上市啦!

    封面 文件夹1 文件夹2 - 当当购买链接 - 京东购买链接 为什么写这本书 写这本书的念头由来已久了. 或许是从我打算写<Android源代码设计模式解析与实战>那时起就萌生了这个念头, ...

  6. Android开发进阶——自定义View的使用及其原理探索

    在Android开发中,系统提供给我们的UI控件是有限的,当我们需要使用一些特殊的控件的时候,只靠系统提供的控件,可能无法达到我们想要的效果,这时,就需要我们自定义一些控件,来完成我们想要的效果了.下 ...

  7. Android开发之万能适配器

    ListView.GridView等等非常多的东西都需要适配器.而如果开发一个app每一个listview都有写一个Adapter的话,那还怎么愉快的玩游戏.. 什么是ViewHolider以及的用法 ...

  8. android 开发进阶自定义控件 类似 TextView

    开发自定义控件的步骤: 1. 继承View: 2.重写构造函数并构造方法中获得我们自定义的属性. 3. 重写onDraw, 4.重写onMeasure 等函数 一.自定义View的属性,首先在res/ ...

  9. android 开发进阶 自定义控件-仿ios自动清除控件

    先上图: 开发中经常需要自定义view控件或者组合控件,某些控件可能需要一些额外的配置.比如自定义一个标题栏,你可能需要根据不同尺寸的手机定制不同长度的标题栏,或者更常见的你需要配置标题栏的背景,这时 ...

随机推荐

  1. Angular开发者指南(七)依赖注入

    依赖注入 依赖注入(DI)是一种软件设计模式,处理组件如何获取其依赖关系. AngularJS注入器子系统负责创建组件,解析它们的依赖关系,并根据请求将它们提供给其他组件. 使用依赖注入 DI遍布An ...

  2. 重复测量的方差分析|Mauchly's Test of Sphericity|

    生物统计学-重复测量的方差分析 之前的方差分析应用条件要求组之间是独立的,即某种因素下相同时段测量的结果数据,但4月与5月数据是有关系的,所以必须考虑某种因素下不同时段测量的结果数据,即使用重复测量的 ...

  3. python3之scrapy数据存储问题(MySQL)

    这次我用的是python3.6,scrapy在python2.7,3.5的使用方法都不同所以要特别注意, 列如 在python3.5的开发环境下scrapy 的主爬虫文件可以使用 from urlli ...

  4. HDU-1164-Eddy's research I(分解质因数)

    由于这道题目数据范围小,所以属于水题.可以采取暴力的做法来解决. 代码如下: #include"bits/stdc++.h" using namespace std; ; ]; v ...

  5. docker 使用:创建nginx容器

    在上一节中了解了镜像和容器.对于镜像可以这样的理解,镜像相当于一个光盘,里面刻录了一个系统这个系统已经带有相关的服务了. 容器是通过镜像这个光盘安装的一个操作系统,光盘预加了什么服务,容器就有什么服务 ...

  6. Fence和非原子操作的ordering

    除了在原子操作中标记memory ordering外,还可以单独使用fence指定memory ordering.Fence是全局的操作,它影响所执行线程中其他原子操作的ordering. 12345 ...

  7. HTTP的响应码?

    响应头对浏览器来说很重要,它说明了响应的真正含义.例如200表示响应成功了,302表示重定向,这说明浏览器需要再发一个新的请求. l 200:请求成功,浏览器会把响应体内容(通常是html)显示在浏览 ...

  8. QQ公众号出炉 同门相争意欲何为

    同门相争意欲何为"> 当初腾讯大张旗鼓地推出微信时,很多业内人士都认为其与QQ在功能.用户等方面多有重叠,肯定会阻碍QQ的发展和微信的成长.没想到,二者避重就轻地在不同的侧重点发展,反 ...

  9. spring入门(14)

    AOP是一个新的专题,基础部分主要是入门 后续的五.六.七都属于AOP专题: 所以有必要对这三章要学什么有个全局的认识. 1 概要 1 什么是AOP及实现方式 介绍了AOP的用途,以及大致的实现方案 ...

  10. 初识Machine Learning

    What is Machine Learning 定义 Arthur Samuel:Field of study that gives computers the ability to learn w ...