0
点赞
收藏
分享

微信扫一扫

ListView - 基本使用方法&适配器封装(参考鸿神)

兵部尚输 2023-02-01 阅读 92


一、写在前面

ListView 是 Android 中很重要的一个控件,我们举个栗子,QQ的TAB1是聊天记录列表,TAB2是联系人列表,TAB3常用的空间也是 ListView 列表.基本上一个APP就是若干个 ListView 加上一些按钮、图片等

二、基本使用方法之 ArrayAdapter & SimpleAdapter

  1. 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 是系统预设好的条目布局

  • 效果
  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
  1. 接口 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);
}

  1. 具体 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());
}
}
});
}



举报

相关推荐

0 条评论