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

采得百花成蜜后,为谁辛苦为谁甜。这篇文章主要讲述Android BaseAdapter和ViewHolder 优化 解决ListView的item抢焦点问题和item错乱问题相关的知识,希望能为你提供帮助。
首先赞下hyman大神
曾经仅仅是简单的重写个BaseAdapter,将getView方法保持抽象。而ViewHolder没有抽象过。
。。



【Android BaseAdapter和ViewHolder 优化 解决ListView的item抢焦点问题和item错乱问题】ViewHolder (用了一个集合+泛型管理存取view)

/** * author : stone * email: [email  protected] * time: 15/7/24 14 27 */ public class StoneViewHolder {private int mPosition; private View mConvertView; private SparseArray< View> mViews; //管理listView-item中的viewpublic 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: [email  protected] * 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 = https://www.songbingjia.com/android/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实现























    推荐阅读