Android 学习笔记之ListView二层封装


ListView作为Android 开发中使用最多的控件之一,相信很多人对他的用法应该都不陌生,最主要就是,定义好每个Itme的布局,给其设置一个数据适配器(Adapter)就完事,下面来回顾一下ListVew的一般用法。

/**
 * Created by 毛麒添 on 2017/1/18 0018.
 */

public class MainActivity extends AppCompatActivity {
    private ArrayList<String>data;

   protected void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        setContentView(R.layout.activity_main);

        data=new ArrayList<String>();
        for (int i=0;i<30;i++){
            data.add("listView数据"+i);
        }

        ListView listView=new ListView(getContext());
        listView.setAdapter(new MyAdapter());
    }


    class MyAdapter extends BaseAdapter{

     
    @Override
    public int getCount() {
        return data.size();
    }

    @Override
    public String getItem(int position) {
        return data.get(position);
    }

    @Override
    public long getItemId(int position) {
        return position;
    }

        @Override
        public View getView(int position, View convertView, ViewGroup parent) {
            ViewHolder viewHolder=null;
            if(convertView==null){
                //1.加载布局文件
                convertView= MyUIUtils.inflate(R.layout.list_home_item);
                viewHolder=new ViewHolder();
                //2.初始化控件
                viewHolder.textView= (TextView) convertView.findViewById(R.id.tv_test);
                //3.打一个标记tag
                convertView.setTag(viewHolder);
            }else {
                viewHolder= (ViewHolder) convertView.getTag();
            }
            //4.根据数据来刷新界面
            viewHolder.textView.setText(getItem(position));
            return convertView;
        }
    }

    static class ViewHolder{
        TextView textView;
    }
}
  • 如果我们只是给一个ListView设置数据,则写一个上面的数据适配器无可厚非,但是如果在一个项目中有许多的ListView,则给每个ListView都写一个数据适配器,则上面的适配器中必须实现 getCount() getItem(),getItemId(),getView() 方法对每个都要写一次,这样多几次应该会感觉很烦,那有没有可能对这几个方法抽取出来,进行封装,简便要写多个 ListView 数据适配器的烦恼呢?答案是肯定的,下面就开始对ListView的数据适配器进行两层封装,以达到我们的目的。

ListView的第一层封装

  • 首先可以对最简单的 getCount(),getItem(),getItemId() 方法进行封装,创建我们自己的父类Adapter,让其继承BaseAdpter.其中getCount(),getItem(),getItemId() 最后设置的返回值一般都是依赖一个List数组,但这个父类是我们定义的,则可以用构造方法来传入List数组,而这时候问题又来了,数组的泛型我们也不知道,这时候可以把泛型定义为Object,但是当我们传入数据的时候则就要将 Object 强转为我们传入数据对应的泛型,其实这里可以直接传入一个大写字母(ArrayList 源码中也是如此定义),代表传入的数据泛型,下面上代码:
/**
 * Created by 毛麒添 on 2017/1/21 0021.
 * 对Adapter的封装 简化数据适配器的设置
 */

public  class MyBaseAdapter<T> extends BaseAdapter {


    private ArrayList<T>data; //T代表泛型,传入的是什么类型就是什么类型

    public MyBaseAdapter(ArrayList<T>data){
         this.data=data;
    }

    @Override
    public int getCount() {
        return data.size();
    }

    @Override
    public T getItem(int position) {
        return data.get(position);
    }

    @Override
    public long getItemId(int position) {
        return position;
    }

    @Override
    public View getView(int position, View convertView, ViewGroup parent) {
       return null;
    }

}
  • 这样当我们使用 MyBaseAdapter 来作为数据适配器的父类,就不用再写 getCount(),getItem(),getItemId()这三个方法了,OK,完成对 ListView 的第一层封装,但是这个我们自定义的父类方法还没完成,接下来就是对getView()的封装。

ListView的第二层封装

  • 前面只是对 getCount(), getItem() ,getItemId() 等方法进行了封装,接下来便是对 getView() 方法来进行封装,以前我们写ListView数据适配器的getView()方法一般都是以下四个步骤:
  • 1.加载布局文件
  • 2.初始化控件
  • 3.打一个标记tag
  • 4.根据数据来刷新界面
  • 无论如何,这四个步骤都是要实现的。而这四个步骤中,都在其中有ViewHolder帮助类来帮助我们实现,所以我们可以考虑在将这四个步骤抽取成为一个工具类,也就是自己定义一个父类holder,在其中实现这几个步骤,进一步进行封装。
import android.view.View;

/**
 * Created by 毛麒添 on 2017/1/21 0021.
 * getView(); ViewHolder封装
 */

public abstract class BaseHolder<T> {


    private final View view;//ListView的item根布局View对象

    private T data;

    /**获取这个父类holder,就是执行
     * 1.加载布局文件
     * 2.初始化控件
     * 3.打一个标记tag
     * 这三个步骤
    */
    public BaseHolder(){

        view = initView();
        //3.打一个标记tag,也就是当前的BaseHolder
        view.setTag(this);
    }

    //设置当前Item的数据
    public void setData(T data){
       this.data=data;
        //获取数据直接将其传递给子类刷新数据
        RefreshView(data);
    }

    //获取当前Item的数据
    public T getData(){
        return data;
    }

    /**
     * 本身并不知道需要加载什么样的布局和控件,让子类去实现
     * 1.加载布局文件
     * 2.初始化控件
     * @return 返回相应的item根布局View对象
     */
    public abstract View initView();

    /**
     * 获取Item布局对象
     * @return 返回item布局对象
     */
    public View getItemView(){
        return view;
    }

    //4.根据数据来刷新界面,本身并不知道是什么数据,让子类去实现
    public abstract void RefreshView(T data);

}
  • 代码中,首选我们实现第一、二个步骤,但是这只是所有布局Item的父类,并不知道每个ListView的Item需要展现什么样的布局,所以定义一个抽象方法 initView()让子类去实现布局的加载与控件的初始化,而且在初始化父类的时候,也就是在构造方法中就调用该方法获取对应的Item的View对象,既然获取了对应的View对象,并且该类就是四个步骤ed封装,也就可以给其设置一个Tag,到此,前三个步骤已经搞定,最后一个步骤是给Item布局设置刷新数据,同理也是定义成抽象方法RefreshView(T data)让子类去实现,再加上两个数据set和get方法。
    至此,四个步骤封装完成。
  • 接下来搞个子类继承BaseHolder实现未实现的方法,指定特定的ListView的Item需要展示的数据。
/**
 * Created by 毛麒添 on 2017/1/21 0021.
 * BaseHolder子类
 */

public class MyHolder extends BaseHolder<String> {

    private TextView textView;

    @Override
    public View initView() {
        //步骤1.加载布局文件
        View view= MyUIUtils.inflate(R.layout.list_home_item);
        //步骤2.初始化控件
        textView = (TextView) view.findViewById(R.id.tv_test);
        return view;
    }

    //步骤4刷新页面数据
    @Override
    public void RefreshView(String data) {
        textView.setText(data);
    }


}

现在,就可以把上面未完成的MyBaseAdapter父类完整实现:

public abstract class MyBaseAdapter<T> extends BaseAdapter {


    private ArrayList<T>data; //T代表泛型,传入的是什么类型就是什么类型

    public MyBaseAdapter(ArrayList<T>data){
         this.data=data;
    }

    @Override
    public int getCount() {
        return data.size();
    }

    @Override
    public T getItem(int position) {
        return data.get(position);
    }

    @Override
    public long getItemId(int position) {
        return position;
    }

    @Override
    public View getView(int position, View convertView, ViewGroup parent) {
        BaseHolder baseHolder;
        if(convertView==null){
            /**
             * 1.加载布局文件
             * 2.初始化控件
             * 3.打一个标记tag
             */
            baseHolder=getHolder();//由子类返回具体对象
        }else {
            baseHolder= (BaseHolder) convertView.getTag();
        }
        //4.根据数据来刷新界面
        baseHolder.setData(getItem(position));

        //返回item对象
        return baseHolder.getItemView();
    }

    //返回当前页面的holder对象,必须由子类去实现
    public abstract BaseHolder<String> getHolder();
}
  • 在上面的getView()方法,只要初始化BaseHolder的对象,就已经完成了四个步骤中的前三个,第四个步骤只要将数据设置给BaseHolder的对象也就完成了,对象的初始化也是需要继承该方法的子类去实现,因为每个LsitView的所展示的Item是不同的。

  • 终于,对ListView二层封装已经完成,下面我们来使用一发:
public class MainActivity extends AppCompatActivity {
    private ArrayList<String>data;

   protected void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        setContentView(R.layout.activity_main);

        data=new ArrayList<String>();
        for (int i=0;i<30;i++){
            data.add("listView数据"+i);
        }

        ListView listView=new ListView(getContext());
        listView.setAdapter(new MyAdapter());
    }


    class MyAdapter extends MyBaseAdapter<String>{

     public MyAdapter(ArrayList<String> data) {
            super(data);
        }

        @Override
        public BaseHolder<String> getHolder() {
            return new MyHolder();
        }
}
  • 好了,以后我们在使用ListView,只需要写一个类继承BaseHolder,然后按照我们的数据获取初始化布局和控件,然后设置数据,只需要在数据适配器中初始化该类就完事。

最后

抽取相同的部分,封装,应该相当于搭建了一个小框架吧,也让我对适配器有了更进一步的认识。如果有哪些地方写得不对,欢迎大家给我提出来纠正,让我们一同进步。

最后编辑于
©著作权归作者所有,转载或内容合作请联系作者
  • 序言:七十年代末,一起剥皮案震惊了整个滨河市,随后出现的几起案子,更是在滨河造成了极大的恐慌,老刑警刘岩,带你破解...
    沈念sama阅读 215,539评论 6 497
  • 序言:滨河连续发生了三起死亡事件,死亡现场离奇诡异,居然都是意外死亡,警方通过查阅死者的电脑和手机,发现死者居然都...
    沈念sama阅读 91,911评论 3 391
  • 文/潘晓璐 我一进店门,熙熙楼的掌柜王于贵愁眉苦脸地迎上来,“玉大人,你说我怎么就摊上这事。” “怎么了?”我有些...
    开封第一讲书人阅读 161,337评论 0 351
  • 文/不坏的土叔 我叫张陵,是天一观的道长。 经常有香客问我,道长,这世上最难降的妖魔是什么? 我笑而不...
    开封第一讲书人阅读 57,723评论 1 290
  • 正文 为了忘掉前任,我火速办了婚礼,结果婚礼上,老公的妹妹穿的比我还像新娘。我一直安慰自己,他们只是感情好,可当我...
    茶点故事阅读 66,795评论 6 388
  • 文/花漫 我一把揭开白布。 她就那样静静地躺着,像睡着了一般。 火红的嫁衣衬着肌肤如雪。 梳的纹丝不乱的头发上,一...
    开封第一讲书人阅读 50,762评论 1 294
  • 那天,我揣着相机与录音,去河边找鬼。 笑死,一个胖子当着我的面吹牛,可吹牛的内容都是我干的。 我是一名探鬼主播,决...
    沈念sama阅读 39,742评论 3 416
  • 文/苍兰香墨 我猛地睁开眼,长吁一口气:“原来是场噩梦啊……” “哼!你这毒妇竟也来了?” 一声冷哼从身侧响起,我...
    开封第一讲书人阅读 38,508评论 0 271
  • 序言:老挝万荣一对情侣失踪,失踪者是张志新(化名)和其女友刘颖,没想到半个月后,有当地人在树林里发现了一具尸体,经...
    沈念sama阅读 44,954评论 1 308
  • 正文 独居荒郊野岭守林人离奇死亡,尸身上长有42处带血的脓包…… 初始之章·张勋 以下内容为张勋视角 年9月15日...
    茶点故事阅读 37,247评论 2 331
  • 正文 我和宋清朗相恋三年,在试婚纱的时候发现自己被绿了。 大学时的朋友给我发了我未婚夫和他白月光在一起吃饭的照片。...
    茶点故事阅读 39,404评论 1 345
  • 序言:一个原本活蹦乱跳的男人离奇死亡,死状恐怖,灵堂内的尸体忽然破棺而出,到底是诈尸还是另有隐情,我是刑警宁泽,带...
    沈念sama阅读 35,104评论 5 340
  • 正文 年R本政府宣布,位于F岛的核电站,受9级特大地震影响,放射性物质发生泄漏。R本人自食恶果不足惜,却给世界环境...
    茶点故事阅读 40,736评论 3 324
  • 文/蒙蒙 一、第九天 我趴在偏房一处隐蔽的房顶上张望。 院中可真热闹,春花似锦、人声如沸。这庄子的主人今日做“春日...
    开封第一讲书人阅读 31,352评论 0 21
  • 文/苍兰香墨 我抬头看了看天上的太阳。三九已至,却和暖如春,着一层夹袄步出监牢的瞬间,已是汗流浃背。 一阵脚步声响...
    开封第一讲书人阅读 32,557评论 1 268
  • 我被黑心中介骗来泰国打工, 没想到刚下飞机就差点儿被人妖公主榨干…… 1. 我叫王不留,地道东北人。 一个月前我还...
    沈念sama阅读 47,371评论 2 368
  • 正文 我出身青楼,却偏偏与公主长得像,于是被迫代替她去往敌国和亲。 传闻我的和亲对象是个残疾皇子,可洞房花烛夜当晚...
    茶点故事阅读 44,292评论 2 352

推荐阅读更多精彩内容