Android BaseAdapter和ViewHolder 优化 解决ListView的item抢焦点问题和item错乱问题

首先赞下hyman大神

曾经仅仅是简单的重写个BaseAdapter,将getView方法保持抽象。而ViewHolder没有抽象过。

。。


ViewHolder (用了一个集合+泛型管理存取view)

/**
 * author : stone
 * email  : aa86799@163.com
 * time   : 15/7/24 14 27
 */
public class StoneViewHolder {

    private int mPosition;
    private View mConvertView;
    private SparseArray<View> mViews;  //管理listView-item中的view

    public StoneViewHolder(Context context, int layoutId, int position, ViewGroup parent) {
        this.mPosition = position;

        this.mConvertView = LayoutInflater.from(context).inflate(layoutId, parent, false);
        this.mConvertView.setTag(this);

        this.mViews = new SparseArray<View>();
    }

    public View getConvertView() {
        return mConvertView;
    }

    public static StoneViewHolder getInstance(Context context, int layoutId, int position, View
            convertView, ViewGroup parent) {
        if (convertView == null) {
            return new StoneViewHolder(context, layoutId, position, parent);
        } else {
            StoneViewHolder holder = (StoneViewHolder) convertView.getTag();
            holder.mPosition = position;  //更新复用的convertView中  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 <T> void setTag(int viewId, T tag) {
        getView(viewId).setTag(tag);
    }

    public <T> T getTag(int viewId) {
        return (T) getView(viewId).getTag();
    }

    /*------------------------  设置view属性(以后扩展) --------------------------------*/

    public StoneViewHolder setText(int viewId, String text) {
        ((TextView)getView(viewId)).setText(text);
        return this;
    }

    public StoneViewHolder setText(int viewId, int resId) {//R.string.
        ((TextView)getView(viewId)).setText(resId);
        return this;
    }

    public StoneViewHolder setImageBitmap(int viewId, Bitmap bitmap) {
        ((ImageView)getView(viewId)).setImageBitmap(bitmap);
        return this;
    }

    public StoneViewHolder setImageResource(int viewId, int resId) {
        ((ImageView)getView(viewId)).setImageResource(resId);
        return this;
    }
}

Adapter

/**
 * author : stone
 * email  : aa86799@163.com
 * time   : 15/7/24 14 46
 */
public abstract class StoneListAdapter<T> extends BaseAdapter {

    private List<T> mData;
    private Context mContext;
    private int mLayoutID;

    public StoneListAdapter(Context context, int layoutID, List<T> data) {
        this.mContext = context;
        this.mLayoutID = layoutID;
        this.mData = data == null ? new ArrayList<T>() : data;
    }

    @Override
    public int getCount() {
        return mData.size();
    }

    @Override
    public T getItem(int position) {
        return mData.get(position);
    }

    @Override
    public long getItemId(int position) {
        return position;
    }

    @Override
    public View getView(int position, View convertView, ViewGroup parent) {
        StoneViewHolder holder = StoneViewHolder.getInstance(mContext, mLayoutID, position,
                convertView, parent);

        getView(mContext, holder, position);


        return holder.getConvertView();
    }

    protected abstract void getView(Context context, StoneViewHolder holder, int position);

}

在ListViewActivity中使用

stoneBaseAdapter = new StoneListAdapter<User>(ListViewActivity.this, R.layout.activity_listview_item, mData) {

    @Override
    protected void getView(Context context, final StoneViewHolder holder, final int position) {
        User user = getItem(position);

        holder.setText(R.id.tv_id, user.getId()).setText(R.id.tv_name, user.getName())
                .setText(R.id.tv_age, user.getAge() + "");

        holder.getView(R.id.btn_test).setOnClickListener(new View.OnClickListener() {
            @Override
            public void onClick(View v) {

            }
        });

    }
};


关于Adapter中View抢焦点

 假设 listView.setOnItemClickListener(listener);   且item中的  button.setOnClickListener(listener); 

 无论怎么点击,button会一直被触发...

 仅仅须要在item的root-layout中 加入 一个属性:   android:descendantFocusability="blocksDescendants"


关于item-view复用后。显示混乱:

有时条目过多,滑动到下一屏数据时。有些view复用后,view的状态(比方CheckBox的选种状态。ImageView的图片反复出现)会变乱。

一般处理呢。须要有一个机制,来管理一种相应关系: 当前position相应哪种状态

比方说checkBox选中状态混乱:

   

class MyAdapter extends StoneListAdapter<User> {
        private SparseBooleanArray mCheckStateArray;

        public MyAdapter(Context context, int layoutID, List data) {
            super(context, layoutID, data);
            this.mCheckStateArray = new SparseBooleanArray();
        }

        public void setChecked(int position, boolean isChecked) {
            mCheckStateArray.put(position, isChecked);
        }

        public boolean isChecked(int position) {
            return mCheckStateArray.get(position);
        }

        @Override
        protected void getView(Context context, final StoneViewHolder holder, final int position) {
            
            CheckBox cb = holder.getView(R.id.cb_check);
            cb.setOnCheckedChangeListener(new CompoundButton.OnCheckedChangeListener() {
                @Override
                public void onCheckedChanged(CompoundButton buttonView, boolean isChecked) {
                    setChecked(position, isChecked);//记录状态。防缓存显示
                }
            });
            cb.setChecked(isChecked(position));
        }
    }

关于SparseArray

 SparseArray 内部实现是Array数组。当长度不够时,会调用System.arrayCopy
        内部有 keys和values两个数组。


        put(key, value); 二分法查找key应该存放的位置  由于key是Integer类型
        put、get时 两个数组都是操作的同一个位置上的数据


    SparseArray 用于替代形如  HashMap<Integer, Object>
    SparseBooleanArray 用于替代形如  HashMap<Integer, Boolean>
    SparseIntArray 用于替代形如  HashMap<Integer, Integer>
    SparseLongArray 用于替代形如  HashMap<Integer, Long>

  support.v4.util.SparseArrayCompat 提供了v4包相应平台的 SparseArray实现


原文地址:https://www.cnblogs.com/yfceshi/p/6786035.html