Android 可复用 BaseAdapter

经过前几章节的学习,是不是对 ListView 有了很大的了解,简直就是眯起眼睛都会写了

这个过程中,也有不愉快的地方,就是 xxxAdapter 重复写太多次了,一模一样的功能,拷贝了一次又一次,只是类名不同而已,在大型的项目中,这简直就是致命的啊

那有没有办法复用呢?

答案是肯定的

本章节,我们一步一个脚印看看写一个可复用的 Base Adapter

原来的 TalkAdapter

首先我们来看看截止上一章节中的 TalkAdapter 代码

package cn.twle.android.listviewcrud;

import android.widget.BaseAdapter;
import android.widget.ImageView;
import android.widget.TextView;

import android.content.Context;

import android.view.View;
import android.view.ViewGroup;
import android.view.LayoutInflater;

import java.util.LinkedList;

public class TalkAdapter extends BaseAdapter {

    private Context mContext;
    private LinkedList<TalkBean> mData;

    public TalkAdapter() {}

    public TalkAdapter(LinkedList<TalkBean> mData, Context mContext) {
        this.mData = mData;
        this.mContext = mContext;
    }

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

    @Override
    public Object getItem(int position) {
        return mData.get(position);
    }

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

    @Override
    public View getView(int position, View convertView, ViewGroup parent) {
        ViewHolder holder = null;

        if(convertView == null){

            convertView = LayoutInflater.from(mContext).inflate(R.layout.listview_item,parent,false);

            holder = new ViewHolder();

            holder.avatar = (ImageView) convertView.findViewById(R.id.avatar);
            holder.say = (TextView) convertView.findViewById(R.id.say);
            convertView.setTag(holder);
        }else{
            holder = (ViewHolder) convertView.getTag();
        }

        holder.avatar.setImageResource(mData.get(position).getAvatarId());
        holder.say.setText(mData.get(position).getSay());
        return convertView;
    }

    // 往特定位置,添加一个元素
    public void insert(TalkBean data, int position){
        if (mData == null) {
            mData = new LinkedList<>();
        }

        int len = mData.size();

        if ( 0 > position )
        {
            position = 0;
        }

        if ( position > len )
        {
            position = len;
        }

        mData.add(position,data);
        notifyDataSetChanged();
    }

    public void add(TalkBean data) {
        if (mData == null) {
            mData = new LinkedList<>();
        }
        mData.add(data);
        notifyDataSetChanged();
    }


    public void remove(TalkBean data) {

        if(mData != null) {
            mData.remove(data);
        }
        notifyDataSetChanged();
    }

    public void remove(int position) {
        if(mData != null) {
            mData.remove(position);
        }
        notifyDataSetChanged();
    }

    public void clear() {

        if(mData != null) {
            mData.clear();
        }
        notifyDataSetChanged();
    }

    public void update(int position, TalkBean data) {

        mData.set(position,data);

        notifyDataSetChanged();
    }


    private class ViewHolder{
        ImageView avatar;
        TextView  say;
    }


}

升级 TalkBean 为泛型

要复用,首先要消灭掉的就是 TalkBean,而消灭的唯一方式就是改成泛型,然后就可以使用各种千奇百怪的 PersonBeanShopBean ...

package cn.twle.android.common;

import android.widget.BaseAdapter;
import android.widget.ImageView;
import android.widget.TextView;

import android.content.Context;

import android.view.View;
import android.view.ViewGroup;
import android.view.LayoutInflater;

import java.util.LinkedList;

public class MsAdapter<T> extends BaseAdapter {

    private Context mContext;
    private LinkedList<T> mData;

    public TalkAdapter() {}

    public TalkAdapter(LinkedList<T> mData, Context mContext) {
        this.mData = mData;
        this.mContext = mContext;
    }

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

    @Override
    public Object getItem(int position) {
        return mData.get(position);
    }

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

    @Override
    public View getView(int position, View convertView, ViewGroup parent) {
        ViewHolder holder = null;

        if(convertView == null){

            convertView = LayoutInflater.from(mContext).inflate(R.layout.listview_item,parent,false);

            holder = new ViewHolder();

            holder.avatar = (ImageView) convertView.findViewById(R.id.avatar);
            holder.say = (TextView) convertView.findViewById(R.id.say);
            convertView.setTag(holder);
        }else{
            holder = (ViewHolder) convertView.getTag();
        }

        holder.avatar.setImageResource(mData.get(position).getAvatarId());
        holder.say.setText(mData.get(position).getSay());
        return convertView;
    }

    // 往特定位置,添加一个元素
    public void insert(T data, int position){
        if (mData == null) {
            mData = new LinkedList<>();
        }

        int len = mData.size();

        if ( 0 > position )
        {
            position = 0;
        }

        if ( position > len )
        {
            position = len;
        }

        mData.add(position,data);
        notifyDataSetChanged();
    }

    public void add(T data) {
        if (mData == null) {
            mData = new LinkedList<>();
        }
        mData.add(data);
        notifyDataSetChanged();
    }


    public void remove(T data) {

        if(mData != null) {
            mData.remove(data);
        }
        notifyDataSetChanged();
    }

    public void remove(int position) {
        if(mData != null) {
            mData.remove(position);
        }
        notifyDataSetChanged();
    }

    public void clear() {

        if(mData != null) {
            mData.clear();
        }
        notifyDataSetChanged();
    }

    public void update(int position, T data) {

        mData.set(position,data);

        notifyDataSetChanged();
    }


    private class ViewHolder{
        ImageView avatar;
        TextView  say;
    }


}

改造 ViewHolder

对于 ViewHolder ,我们之前只是用,但没说为什么要用

那么,现在来讲讲,我们先看看 ViewHolder 做了什么

findViewById,设置控件状态

因此,我们有个大胆的想法,把 getView() 里面的大部分内容都搬到 ViewHolder

  1. 定义一个查找控件的方法,通过暴露公共的方法,调用方法时传递过来 控件 id,以及设置的内容,比如 TextView 设置文本

    public ViewHolder setText(int id, CharSequence text){ /*文本设置*/ }
    
  2. convertView 复用部分搬到这里,需要传递一个 context 对象,把需要获取 的部分都写到构造方法中

  3. 写一堆设置方法 (public),比如设置文字大小颜色,图片背景等

相关参数与构造方法

public static class ViewHolder {

    private SparseArray<View> mViews;   // 存储 ListView 的 item中的View
    private View item;                  // 存放 convertView
    private int position;               // 游标
    private Context context;            // Context 上下文

    //构造方法,完成相关初始化
    private ViewHolder(Context context, ViewGroup parent, int layoutRes) {
        mViews = new SparseArray<>();
        this.context = context;
        View convertView = LayoutInflater.from(context).inflate(layoutRes, parent,false);
        convertView.setTag(this);
        item = convertView;
    }

    ImageView img_icon;
    TextView txt_content;
}

绑定 ViewHolder 与 Item

接下来影响复用的就是 getView() 里的 ViewHolder 了,ViewHolder 不能去掉,移出该文件,所以要定义一个方法,将 AdapterViewHolder 绑在一起

//绑定 ViewHolder 与 item
public static ViewHolder bind(Context context, View convertView, ViewGroup parent,int layoutRes, int position) {

    ViewHolder holder;
    if(convertView == null) {

        holder = new ViewHolder(context, parent, layoutRes);
    } else {
        holder = (ViewHolder) convertView.getTag();
        holder.item = convertView;
    }
    holder.position = position;
    return holder;
}

根据 id 获取集合中保存的控件

接下来就是改变 getView() 方法了

public <V extends View> V getView(int id) {
    V v = (V) mViews.get(id);
    if(v == null) {
        v = (V) item.findViewById(id);
        mViews.put(id, v);
    }
    return v;
}

再定义一堆暴露出来的方法

/**
 * 获取当前条目
 */
public View getItemView() {
    return item;
}

/**
 * 获取条目位置
 */
public int getItemPosition() {
    return position;
}

/**
 * 设置文字
 */
public ViewHolder setText(int id, CharSequence text) {
    View view = getView(id);
    if(view instanceof TextView) {
        ((TextView) view).setText(text);
    }
    return this;
}

/**
 * 设置图片
 */
public ViewHolder setImageResource(int id, int drawableRes) {
    View view = getView(id);
    if(view instanceof ImageView) {
        ((ImageView) view).setImageResource(drawableRes);
    } else {
        view.setBackgroundResource(drawableRes);
    }
    return this;
}


/**
 * 设置点击监听
 */
public ViewHolder setOnClickListener(int id, View.OnClickListener listener) {
    getView(id).setOnClickListener(listener);
    return this;
}

/**
 * 设置可见
 */
public ViewHolder setVisibility(int id, int visible) {
    getView(id).setVisibility(visible);
    return this;
}

/**
 * 设置标签
 */
public ViewHolder setTag(int id, Object obj) {
    getView(id).setTag(obj);
    return this;
}

定义一个抽象方法,绑定 ViewHolder 与 Data 数据集

public abstract void bindView(ViewHolder holder, T obj);

然后创建新的 Adapter 的时候,只要实现这个方法就好

另外,也要将我们自定义的 MsAdapter 改成 abstact 抽象

修改 MsAdapter.getView() 方法

@Override
public View getView(int position, View convertView, ViewGroup parent) {
    ViewHolder holder = ViewHolder.bind(parent.getContext(), convertView, parent, mLayoutRes
            , position);
    bindView(holder,getItem(position));
    return holder.getItemView();
}

好的,基本上就这样了,看看我们完成后的代码

对了,我把它放到网上了 [/static/i/android/MsAdapter.java]

package cn.twle.android.common;

import android.widget.BaseAdapter;
import android.widget.ImageView;
import android.widget.TextView;

import android.content.Context;

import android.view.View;
import android.view.ViewGroup;
import android.view.LayoutInflater;

import java.util.ArrayList;


public abstract class MsAdapter<T> extends BaseAdapter {

    private ArrayList<T> mData;
    private int mLayoutRes;           //布局 id


    public MsAdapter() {
    }

    public MsAdapter(ArrayList<T> mData, int mLayoutRes) {
        this.mData = mData;
        this.mLayoutRes = mLayoutRes;
    }

    @Override
    public int getCount() {
        return mData != null ? mData.size() : 0;
    }

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

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

    @Override
    public View getView(int position, View convertView, ViewGroup parent) {
        ViewHolder holder = ViewHolder.bind(parent.getContext(), convertView, parent, mLayoutRes
                , position);
        bindView(holder, getItem(position));
        return holder.getItemView();
    }

    public abstract void bindView(ViewHolder holder, T obj);

    //添加一个元素
    public void add(T data) {
        if (mData == null) {
            mData = new ArrayList<>();
        }
        mData.add(data);
        notifyDataSetChanged();
    }

    //往特定位置,添加一个元素
    public void add(int position, T data) {
        if (mData == null) {
            mData = new ArrayList<>();
        }
        mData.add(position, data);
        notifyDataSetChanged();
    }

    public void remove(T data) {
        if (mData != null) {
            mData.remove(data);
        }
        notifyDataSetChanged();
    }

    public void remove(int position) {
        if (mData != null) {
            mData.remove(position);
        }
        notifyDataSetChanged();
    }

    public void clear() {
        if (mData != null) {
            mData.clear();
        }
        notifyDataSetChanged();
    }


    public static class ViewHolder {

        private SparseArray<View> mViews;   // 存储 ListView 的 item 中的 View
        private View item;                  // 存放 convertView
        private int position;               // 游标
        private Context context;            // Context上下文

        // 构造方法,完成相关初始化
        private ViewHolder(Context context, ViewGroup parent, int layoutRes) {
            mViews = new SparseArray<>();
            this.context = context;
            View convertView = LayoutInflater.from(context).inflate(layoutRes, parent, false);
            convertView.setTag(this);
            item = convertView;
        }

        //绑定 ViewHolder 与 item
        public static ViewHolder bind(Context context, View convertView, ViewGroup parent,
                                      int layoutRes, int position) {
            ViewHolder holder;
            if (convertView == null) {
                holder = new ViewHolder(context, parent, layoutRes);
            } else {
                holder = (ViewHolder) convertView.getTag();
                holder.item = convertView;
            }
            holder.position = position;
            return holder;
        }

        @SuppressWarnings("unchecked")
        public <T extends View> T getView(int id) {
            T t = (T) mViews.get(id);
            if (t == null) {
                t = (T) item.findViewById(id);
                mViews.put(id, t);
            }
            return t;
        }


        /**
         * 获取当前条目
         */
        public View getItemView() {
            return item;
        }

        /**
         * 获取条目位置
         */
        public int getItemPosition() {
            return position;
        }

        /**
         * 设置文字
         */
        public ViewHolder setText(int id, CharSequence text) {
            View view = getView(id);
            if (view instanceof TextView) {
                ((TextView) view).setText(text);
            }
            return this;
        }

        /**
         * 设置图片
         */
        public ViewHolder setImageResource(int id, int drawableRes) {
            View view = getView(id);
            if (view instanceof ImageView) {
                ((ImageView) view).setImageResource(drawableRes);
            } else {
                view.setBackgroundResource(drawableRes);
            }
            return this;
        }


        /**
         * 设置点击监听
         */
        public ViewHolder setOnClickListener(int id, View.OnClickListener listener) {
            getView(id).setOnClickListener(listener);
            return this;
        }

        /**
         * 设置可见
         */
        public ViewHolder setVisibility(int id, int visible) {
            getView(id).setVisibility(visible);
            return this;
        }

        /**
         * 设置标签
         */
        public ViewHolder setTag(int id, Object obj) {
            getView(id).setTag(obj);
            return this;
        }

        //其他方法可自行扩展

    }

}

我们写一个范例来试一试

范例

  1. 创建一个 空的 Android 项目 cn.twle.android.MsAdapter

  2. 下载 /static/i/android/it_language_icon.zip 解压并把所有的图片拖到 res/drawable 目录

  3. 修改 activity_main.xml 添加两个 ListView

    <?xml version="1.0" encoding="utf-8" ?>
    <LinearLayout 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="16dp" 
        android:orientation="vertical">
    
        <ListView
            android:id="@+id/list_book"
            android:layout_width="wrap_content"
            android:layout_weight="1"
            android:layout_height="wrap_content"/>
    
        <ListView
            android:id="@+id/list_lang"
            android:layout_width="wrap_content"
            android:layout_weight="1"
            android:layout_height="wrap_content" />
    
    </LinearLayout>
    
  4. res/layout 目录下新建 listview_lang_item.xml

    <?xml version="1.0" encoding="utf-8"?>
    <LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
        android:layout_width="match_parent"
        android:layout_height="match_parent"
        android:orientation="horizontal"
        android:padding="5dp">
    
        <ImageView
            android:id="@+id/icon"
            android:layout_width="32dp"
            android:layout_height="32dp"
            android:src="@drawable/scala" />
    
        <TextView
            android:id="@+id/name"
            android:layout_width="wrap_content"
            android:layout_height="wrap_content"
            android:text="Scala"
            android:textSize="20sp" />
    </LinearLayout>
    
  5. res/layout 目录下新建 listview_book_item.xml

    <?xml version="1.0" encoding="utf-8"?>
    <LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
        android:layout_width="match_parent"
        android:layout_height="match_parent"
        android:orientation="horizontal"
        android:padding="10dp">
    
        <TextView
            android:id="@+id/name"
            android:layout_width="wrap_content"
            android:layout_height="wrap_content"
            android:text="时间简史"
            android:textColor="#F3684A"
            android:textSize="18sp" />
    
        <TextView
            android:id="@+id/author"
            android:layout_width="wrap_content"
            android:layout_height="wrap_content"
            android:layout_marginLeft="20dp"
            android:text="史蒂芬·霍金"
            android:textColor="#44BDED"
            android:textSize="18sp" />
    </LinearLayout>
    
  6. MainActivity.java 同一个目录新建一个 Bean LanguageBean.java

    package cn.twle.android.msadapter;
    
    public class LanguageBean {
        private int icon;
        private String name;
    
        public LanguageBean() {
        }
    
        public LanguageBean(int icon, String name) {
            this.icon = icon;
            this.name = name;
        }
    
        public int getIcon() {
            return icon;
        }
    
        public String getName() {
            return name;
        }
    
        public void setIcon(int icon) {
            this.icon = icon;
        }
    
        public void setName(String name) {
            this.name = name;
        }
    }
    
  7. MainActivity.java 同一个目录新建一个 Bean BookBean.java

    package cn.twle.android.msadapter;
    
    public class BookBean {
        private String name;
        private String author;
    
        public BookBean() {
        }
    
        public BookBean(String name, String author) {
            this.name = name;
            this.author = author;
        }
    
        public String getName() {
            return name;
        }
    
        public String getAuthor() {
            return author;
        }
    
        public void setName(String name) {
            this.name = name;
        }
    
        public void setAuthor(String author) {
            this.author = author;
        }
    }
    
  8. 新建一个包 cn.twle.android.common 然后下载 /static/i/android/MsAdapter.java 放到该目录下

  9. 修改 MainActivity.java

    package cn.twle.android.msadapter;
    
    import cn.twle.android.common.MsAdapter;
    import android.content.Context;
    import android.os.Bundle;
    import android.support.v7.app.AppCompatActivity;
    import android.widget.ListView;
    
    import java.util.ArrayList;
    import java.util.List;
    
    public class MainActivity extends AppCompatActivity {
    
        private Context mContext;
        private ListView list_book;
        private ListView list_lang;
    
        private MsAdapter<LanguageBean> adapter1 = null;
        private MsAdapter<BookBean> adapter2 = null;
        private List<LanguageBean> mData1 = null;
        private List<BookBean> mData2 = null;
    
        @Override
        protected void onCreate(Bundle savedInstanceState) {
            super.onCreate(savedInstanceState);
            setContentView(R.layout.activity_main);
            mContext = MainActivity.this;
            init();
    
        }
    
        private void init() {
    
            list_book = (ListView) findViewById(R.id.list_book);
            list_lang = (ListView) findViewById(R.id.list_lang);
    
            //数据初始化
            mData1 = new ArrayList<LanguageBean>();
            mData1.add(new LanguageBean(R.drawable.kotlin,"Kotlin"));
            mData1.add(new LanguageBean(R.drawable.swift,"Swift"));
            mData1.add(new LanguageBean(R.drawable.scala,"Scala"));
    
            mData2 = new ArrayList<BookBean>();
            mData2.add(new BookBean("《时间简史(插图本)》","史蒂芬·霍金"));
            mData2.add(new BookBean("《梦幻花》","东野圭吾"));
            mData2.add(new BookBean("《原则》","瑞·达利欧"));
    
            // Adapter 初始化
            adapter1 = new MsAdapter<LanguageBean>((ArrayList)mData1,R.layout.listview_lang_item) {
                @Override
                public void bindView(ViewHolder holder, LanguageBean obj) {
                    holder.setImageResource(R.id.icon,obj.getIcon());
                    holder.setText(R.id.name,obj.getName());
                }
            };
    
            adapter2 = new MsAdapter<BookBean>((ArrayList)mData2,R.layout.listview_book_item) {
                @Override
                public void bindView(ViewHolder holder, BookBean obj) {
                    holder.setText(R.id.name,obj.getName());
                    holder.setText(R.id.author,obj.getAuthor());
                }
            };
    
            //ListView 设置 Adapter
    
            list_book.setAdapter(adapter2);
            list_lang.setAdapter(adapter1);
    
        }
    
    }
    

Android 基础教程

关于   |   FAQ   |   我们的愿景   |   广告投放   |  博客

  简单教程,简单编程 - IT 入门首选站

Copyright © 2013-2022 简单教程 twle.cn All Rights Reserved.