一、写在前面
ListView 是 Android 中很重要的一个控件,我们举个栗子,QQ的TAB1是聊天记录列表,TAB2是联系人列表,TAB3常用的空间也是 ListView 列表.基本上一个APP就是若干个 ListView 加上一些按钮、图片等
二、基本使用方法之 ArrayAdapter
& SimpleAdapter
-
ArrayAdapter
:显示数组中各值-
用法(布局使用系统自带的)
String names[] = { "zhangsan", "lisi", "zhaoliu", "wangwu" }; lv.setAdapter(new ArrayAdapter<>(this, android.R.layout.simple_list_item_1, names)); // android.R.layout.simple_list_item_1 是系统预设好的条目布局
-
效果
-
-
SimpleAdapter
:数据一般为HashMap
构成的List
-
布局及用法
<?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="wrap_content" android:orientation="horizontal"> <ImageView android:id="@+id/iv_icon" android:layout_width="0dp" android:layout_height="wrap_content" android:layout_weight="1" /> <TextView android:id="@+id/tv_name" android:layout_width="0dp" android:layout_height="wrap_content" android:layout_weight="1" /> </LinearLayout>
List<Map<String, Object>> list = new ArrayList<>(); Map<String, Object> map; for (int i = 0; i < 5; i++){ map = new HashMap<>(); map.put("icon", R.mipmap.ic_launcher); map.put("name", "huluwa:" + i); list.add(map); } // param1:上下文 // param2:显示的数据集合 // param3:条目布局 // param4:Map 中 key 数组 // param5:条目布局中控件 id 数组 lv.setAdapter(new SimpleAdapter(this, list, R.layout.item_simpleadapter, new String[]{"icon", "name"}, new int[]{R.id.iv_icon, R.id.tv_name}));
-
效果
-
三、ListView 优化方案
-
复用 convertView 缓存(减少 ListView 绘制).
-
自定义静态类 ViewHolder(减少 findViewById 次数),通过 setTag()、getTag() 获取 holder 实例. 声明为静态是为了避免对外部类(如 Activity)对象的引用.
-
使用分页加载.
-
使用软引用 WeakReference 引用 ImageView 对象:当 item 划出屏幕,可以释放对大图片的引用,节省内存,提高效率.
-
另外对图片还可以使用 LruCache 和三级缓存来优化.
四、常规使用方式及分析
public class MyAdapter extends BaseAdapter {
private LayoutInflater mInflater;
private Context ctx;
private List<String> mDatas;
public MyAdapter(Context ctx, List<String> mDatas) {
mInflater = LayoutInflater.from(ctx);
this.ctx = ctx;
this.mDatas = mDatas;
}
@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缓存
convertView = mInflater.inflate(R.layout.item_str, null); // 条目布局
holder = new ViewHolder();
holder.tv = (TextView) convertView.findViewById(R.id.tv); // holder存储控件
convertView.setTag(holder);
} else {
holder = (ViewHolder) convertView.getTag();
}
holder.tv.setText(mDatas.get(position));
return convertView;
}
static class ViewHolder {
TextView tv;
}
}
上述代码大家都很熟悉,基本遇到就是一套,都写烂了,分析一下
-
复写的三个方法都是固定的,很好抽取
-
getView 中通过 set、get 处理 ViewHolder 对象可以抽取
-
holder 中的 findViewById 可以抽取
五、提炼
-
PART_A Adapter
package com.catface.cadapter; import android.content.Context; import android.view.LayoutInflater; import android.view.View; import android.view.ViewGroup; import android.widget.BaseAdapter; import java.util.List; /** * @desc 使用泛型,接收不同类型的对象 */ public abstract class CAdapter<T> extends BaseAdapter { protected Context ctx; protected List<T> datas; protected LayoutInflater inflater; private int layoutId; // arg1-3:上下文 | 集合<泛型bean> | 条目布局 public CAdapter(Context ctx, List<T> datas, int layoutId) { this.ctx = ctx; this.datas = datas; this.inflater = LayoutInflater.from(ctx); this.layoutId = layoutId; } @Override public int getCount() { return datas == null ? 0 : datas.size(); } @Override public T getItem(int position) { return datas == null ? null : datas.get(position); } @Override public long getItemId(int position) { return position; } /** * @desc 1.获取ViewHolder对象(上下文、复用缓存、父布局、条目布局) */ @Override public View getView(int position, View convertView, ViewGroup parent) { ViewHolder holder = ViewHolder.get(ctx, convertView, parent, layoutId, position); convert(holder, getItem(position)); // ------>> 暴露抽象方法 return holder.getConvertView(); // 设置结束各个组件,返回ViewHolder对象方便继续设置控件值 } /** * @desc 暴露出去并传入holder与bean:拿着控件设置值 */ public abstract void convert(ViewHolder holder, T t); }
-
PART_B ViewHolder
package com.catface.cadapter; import android.annotation.SuppressLint; import android.content.Context; import android.graphics.Bitmap; import android.graphics.Paint; import android.graphics.Typeface; import android.graphics.drawable.Drawable; import android.os.Build; import android.text.util.Linkify; import android.util.SparseArray; import android.view.LayoutInflater; import android.view.View; import android.view.ViewGroup; import android.view.animation.AlphaAnimation; import android.widget.Checkable; import android.widget.ImageView; import android.widget.ProgressBar; import android.widget.RatingBar; import android.widget.TextView; /** * @desc 待修复问题: * * 1.CheckBox选中错位(复用convertView所致) √ * a.在Bean中添加isChecked字段 * b.在ListView中用ArrayList数组记录holder中当前选中的CheckBox * * 2.图片显示错位 × * * 3.上拉刷新 + 下拉加载 × */ public class ViewHolder { private Context ctx; // 上下文 private View convertView; // 复用缓存 private int mLayoutId; // 条目布局 private SparseArray<View> views; // 各组件及对应id(SparseArray代替HashMap,提高效率) private int position; public ViewHolder(Context ctx, ViewGroup parent, int layoutId, int position) { this.ctx = ctx; this.convertView = LayoutInflater.from(ctx).inflate(layoutId, parent, false); this.mLayoutId = layoutId; this.views = new SparseArray<View>(); this.position = position; this.convertView.setTag(this); } /** * @desc 通过setTag()和getTag()来获取ViewHolder对象 */ public static ViewHolder get(Context ctx, View convertView, ViewGroup parent, int layoutId, int position) { if (convertView == null) { return new ViewHolder(ctx, parent, layoutId, position); } else { ViewHolder holder = (ViewHolder) convertView.getTag(); holder.position = position; return holder; } } /** * @desc 获取当前holder的position */ public int getPosition() { return position; } public int getLayoutId() { return mLayoutId; } /** * @desc 通过viewId获取控件 */ @SuppressWarnings("unchecked") public <T extends View> T getViewByViewId(int viewId) { View view = views.get(viewId); if (view == null) { view = convertView.findViewById(viewId); views.put(viewId, view); } return (T) view; } public View getConvertView() { return convertView; } /************************************************ PS:图片可以使用第三方工具类加载图片 ************************************************/ /** 设置TextView文本 */ public ViewHolder setText(int viewId, String text) { ((TextView) getViewByViewId(viewId)).setText(text); return this; } /** 设置TextView颜色 */ public ViewHolder setTextColor(int viewId, int textColor) { ((TextView) getViewByViewId(viewId)).setTextColor(textColor); return this; } /** 设置TextView颜色 */ public ViewHolder setTextColorRes(int viewId, int textColorRes) { ((TextView) getViewByViewId(viewId)).setTextColor(ctx.getResources().getColor(textColorRes)); return this; } /** 设置文本字体 */ public ViewHolder setTypeface(Typeface typeface, int... viewIds) { for (int viewId : viewIds) { TextView view = getViewByViewId(viewId); view.setTypeface(typeface); view.setPaintFlags(view.getPaintFlags() | Paint.SUBPIXEL_TEXT_FLAG); } return this; } /** 设置图片:资源id */ public ViewHolder setImageResource(int viewId, int resId) { ((ImageView) getViewByViewId(viewId)).setImageResource(resId); return this; } /** 设置图片:Bitmap */ public ViewHolder setImageBitmap(int viewId, Bitmap bitmap) { ((ImageView) getViewByViewId(viewId)).setImageBitmap(bitmap); return this; } /** 设置图片:Drawable */ public ViewHolder setImageDrawable(int viewId, Drawable drawable) { ((ImageView) getViewByViewId(viewId)).setImageDrawable(drawable); return this; } /** 设置背景色 */ public ViewHolder setBackgroundColor(int viewId, int color) { getViewByViewId(viewId).setBackgroundColor(color); return this; } /** 设置背景色 */ public ViewHolder setBackgroundRes(int viewId, int backgroundRes) { getViewByViewId(viewId).setBackgroundResource(backgroundRes); return this; } @SuppressLint("NewApi") public ViewHolder setAlpha(int viewId, float value) { if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.HONEYCOMB) { getViewByViewId(viewId).setAlpha(value); } else { // Pre-honeycomb hack to set Alpha value AlphaAnimation alpha = new AlphaAnimation(value, value); alpha.setDuration(0); alpha.setFillAfter(true); getViewByViewId(viewId).startAnimation(alpha); } return this; } /** 设置可见状态 */ public ViewHolder setVisible(int viewId, boolean visible) { getViewByViewId(viewId).setVisibility(visible ? View.VISIBLE : View.GONE); return this; } public ViewHolder linkify(int viewId) { TextView view = getViewByViewId(viewId); Linkify.addLinks(view, Linkify.ALL); return this; } /** 设置进度 */ public ViewHolder setProgress(int viewId, int progress) { ((ProgressBar) getViewByViewId(viewId)).setProgress(progress); return this; } /** 设置最大进度 */ public ViewHolder setMax(int viewId, int max) { ProgressBar view = getViewByViewId(viewId); view.setMax(max); return this; } /** 设置进度、最大进度 */ public ViewHolder setProgress(int viewId, int progress, int max) { ProgressBar view = getViewByViewId(viewId); view.setMax(max); view.setProgress(progress); return this; } public ViewHolder setRating(int viewId, float rating) { RatingBar view = getViewByViewId(viewId); view.setRating(rating); return this; } public ViewHolder setRating(int viewId, float rating, int max) { RatingBar view = getViewByViewId(viewId); view.setMax(max); view.setRating(rating); return this; } public ViewHolder setTag(int viewId, Object tag) { View view = getViewByViewId(viewId); view.setTag(tag); return this; } public ViewHolder setTag(int viewId, int key, Object tag) { View view = getViewByViewId(viewId); view.setTag(key, tag); return this; } public ViewHolder setChecked(int viewId, boolean checked) { Checkable view = (Checkable) getViewByViewId(viewId); view.setChecked(checked); return this; } /** 设置点击事件 */ public ViewHolder setOnClickListener(int viewId, View.OnClickListener listener) { getViewByViewId(viewId).setOnClickListener(listener); return this; } /** 设置触摸事件 */ public ViewHolder setOnTouchListener(int viewId, View.OnTouchListener listener) { getViewByViewId(viewId).setOnTouchListener(listener); return this; } /** 设置长按事件 */ public ViewHolder setOnLongClickListener(int viewId, View.OnLongClickListener listener) { getViewByViewId(viewId).setOnLongClickListener(listener); return this; } }
-
PART_C 多条目的Adapter
-
接口 Adapter
package com.catface.cadapter; /** * @desc 多条目ListView的Adapter接口 */ public interface IMultiCAdapter<T> { int getLayoutId(int position, T t); int getViewTypeCount(); int getItemViewType(int postion, T t); }
-
具体 Adapter
package com.catface.cadapter; import android.content.Context; import android.view.View; import android.view.ViewGroup; import java.util.List; /** * @desc 多条目的Adapter */ public abstract class MultiCAdapter<T> extends CAdapter<T> { protected IMultiCAdapter<T> mMultiItemTypeSupport; public MultiCAdapter(Context context, List<T> datas, IMultiCAdapter<T> multiItemTypeSupport) { super(context, datas, -1); this.mMultiItemTypeSupport = multiItemTypeSupport; } @Override public int getViewTypeCount() { if (mMultiItemTypeSupport != null) { return mMultiItemTypeSupport.getViewTypeCount(); } return super.getViewTypeCount(); } @Override public int getItemViewType(int position) { if (mMultiItemTypeSupport != null) { return mMultiItemTypeSupport.getItemViewType(position, datas.get(position)); } return super.getItemViewType(position); } @Override public View getView(int position, View convertView, ViewGroup parent) { if (mMultiItemTypeSupport == null) { return super.getView(position, convertView, parent); } int layoutId = mMultiItemTypeSupport.getLayoutId(position, getItem(position)); ViewHolder viewHolder = ViewHolder.get(ctx, convertView, parent, layoutId, position); convert(viewHolder, getItem(position)); return viewHolder.getConvertView(); } }
-
六、提炼成果
-
普通条目调用方式
setListAdapter(new CAdapter<Person>(getActivity(), datas, R.layout.item_fragment1) { @Override public void convert(ViewHolder viewHolder, Person person) { viewHolder.setText(R.id.tv_name, person.getName()).setText(R.id.tv_age, person.getAge() + "").setImageResource(R.id.iv_icon, person.getIcon()); } });
-
带CheckBox等控件,解决其抢占item事件的方法
-
条目布局父控件:
android:descendantFocusability="blocksDescendants"
-
CheckBox:
android:focusable="false"
setListAdapter(new CAdapter<Person>(getActivity(), datas, R.layout.item_fragment1up) { private List<Integer> points = new ArrayList<Integer>(); @Override public void convert(final ViewHolder viewHolder, Person person) { viewHolder.setText(R.id.tv_name, person.getName()).setText(R.id.tv_age, person.getAge() + "").setImageResource(R.id.iv_icon, person.getIcon()); final CheckBox cb = (CheckBox) viewHolder.getViewByViewId(R.id.cb_f1up); cb.setChecked(false); // 先置为false if (points.contains(viewHolder.getPosition())) { cb.setChecked(true); } cb.setOnClickListener(new View.OnClickListener() { @Override public void onClick(View v) { if (cb.isChecked()) { points.add(viewHolder.getPosition()); } else { points.remove((Integer) viewHolder.getPosition()); } } }); }
-
适配器封装参考鸿神 http://blog.csdn.net/lmj623565791/article/details/38902805.