android开发笔记之打造终极适配器

大家看到这个标题是不是觉得很诧异呢?什么叫终极适配器,其实就是这种适配器是万能的,所有需要使用适配器的组件,都可用这一个适配器就行。

既然这样,就来讲讲吧。

效果:

当然这是个简单的布局,用普通的适配器也可以实现,这里只是用它来做个例子,用终极适配器的话,以后你换其他布局,适配器是不用变的,减少了很多代码。

首先普通的适配器的写法是:

public class MyAdapter extends BaseAdapter{

    private Context mContext;
    private List<Bean> mDatas;
    private LayoutInflater mLayoutInflater;
    private int mResId;
    public MyAdapter(Context context,List<Bean> data, int resId) {
        mContext = context;
        mDatas = data;
        mLayoutInflater = LayoutInflater.from(mContext);
        mResId = resId;
    }
    @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;
    }
    @Override
    public View getView(int position, View convertView, ViewGroup parent) {
        Bean bean = mDatas.get(position);
        ViewHolder holder;
        if (convertView == null) {
            convertView= mLayoutInflater.inflate(R.layout.myitem, parent, false);
            holder = new ViewHolder();
            holder.title_Tv = (TextView) convertView.findViewById(R.id.title_Tv);
            holder.desc_Tv = (TextView) convertView.findViewById(R.id.desc_Tv);
            holder.time_Tv = (TextView) convertView.findViewById(R.id.time_Tv);
            holder.phone_Tv = (TextView) convertView.findViewById(R.id.phone_Tv);
            convertView.setTag(holder);
        }else {
            holder = (ViewHolder) convertView.getTag();
        }
        holder.title_Tv.setText(bean.getTitle());
        holder.desc_Tv.setText(bean.getDesc());
        holder.time_Tv.setText(bean.getTime());
        holder.phone_Tv.setText(bean.getPhone());
        return convertView;
    }
    static class ViewHolder {
        TextView title_Tv;        //标题
        TextView desc_Tv;         //简介
        TextView time_Tv;         //时间
        TextView phone_Tv;        //电话
    }
}

制作终极适配器的步骤:

首先我们得分析哪些代码是不变的,哪些是可变的,这样才能确定哪些代码能够减少。

这三个重写的方法应该是不变的。

@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 holder;
        if (convertView == null) {
            convertView= mLayoutInflater.inflate(R.layout.myitem, parent, false);
            holder = new ViewHolder();
            holder.title_Tv = (TextView) convertView.findViewById(R.id.title_Tv);
            holder.desc_Tv = (TextView) convertView.findViewById(R.id.desc_Tv);
            holder.time_Tv = (TextView) convertView.findViewById(R.id.time_Tv);
            holder.phone_Tv = (TextView) convertView.findViewById(R.id.phone_Tv);
            convertView.setTag(holder);
        }else {
            holder = (ViewHolder) convertView.getTag();
        }

只不过findViewById我们需要处理一下。所以我们应该把这些不变的代码抽取出来,不应该让用户重复写这些代码。

把这些代码抽取出来,当然这些代码都应该放在ViewHolder中

ViewHolder holder;
        if (convertView == null) {
            convertView= mLayoutInflater.inflate(R.layout.myitem, parent, false);
            holder = new ViewHolder();
        }else {
            holder = (ViewHolder) convertView.getTag();
        }

新建一个类 ViewHolder.java

public class ViewHolder {
    private View mConvertView;
//ViewHolder构造函数,只有当convertView为空的时候才创建
    public ViewHolder(Context context,View convertView, ViewGroup parent, int layouId) {
        convertView = LayoutInflater.from(context).inflate(layouId,parent,false);
        convertView.setTag(this);       //将其setTag()
        mConvertView = convertView;
    }
    //返回一个ViewHolder对象
    public static ViewHolder getHolder(Context context, View convertView, ViewGroup parent, int layoutId) {
        if (convertView == null) {
            return new ViewHolder(context,convertView,parent,layoutId);
        }else {
            return (ViewHolder) convertView.getTag();
        }
    }
}

传过来的参数包括:Context context, View convertView, ViewGroup parent, int layoutId,这些参数都是加载布局文件所需要的。

然后就是这些代码需要抽取了:

holder.title_Tv = (TextView) convertView.findViewById(R.id.title_Tv);
            holder.desc_Tv = (TextView) convertView.findViewById(R.id.desc_Tv);
            holder.time_Tv = (TextView) convertView.findViewById(R.id.time_Tv);
            holder.phone_Tv = (TextView) convertView.findViewById(R.id.phone_Tv);

新建方法:

public class ViewHolder {
    //用来存布局中的各个组件,以键值对形式
    private SparseArray<View> mViews = new SparseArray<>();
    //返回一个View的子类对象,因为不确定用户布局有什么组件,相当于findViewById
    //这里返回一个泛型,也可以返回一个View或Object
    public <T extends View>T getView(int resId) {
        View view = mViews.get(resId);  //从集合中取出这个组件
        if (view == null) {         //如果为空,说明为第一屏
            view = mConvertView.findViewById(resId);    //从convertView中找
            mViews.put(resId,view);     //再将其以键值对存进去
        }
        return (T) view;
    }
}

接下里的这个返回值就容易了,直接返回就行了

public class ViewHolder {
    /**
     * @return  返回convertView
     */
    public View getConvertView() {
        return mConvertView;
    }
}

这部分工作都完了,就只留下了为组件设置数据的那段代码了,这段代码由于是可变的,应该让用户来做,所以设置为抽象方法。

新建一个类 CommonAdapter.java 继承BaseAdapter:

public abstract class CommonAdapter<T> extends BaseAdapter {
    //需要显示的数据,List中的类型为泛型,因为不知道用户的封装Bean
    private List<T> mDatas;
    //上下文
    private Context mContext;
    //布局文件Id
    private int mLayoutId;
    public CommonAdapter(Context context,List<T> data,int layoutId) {
        mDatas = data;
        mContext = context;
        mLayoutId = layoutId;
    }
    @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;
    }

    @Override
    public View getView(int position, View convertView, ViewGroup parent) {
        ViewHolder holder = ViewHolder.getHolder(mContext,convertView, parent, mLayoutId);
        setDatas(holder,getItem(position));
        return holder.getConvertView();
    }

    /**
     * 为各个item中的控件设置数据
     * @param holder   ViewHolder
     * @param object  从集合中所取的一个对象
     */
    public abstract void setDatas(ViewHolder holder, Object object);
}

到这里就把刚才那段代码全部抽取出来了,那再来看一下如何使用吧。

使用步骤:

①添加ListView, 找ListView 这些步骤是一样的。

②新建一个 Adapter 继承 CommonAdapter

public class MagicAdapter extends CommonAdapter<Bean> {
    public MagicAdapter(Context context, List<Bean> data, int layoutId) {
        super(context, data, layoutId);
    }
    @Override
    public void setDatas(ViewHolder holder, Object object) {
        Bean bean = (Bean) object;
        ((TextView)holder.getView(R.id.title_Tv)).setText(bean.getTitle());
        ((TextView)holder.getView(R.id.desc_Tv)).setText(bean.getDesc());
        ((TextView)holder.getView(R.id.time_Tv)).setText(bean.getTime());
        ((TextView)holder.getView(R.id.phone_Tv)).setText(bean.getPhone());
    }
}

注:

③为ListView设置适配器

//为listView设置适配器
        mListView.setAdapter(new MagicAdapter(this,mDatas,R.layout.listview_item));

到这里就实现了万能的适配器了,是不是减少了很多代码。我们知道java三大特性是封装、继承、多态。这篇例子可以锻炼一下大家的封装能力。大家试试吧

核心代码:

activity_main.xml

<?xml version="1.0" encoding="utf-8"?>
<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"
    tools:context="com.briup.universal.MainActivity">

    <ListView
        android:id="@+id/listView"
        android:layout_width="wrap_content"
        android:layout_height="wrap_content"/>
</RelativeLayout>

listview_item.xml

<?xml version="1.0" encoding="utf-8"?>
<RelativeLayout xmlns:android="http://schemas.android.com/apk/res/android"
              android:layout_width="match_parent"
              android:layout_height="match_parent">
    <TextView
        android:layout_marginTop="12dp"
        android:layout_marginLeft="12dp"
        android:textColor="#000"
        android:layout_width="wrap_content"
        android:layout_height="wrap_content"
        android:textSize="20sp"
        android:text="新技能get"
        android:id="@+id/title_Tv"/>
    <TextView
        android:layout_marginLeft="12dp"
        android:layout_marginTop="10dp"
        android:textSize="18sp"
        android:textColor="#CDCDCD"
        android:layout_width="wrap_content"
        android:layout_height="wrap_content"
        android:text="打造万能适配器"
        android:layout_below="@id/title_Tv"
        android:id="@+id/desc_Tv"/>
    <TextView
        android:id="@+id/time_Tv"
        android:layout_marginTop="10dp"
        android:layout_marginLeft="12dp"
        android:text="2016-5-21"
        android:layout_below="@id/desc_Tv"
        android:layout_width="wrap_content"
        android:layout_height="wrap_content"/>
    <TextView
        android:id="@+id/phone_Tv"
        android:text="10086"
        android:layout_marginRight="12dp"
        android:layout_alignParentRight="true"
        android:layout_alignBaseline="@id/time_Tv"
        android:layout_width="wrap_content"
        android:layout_height="wrap_content"/>
</RelativeLayout>

MainActivity.java

public class MainActivity extends AppCompatActivity {

    private ListView mListView;
    private List<Bean> mDatas;
    @Override
    protected void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        setContentView(R.layout.activity_main);
        initData();
        initView();
        //为listView设置适配器
        mListView.setAdapter(new MagicAdapter(this,mDatas,R.layout.listview_item));
    }

    /**
     * 初始化组件
     */
    private void initView() {
        mListView = (ListView) findViewById(R.id.listView);
    }

    /**
     * 初始化数据
     */
    private void initData() {
        mDatas = new ArrayList<>();
        //模拟数据
        for (int i=0; i < 6; i++) {
            Bean bean = new Bean("新技能" +i,"打造万能适配器"+ i,"2016-5-22","10086");
            mDatas.add(bean);
        }
    }
}

ViewHolder.java

public class ViewHolder {
    private View mConvertView;
    //用来存布局中的各个组件,以键值对形式
    private SparseArray<View> mViews = new SparseArray<>();
    //ViewHolder构造函数,只有当convertView为空的时候才创建
    public ViewHolder(Context context,View convertView, ViewGroup parent, int layouId) {
        convertView = LayoutInflater.from(context).inflate(layouId,parent,false);
        convertView.setTag(this);       //将其setTag()
        mConvertView = convertView;
    }
    //返回一个ViewHolder对象
    public static ViewHolder getHolder(Context context, View convertView, ViewGroup parent, int layoutId) {
        if (convertView == null) {
            return new ViewHolder(context,convertView,parent,layoutId);
        }else {
            return (ViewHolder) convertView.getTag();
        }
    }
    //返回一个View的子类对象,因为不确定用户布局有什么组件,相当于findViewById
    //这里返回一个泛型,也可以返回一个View或Object
    public <T extends View>T getView(int resId) {
        View view = mViews.get(resId);  //从集合中取出这个组件
        if (view == null) {         //如果为空,说明为第一屏
            view = mConvertView.findViewById(resId);    //从convertView中找
            mViews.put(resId,view);     //再将其以键值对存进去
        }
        return (T) view;
    }

    /**
     * @return  返回convertView
     */
    public View getConvertView() {
        return mConvertView;
    }

}

CommonAdapter.java

public abstract class CommonAdapter<T> extends BaseAdapter {
    //需要显示的数据,List中的类型为泛型,因为不知道用户的封装Bean
    private List<T> mDatas;
    //上下文
    private Context mContext;
    //布局文件Id
    private int mLayoutId;
    public CommonAdapter(Context context,List<T> data,int layoutId) {
        mDatas = data;
        mContext = context;
        mLayoutId = layoutId;
    }
    @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;
    }

    @Override
    public View getView(int position, View convertView, ViewGroup parent) {
        ViewHolder holder = ViewHolder.getHolder(mContext,convertView, parent, mLayoutId);
        setDatas(holder,getItem(position));
        return holder.getConvertView();
    }

    /**
     * 为各个item中的控件设置数据
     * @param holder   ViewHolder
     * @param object  从集合中所取的一个对象
     */
    public abstract void setDatas(ViewHolder holder, Object object);
}

Bean.java

public class Bean {
    private String title;       //标题
    private String desc;        //简介
    private String time;        //时间
    private String phone;       //电话

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

MagicAdapter.java

public class MagicAdapter extends CommonAdapter<Bean> {
    public MagicAdapter(Context context, List<Bean> data, int layoutId) {
        super(context, data, layoutId);
    }
    @Override
    public void setDatas(ViewHolder holder, Object object) {
        Bean bean = (Bean) object;
        ((TextView)holder.getView(R.id.title_Tv)).setText(bean.getTitle());
        ((TextView)holder.getView(R.id.desc_Tv)).setText(bean.getDesc());
        ((TextView)holder.getView(R.id.time_Tv)).setText(bean.getTime());
        ((TextView)holder.getView(R.id.phone_Tv)).setText(bean.getPhone());
    }
}

源码下载:http://download.csdn.net

时间: 2024-10-10 20:08:25

android开发笔记之打造终极适配器的相关文章

Android开发笔记(一百一十九)工具栏ToolBar

Toolbar 在前面的博文<Android开发笔记(二十)顶部导航栏>中,我们学习了ActionBar的用法,可是ActionBar着实是不怎么好用,比如文字风格不能定制.图标不能定制,而且还存在低版本的兼容性问题,所以实际开发中大家还是不倾向使用ActionBar.为此,Android提供了加强版的工具栏控件即Toolbar,因为Toolbar继承自ViewGroup,而且可在布局文件中像其它布局视图一样使用,所以灵活性大大的提高了.既然Android都与时俱进了,那我们也不能落后,现在就

Android开发笔记(一百)折叠式列表

更多动态视图MoreNewsView 经常看朋友圈的动态,有的动态内容较多就只展示前面一段,如果用户想看完整的再点击展开,这样整个页面的动态列表比较均衡,不会出现个别动态占用大片屏幕的情况.同样,查看博客的文章列表也类似,只展示文章开头几行内容,有需要再点击加载全篇文章. 动态列表直接使用ListView,动态内容就得自己写个控件了,自定义控件的难点在于如何把握动态下拉和收起的动画.这里我们要先预习TextView的相关函数,下面是本文用到的方法说明: getHeight : 获取TextVie

【转】Android开发笔记(序)写在前面的目录

原文:http://blog.csdn.net/aqi00/article/details/50012511 知识点分类 一方面写写自己走过的弯路掉进去的坑,避免以后再犯:另一方面希望通过分享自己的经验教训,与网友互相切磋,从而去芜存菁进一步提升自己的水平.因此博主就想,入门的东西咱就不写了,人不能老停留在入门上:其次是想拾缺补漏,写写虽然小众却又用得着的东西:另外就是想以实用为主,不求大而全,但求小而精:还有就是有的知识点是java的,只是Android开发也会经常遇上,所以蛮记下来.个人的经

Android开发笔记(一百零三)地图与定位SDK

集成地图SDK 国内常用的地图SDK就是百度和高德了,二者的用法大同小异,可按照官网上的开发指南一步步来.下面是我在集成地图SDK时遇到的问题说明: 1.点击基本地图功能选项,不能打开地图,弹出"key验证出错!请在AndroidManifest.xml文件中检查key设置的"的红色字提示.查看日志提示"galaxy lib host missing meta-data,make sure you know the right way to integrate galaxy&

[APP] Android 开发笔记 003

接上节 [APP] Android 开发笔记 002 5. 使用ant release 打包 1)制作 密钥文件 release.keystore (*.keystore) keytool -genkey -v -keystore "release.keystore" -alias "release" -keyalg "RSA" -validity "10000" 这里需要注意的是: -keystore "relea

《ArcGIS Runtime SDK for Android开发笔记》——(10)、ArcGIS Runtime SDK支持的空间数据类型

1.前言 移动端的数据来源非常重要,它决定了移动端功能的实现.早期的ArcGIS Android API中,主要以接入在线的数据源为主,因此主要实现在线的地图浏览.查询和路径分析.地理处理等从操作:在v1.0.1版本中,ArcGIS移动产品第一次可以加载松散型切片,自此逐渐掀开了对本地离线数据源的支持,也因此可以在移动端实现越来越受欢迎的离线功能.现在最新的10.2.7 API离线支持数据主要包括紧凑型切片.tpk切片包..geodatabase..shp文件.地名地址库.网络数据集. 转载请注

Android开发笔记--hello world 和目录结构

原文:Android开发笔记--hello world 和目录结构 每接触一个新东西 都有一个hello world的例子. 1.新建项目 2.配置AVD AVD 没有要新建个,如果不能创建 运行SDK Manager更新 3.接着运行就可以了 第一次启动要1分多钟 不要关 4.添加代码 5.接着在运行就OK了 目录结构 1.src - 用于放置源程序 2.gen - 自动生成 R.java 文件,用于引用资源文件(即 res 目录下的数据) 3.assets - 用于放置原始文件,Androi

Android开发笔记(一百一十六)网络学习资源

知名网站 本系列的开发笔记,对Android开发来说只是沧海一瓢,还有更多的技术等待我们去汲取.下面列出几个常用的开发网站,供初学者上路: 首先当然是国内首屈一指的技术网站csdn啦,csdn提供了众多频道,包括博客.论坛.下载.问答等等,其中博客专栏提供了最新的技术文章,值得推荐.csdn博客专栏的地址是 http://blog.csdn.net/column.html 下面是csdn博客专栏的网页截图: 其次是国外有名的开源网站GitHub,这里有众多的开源项目源码,是开发者分享代码的乐园.

Android开发笔记(八十八)同步与加锁

同步synchronized 同步方法 synchronized可用来给方法或者代码块加锁,当它修饰一个方法或者一个代码块的时候,同一时刻最多只有一个线程执行这段代码.这就意味着,当两个并发线程同时访问synchronized代码块时,两个线程只能是排队做串行处理,另一个线程要等待前一个线程执行完该代码块后,才能再次执行synchronized代码块. 使用synchronized修饰某个方法,该方法便成为一个同步方法,在同一时刻只能有一个线程执行该方法.可是,synchronized的锁机制太