使用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要强大的多,详见博文