ListView的使用及打造万能Adapter
ListView学习
ListView与RecyclerView的不同
- ListView可以用OnItemClickListener方便地对Item的点击事件作出回应。
- RecyclerView可以通过LayoutManager实现不同的布局效果。并且可设置横向或纵向滑动
- ListView本身没有ViewHolder,需要自己写ViewHolder来对其进行优化,而Recycler有了自己的ViewHolder,规范化了ViewHolder的写法。
- ListView可以设置选择模式,并添加MultiChoiceModeListener。
- RecyclerView在默认情况下并不在item之间展示间隔符,而在ListView中如果我们想要在item之间添加间隔符,我们只需要在布局文件中对ListView添加如下属性即可
android:divider="@android:color/transparent"
android:dividerHeight="5dp"
打造ListView的万能Adapter
传统的Adapter写法
public class MyAdapter extends BaseAdapter {
private List<Book> mDatas;
private LayoutInflater mInflater;
public MyAdapter(Context context, List<Book> datas){
mInflater = LayoutInflater.from(context);
mDatas = datas;
}
@Override
public int getCount() {
return mDatas.size();
}
@Override
public Object 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 holder = null;
if (convertView == null){
convertView = mInflater.inflate(R.layout.item_list,parent,false);
holder = new ViewHolder();
holder.mName = convertView.findViewById(R.id.tv_bookname);
holder.mDesc = convertView.findViewById(R.id.tv_desc);
holder.mPrice = convertView.findViewById(R.id.tv_price);
convertView.setTag(holder);
}else{
holder = (ViewHolder) convertView.getTag();
}
Book book = mDatas.get(position);
holder.mName.setText(book.getName());
holder.mDesc.setText(book.getDesc());
holder.mPrice.setText("¥"+String.valueOf(book.getPrice()));
return convertView;
}
private class ViewHolder{
TextView mName;
TextView mDesc;
TextView mPrice;
}
}
可以发现,传统的Adapter非常长,并且其中有许多重复的代码,我们完全可以把它们抽出来封装成一个更简单的Adapter,所以我们在这里做一个万能的Adapter。
打造通用ViewHolder
传统的ViewHolder是通过convertView的setTag,然后在后面进行获取holder的
convertView.setTag(holder);
ViewHolder中存放了Item中各种控件的引用。然后再在getView方法中来获取初始化ViewHolder等操作。
现在由于我们需要做成一个通用的,所以我们需要一个容器,来存储我们的控件。这里存储控件的容器,由于Map效率不是那么高,所以我们这里使用SparseArray来存储。它实际上也是Map,key为Integer,value为Object,它比传统的HashMap效率更高,以后若是key是Integer的Map,均可以用它。然后可以通过ViewHolder的getView(int id)来获取到我们的控件。
代码如下
public class ViewHolder {
private SparseArrayView mViews;
private int mPosition;
private View mConvertView;
public ViewHolder(Context context, ViewGroup parent,int layoutId,int position){
this.mPosition = position;
this.mViews = new SparseArray<View>();
mConvertView = LayoutInflater.from(context).inflate(layoutId,parent,false);
mConvertView.setTag(this);
}
public static ViewHolder get(Context context,View convertView,ViewGroup parent,
int layoutId,int position){
if (convertView == null){
return new ViewHolder(context,parent,layoutId,position);
}else{
ViewHolder holder = (ViewHolder) convertView.getTag();
holder.mPosition = position; //更新位置
return holder;
}
}
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;
}
}
下面是我们运用了通用ViewHolder的Adapter,可以看到,相比原来的传统Adapter,代码量减少了一些。
public class MyAdapterWithViewHolder extends BaseAdapter {
private List<Book> mDatas;
private LayoutInflater mInflater;
private Context mContext;
public MyAdapterWithViewHolder(Context context, List<Book> datas){
mInflater = LayoutInflater.from(context);
mDatas = datas;
mContext = context;
}
@Override
public int getCount() {
return mDatas.size();
}
@Override
public Object 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 holder = ViewHolder.get(mContext,convertView,parent, R.layout.item_list,position);
Book book = mDatas.get(position);
TextView mName = holder.getView(R.id.tv_bookname);
TextView mDesc = holder.getView(R.id.tv_desc);
TextView mPrice = holder.getView(R.id.tv_price);
mName.setText(book.getName());
mDesc.setText(book.getDesc());
mPrice.setText("¥"+String.valueOf(book.getPrice()));
return holder.getConvertView();
}
}
二、打造通用的Adapter
观察传统的Adapter的代码可以发现,除了getView的代码不太一样以外,其余基本都大同小异,因此我们可以建立一个CommonAdapter类,抽取重复部分。
public abstract class CommonAdapterT extends BaseAdapter {
protected Context mContext;
protected ListT mDatas;
protected LayoutInflater mInflater;
public CommonAdapter(Context context, List<T> datas) {
this.mDatas = datas;
mInflater = LayoutInflater.from(context);
this.mContext = context;
}
@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 holder = ViewHolder.get(mContext,convertView,parent,R.layout.item_list,position);
convert(holder,mDatas.get(position));
return holder.getConvertView();
}
//将getView方法继续抽,抽掉return语句以及初始化holder语句。
public abstract void convert(ViewHolder holder,T t);
}
于是,当我们写Adapter时,就只需要这样写就好了:
public class MyAdapterWithViewHolder extends CommonAdapterBook {
public MyAdapterWithViewHolder(Context context, ListBook datas){
super(context,datas);
}
@Override
public void convert(ViewHolder holder, Book book) {
TextView mName = holder.getView(R.id.tv_bookname);
TextView mDesc = holder.getView(R.id.tv_desc);
TextView mPrice = holder.getView(R.id.tv_price);
mName.setText(book.getName());
mDesc.setText(book.getDesc());
mPrice.setText("¥"+String.valueOf(book.getPrice()));
}
}
可以发现,代码相对于我们传统的Adapter,减少了接近60行代码。
三、够简单了么?还可以更简单!
我们还可以在ViewHolder中为TextView专门设计设置文字的方法,为ImageView专门设计设置图片的方法等等
public class ViewHolder {
private SparseArrayView mViews;
private int mPosition;
private View mConvertView;
public ViewHolder(Context context, ViewGroup parent,int layoutId,int position){
this.mPosition = position;
this.mViews = new SparseArray<View>();
mConvertView = LayoutInflater.from(context).inflate(layoutId,parent,false);
mConvertView.setTag(this);
}
public static ViewHolder get(Context context,View convertView,ViewGroup parent,
int layoutId,int position){
if (convertView == null){
return new ViewHolder(context,parent,layoutId,position);
}else{
ViewHolder holder = (ViewHolder) convertView.getTag();
holder.mPosition = position; //更新位置
return holder;
}
}
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,CharSequence text){
TextView textView = getView(viewId);
textView.setText(text);
return this;
}
//设置ImageView的图片(resource)
public ViewHolder setImageResource(int viewId,int resId){
ImageView imageView = getView(viewId);
imageView.setImageResource(resId);
return this;
}
//设置ImageView的图片(bitmap)
public ViewHolder setImageBitmap(int viewId, Bitmap bitmap){
ImageView imageView = getView(viewId);
imageView.setImageBitmap(bitmap);
return this;
}
//设置ImageView的图片(drawable)
public ViewHolder setImageResource(int viewId, Drawable drawable){
ImageView imageView = getView(viewId);
imageView.setImageDrawable(drawable);
return this;
}
//设置ImageView的图片(url)
public ViewHolder setImageUrl(int viewId, String url){
ImageView imageView = getView(viewId);
//比如我这里有一个ImageLoader类
//ImageLoader.getInstance().loadImg(view,url);
return this;
}
}
这样我们使用的时候只需要这样就可以成功写出Adapter了。
可以发现,代码量减少了非常非常多。
public void convert(ViewHolder holder, final Book book) {
holder.setText(R.id.tv_bookname,book.getName())
.setText(R.id.tv_desc,book.getDesc())
.setText(R.id.tv_price,String.valueOf(book.getPrice()));
}