Android ListView控件

来源:互联网 发布:劳动法教材推荐 知乎 编辑:程序博客网 时间:2024/06/05 03:50

使用ListView来展示数据需要以下三个元素:
1.Android提供的ListView的控件 —— 专门用来展示数据的界面
2.Adapter 适配器 —— 连接ListView和数据的桥梁
3.Data 数据 —— 需要在ListView上展示的数据

在以上三个元素中,Adpater起到了非常重要的作用,它把各种各样的数据抽象为统一的接口供ListView来使用。所以得名“适配器”。

列表的适配器一般分为三种: ArrayAdapter SimpleAdapter SimpleCursorAdapter, 其中以ArrayAdapter最为简单,只能展示一行字。SimpleAdapter有最好的扩充性,可以自定义出各种效果。SimpleCursorAdapter可以认为是SimpleAdapter对数据库的简单结合,可以方面的把数据库的内容以列表的形式展示出来。

一些较为简单的列表(比如ListView的每一项都是用做展示等等),以上的几种Adapter就完全足够用了,但是,如果ListView的某些项用来和用户交互,比如有可以点击的按钮,那么这时候上面的几种Adapter就不能满足我们的需要了,这时候我们必须从BaseAdapter派生出我们自己的Adapter来实现相应的功能。
具体的例子可以看该博客的内容:
点击查看链接

下面重点介绍关于ListView优化的一些知识。
1.使用getView()函数的参数ConvertView

getView方法里也提供了一个参数:convertView,这个参数代表着可以复用的view对象,当然这个对象也可能为空,当它为空的时候,表示该条目view第一次创建,所以我们需要inflate一个view出来,不为空时,直接复用:

@Override  public View getView(int position, View convertView, ViewGroup parent) {      View view;      // 判断convertView的状态,来达到复用效果      if (null == convertView) {           //如果convertView为空,则表示第一次显示该条目,需要创建一个view           view = View.inflate(MainActivity.this, R.layout.listview_item,null);       } else {           //否则表示可以复用convertView           view = convertView;       }       // listview_item里只有一个textview       TextView tv_item = (TextView) view.findViewById(R.id.tv_item);         tv_item.setText(list.get(position));       return view;  }

2.使用View Holder模式
上面的例子中,我们可以通过参数convertView来决定是不是需要inflate出来一个ItemView来达到优化的目的,但是呢,拿到convertView之后我们依然需要每次都通过函数findViewById()来查找对应的子控件,如果Layout布局比较复杂,那么这样的操作也会很耗时,所以可以从这里着手来进行进一步的优化:

private static class ViewHolder {     private TextView tvHolder; }@Override  public View getView(int position, View convertView, ViewGroup parent) {      View view;      ViewHolder holder;      // 判断convertView的状态,来达到复用效果      if (null == convertView) {           // 如果convertView为空,则表示第一次显示该条目,需要创建一个view           view = View.inflate(MainActivity.this, R.layout.listview_item, null);           //新建一个viewholder对象           holder = new ViewHolder();           //将findviewbyID的结果赋值给holder对应的成员变量           holder.tvHolder = (TextView) view.findViewById(R.id.tv_item);           // 将holder与view进行绑定           view.setTag(holder);       } else {           // 否则表示可以复用convertView           view = convertView;           holder = (ViewHolder) view.getTag();      }       // 直接操作holder中的成员变量即可,不需要每次都findViewById       holder.tvHolder.setText(list.get(position));       return view;  }

这样,每次在新创建ItemView的时候,将View和持有View上的子控件的引用的ViewHolder进行绑定,当getView函数对以前的ItemView进行复用的时候,不需要再次用函数findViewById()进行查找了,而是直接用ViewHolder持有的引用进行操作就可以了,从而达到优化效率的目的。

3.一个优化后的、通用的Adapter的例子:

import android.content.Context;import android.util.SparseArray;import android.view.LayoutInflater;import android.view.View;import android.view.ViewGroup;import android.widget.BaseAdapter;import android.widget.ImageView;import android.widget.TextView;import java.util.ArrayList;public abstract class MyAdapter<T> extends BaseAdapter {    private ArrayList<T> mData;   // 界面上的数据    private int mLayoutRes;       // item布局的资源id    public MyAdapter() {    }    public MyAdapter(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;    }    // 获取ItemView    @Override    public View getView(int position, View convertView, ViewGroup parent) {        // 拿到holder        ViewHolder holder = ViewHolder.bind(parent.getContext(), convertView, parent, mLayoutRes                , position);        // 数据与view相结合                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();    }    // viewHolder类的实现    public static class ViewHolder {        private SparseArray<View> mViews;   // 缓存ListView中的Item上的所有子item        private View item;                  // 该viewHolder所持有的item        private int position;               // item所对应的位置        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;        }        // 获取Item上的子view,如果有缓存则直接返回,否则需要进行查找并缓存        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;        }        // 获得当前持有的item        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;        }    }}

使用的时候实现BindView函数定义具体的绑定操作:

//Adapter初始化myAdapter1 = new MyAdapter<App>((ArrayList)mData1,R.layout.item_one) {     @Override     public void bindView(ViewHolder holder, App obj) {            holder.setImageResource(R.id.img_icon,obj.getaIcon());            holder.setText(R.id.txt_aname,obj.getaName());            } };

//////////////////////////////////////////////////////////////////////////////
RecyclerView也可以实现ListView的所有功能,但是功能比ListView要强大的多,详见博文