Android_ListView适配器

ListView如何优化

  • 复用convertView缓存(减少ListView绘制).
  • 自定义静态类ViewHolder(减少findViewById次数),通过setTag()、getTag()获取holder实例.
  • 使用分页加载.
  • 使用软引用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;
    }

    private final 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

  • 接口
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());
            }
        }
    });
}
posted @ 2016-09-28 19:18  陨落的星辰  阅读(330)  评论(0编辑  收藏  举报