Android:通用的ListView数据适配器

在写Android程序时,经常会用到ListView控件进行数据展示。
使用时,需要定义一个ListView并创建其Item布局,然后对ListView设置一个适配器adapter,一般继承自BaseAdapter,同时需要定义一个ViewHolder类存储控件。但是当有很多的ListView时,这种做法就比较麻烦了,我们可以发现其中有很多通用的代码。这时候就需要抽象出一个共同的适配器出来。

(一)ListView的用法

页面布局文件和Item布局文件

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=".MainActivity">
    <ListView
        android:id="@+id/lv"
        android:layout_width="match_parent"
        android:layout_height="match_parent"
        android:layout_margin="5dp"
        android:divider="#d9d9d9"
        android:dividerHeight="1dp">
    </ListView>
</RelativeLayout>

list_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:gravity="center_vertical">
    <ImageView
        android:id="@+id/item_image"
        android:layout_width="48dp"
        android:layout_height="48dp"
        android:layout_margin="8dp"
        android:background="@drawable/wx" />
    <TextView
        android:id="@+id/item_tv"
        android:layout_width="wrap_content"
        android:layout_height="wrap_content"
        android:text="我是ListView的Item布局"
        android:textSize="18sp" />
</LinearLayout>

Adapter和ViewHolder

构建一个数据适配器MyBaseAdapter继承BaseAdapter,此处将MyBaseAdapter类设置成MainActivity的内部类,以便对一些成员进行直接调用。在编写一个ViewHolder类,包含Item中的两个子控件。
ViewHolder的作用:通过convertView.setTag(ViewHolder v)方法与convertView进行绑定,然后当convertView复用时,直接从与之对于的convertView.getTag()中拿到convertView布局中的控件,省去了findViewById的时间

public class MainActivity extends AppCompatActivity {
    ListView mListView;
    //需要适配的数据
    String[] names = {"京东商城", "QQ", "QQ斗地主", "新浪微博", "天猫",
            "UC浏览器", "微信"};
    //图片集合
    int[] icons = {R.drawable.jd, R.drawable.qq, R.drawable.dz,
            R.drawable.xl, R.drawable.tm, R.drawable.uc, R.drawable.wx};
    @Override
    protected void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        setContentView(R.layout.activity_main);
        //初始化ListView控件
        mListView = (ListView) findViewById(R.id.lv);
        //创建一个Adapter的实例
        MyBaseAdapter mAdapter = new MyBaseAdapter();
        //设置Adapter
        mListView.setAdapter(mAdapter);
    }
    class MyBaseAdapter extends BaseAdapter {
        //得到item的总数
        @Override
        public int getCount() {
            //返回ListView Item条目的总数
            return names.length;
        }
        //得到Item代表的对象
        @Override
        public Object getItem(int position) {
            //返回ListView Item条目代表的对象
            return names[position];
        }
        //得到Item的id
        @Override
        public long getItemId(int position) {
            //返回ListView Item的id
            return position;
        }
        //得到Item的View视图
        @Override
        public View getView(int position, View convertView, ViewGroup parent) {
            ViewHolder holder;
            //在第一次创建convertView时将控件找出,在第二次重用convertView时直接通过getTag()方法获得这些控件
            if (convertView == null) {
                convertView = LayoutInflater.from(getApplicationContext()).
                        inflate(R.layout.list_item,parent,false);
                holder = new ViewHolder();
                holder.mTextView = (TextView)convertView.findViewById(R.id. item_tv);
                holder.imageView=(ImageView) convertView.findViewById(R.id.item_image);
                convertView.setTag(holder);
            } else {
                holder = (ViewHolder) convertView.getTag();
            }
            holder.mTextView.setText(names[position]);
            holder.imageView.setBackgroundResource(icons[position]);
            return convertView;
        }
        class ViewHolder {
            TextView mTextView;
            ImageView imageView;
        }
}

运行结果:
在这里插入图片描述

(二)抽象一个通用的ListView的适配器

布局文件activity_main.xml文件和Item的布局list_item.xml同上。

Java Bean类

新建一个Java Bean类,用于描述ListView中每一个Item的信息

此例中每一个item包括一个文本、一个图片(用资源号表示)

public class Bean {
    int pic; //对应Item的图片资源号
    String text;//对应Item的文本
    public Bean(int pic, String text) {
        this.pic = pic;
        this.text = text;
    }
    public int getPic() {
        return pic;
    }
    public void setPic(int pic) {
        this.pic = pic;
    }
    public String getText() {
        return text;
    }
    public void setText(String text) {
        this.text = text;
    }
}

通用的ViewHolder类

在上面的例子中,对于每一个ListView,都需要一个ViewHolder来存储Item中包含的控件,在通用的ViewHolder类中我们需要一个容器SparseArray(SparseArrays
map integers to Objects)存储一个id及其对应的view控件,我们再新建一个getView(int id),通过id获取到我们的控件即可。
同时可以为一些常用的控件设置属性值的方法。

public class ViewHolder {
    //通用的ViewHolder类,提供一个容器,专门存每个Item布局中的所有控件
    private final SparseArray<View> mViews;
    //android提供的SparseArray类,和Map类似,但是比Map效率,不过键只能为Integer
    //通过键值对保存Item中的所有控件,键为控件ID,值为控件引用
    private View mConvertView;
    private int mPosition;
    private ViewHolder(Context context, ViewGroup parent, int layoutId, int position) {
        this.mViews = new SparseArray<View>();
        this.mPosition=position;
        mConvertView = LayoutInflater.from(context).inflate(layoutId, parent, false);
        //setTag
        mConvertView.setTag(this);
    }
    //拿到一个ViewHolder对象
    public static ViewHolder get(Context context, View convertView,
                                 ViewGroup parent, int layoutId, int position) {
        if (convertView == null) {
            return new ViewHolder(context, parent, layoutId, position);
        }
        return (ViewHolder) convertView.getTag();
    }

    /**
     * 通过控件的Id获取对于的控件,如果没有则加入views
     * 当需要拿这些控件时,通过getView(id)进行获取;
     */
    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;
    }

    // 为TextView设置字符串
    public ViewHolder setText(int viewId, String text) {
        TextView view = getView(viewId);
        view.setText(text);
        return this;
    }

    //为ImageView设置图片

    public ViewHolder setImageResource(int viewId, int drawableId) {
        ImageView view = getView(viewId);
        view.setImageResource(drawableId);
        return this;
    }
    //为ImageView设置图片
    public ViewHolder setImageBitmap(int viewId, Bitmap bm) {
        ImageView view = getView(viewId);
        view.setImageBitmap(bm);
        return this;
    }
    public int getPosition() {
        return mPosition;
    }
}

通用的Adapter类

构建一个通用的Adapter类,继承BaseAdapter类,指定其泛型,以便适应不同的Item。指定其为抽象类,以便在具体实现时设置不同控件的属性值

//编写通用的Adapter,指定泛型,以便适应不同的Java bean
public abstract class CommonAdapter<T> extends BaseAdapter {
    protected LayoutInflater mInflater;
    protected Context mContext;
    protected List<T> mDatas;
    protected final int mItemLayoutId;
    public CommonAdapter(Context context, List<T> mDatas, int itemLayoutId) {
        this.mContext = context;
        this.mInflater = LayoutInflater.from(mContext);
        this.mDatas = mDatas;
        this.mItemLayoutId = itemLayoutId;
    }
    @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 = getViewHolder(position, convertView, parent);
        convert(viewHolder, getItem(position));//getItem(position)的类型就是T,这句话在子类中的具体实现就是给具体的控件初始化
        //并赋值,初始化赋值控件时需要viewHolder和具体的数据Java bean,在这里抽象出来就是类型T
        return viewHolder.getConvertView();
    }

    public abstract void convert(ViewHolder helper, T item);

    private ViewHolder getViewHolder(int position, View convertView, ViewGroup parent) {
        return ViewHolder.get(mContext, convertView, parent, mItemLayoutId, position);
    }
}

设置数据源初始化Java Bean

在MainActivity.java文件中设置数据源初始化Java Bean,在设置Adapter时实现CommonAdapter类的抽象方法,实现为不同的Item设置数据

public class MainActivity extends AppCompatActivity {
    //需要适配的数据
    String[] names = {"京东商城", "QQ", "QQ斗地主", "新浪微博", "天猫", "UC浏览器", "微信"};
    //图片集合
    int[] icons = {R.drawable.jd, R.drawable.qq, R.drawable.dz,
            R.drawable.xl, R.drawable.tm, R.drawable.uc, R.drawable.wx};
    List<Bean> mDatas;//数据源  每一个item中的数据 由names和icons组成
    ListView listView;

    @Override
    protected void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        setContentView(R.layout.activity_main);
        initData();
        listView = findViewById(R.id.lv);
        listView.setAdapter(new CommonAdapter<Bean>(getApplicationContext(), mDatas, R.layout.list_item) {
            @Override
            public void convert(ViewHolder helper, Bean item) {
                helper.setText(R.id.item_tv, item.getText());//为对应的TextView设置文本
                helper.setImageResource(R.id.item_image, item.getPic());//为对应的ImageView设置图片
            }
        });
    }

    public void initData() {  //初始化数据源
        mDatas = new ArrayList<Bean>();
        for (int i = 0; i < names.length; i++) {
            Bean bean = new Bean(icons[i], names[i]);
            mDatas.add(bean);
        }
    }
}

至此,成功构建了一个通用的ListView数据适配器模板,今后在使用ListView并进行数据适配时,编写与Item中对应的Java Bean,在ViewHolder类中编写设置所用控件属性值的方法,然后在Activity中实例化即可。

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值