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

首先赞下hyman大神

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

。。

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中的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  : [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 = 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实现

时间: 2024-12-15 09:12:17

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

Android BaseAdapter和ViewHolder 优化

首先赞下hyman大神 以前只是简单的重写个BaseAdapter,将getView方法保持抽象.而ViewHolder没有抽象过... ViewHolder (用了一个集合+泛型管理存取view) /** * author : stone * email : [email protected] * time : 15/7/24 14 27 */ public class StoneViewHolder { private int mPosition; private View mConvert

Android中利用ViewHolder优化自定义Adapter的典型写法

public class MarkerItemAdapter extends BaseAdapter { private Context mContext = null; private List<MarkerItem> mMarkerData = null; public MarkerItemAdapter(Context context, List<MarkerItem> markerItems) { mContext = context; mMarkerData = mark

Android性能优化之------Listview优化

ListView的工作原理 发表于:2015/7/7  15:18:24 首先来了解一下ListView的工作原理(可参见http://mobile.51cto.com/abased-410889.htm),如图: ListView 针对每个item,要求 adapter “返回一个视图” (getView),也就是说ListView在开始绘制的时候,系统首先调用getCount()函数,根据他的返回值得到ListView的长度,然后根据这个长度,调用getView()一行一行的绘制ListVi

Android小白的成长之路-ListView优化提案

在android开发中Listview是一个很重要的组件,它以列表的形式根据数据的长自适应展示具体内容,用户可以自由的定义listview每一列的布局,但当listview有大量的数据需要加载的时候,会占据大量内存,影响性能. 本文的重点即是从如下几个方面介绍如何对ListView进行优化. 1.convertView重用 利用好 convertView 来重用 View,切忌每次 getView() 都新建.ListView 的核心原理就是重用 View,如果重用 view 不改变宽高,重用V

android 自定义adapter和线程结合 + ListView中按钮滑动后状态丢失解决办法

adapter+线程 1.很多时候自定义adapter的数据都是来源于服务器的,所以在获取服务器的时候就需要异步获取,这里就需要开线程了(线程池)去获取服务器的数据了.但这样有的时候adapter的中没有数据. 如下面的代码: 这就是在initData中异步获取服务器的数据,然后实例化adatper,再将adapter赋给listView. 2.initData()中的代码是: 这里线程要睡眠5秒钟,是为了模仿网络的耗时操作 3.Handler: 在Handler中接收到数据后给list赋值后,

Android之史上最强ListView优化方案

在android开发中Listview是一个很重要的组件,它以列表的形式根据数据的长自适应展示具体内容,用户可以自由的定义listview每一列的布局,但当listview有大量的数据需要加载的时候,会占据大量内存,影响性能. 本文的重点即是从如下几个方面介绍如何对ListView进行优化. 1.convertView重用 Android SDK中这样讲: the old view to reuse, if possible. Note: You should check that this v

Android内存优化解决 资料和总结的经验分享

在前公司做一个图片处理的应用时, 项目交付的时候,客户的手机在运行应用的时候,一直在崩溃,而这个异常就是OutOfMemory的错误,简称为OOM, 搞得我们也是极其的崩溃,最后 ,我们是通过网上搜集资料和代码走查的方式来优化解决的,这里,我就把我们收集到资料和总结的经验分享下吧. Android的虚拟机是基于寄存器的Dalvik,它的最大堆大小一般是16M,有的机器为24M.我们平常看到的OutOfMemory的错误,通常 是堆内存溢出.移动开发和web开发的最大的区别是设备资源受限,对一般手

[Android]对BaseAdapter中ViewHolder编写简化(转)

来自博客:http://www.cnblogs.com/tiantianbyconan/p/3642849.html 在Android项目中,经常都会用到ListView这个控件,而相应的Adapter中getView()方法的编写有一个标准的形式,如下: 1 @Override 2 public View getView(int position, View convertView, ViewGroup parent) { 3 ViewHolder holder; 4 if(null == c

Android 解决ListView在使用多个布局的同时使用convertView进行缓存时导致ListView下面有空白的问题

在使用ListView时,在Adapter里面使用convertView会提高ListView的性能,提升100%?但今天发现在listView同时加载不同的View的同时会导致ListView最底下有一块空白,可能是恰巧吧,导致这样的原因可能是加载了几个不同的View,而他们的高度不同,数量也不同,系统无法准确计算ListView的总高度. 在 BaseAdapter里面提供了两个回调函数来指定有多少种布局,指定position对应的是哪一个布局. @Override public int g