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

一、写在前面

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 是系统预设好的条目布局
      
    • 效果

      这里写图片描述

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

评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值