在Adapter中使用Holder的那些坑

??在使用GridView、ListView时,通常会在Adapter中采用Holder缓存每一项以提高效率,但如果没有用好Holder,这个缓存机制会导致许多意想不到的问题,结合自己的经验特地总结一下,以免今后再犯。

内容错乱

??在Adapter的getView方法中通过position更新每一项的内容,对于根据判断条件给每一项设置属性的情况,每个判断条件下都需要给每一项的每个属性赋值,否则在滑动ListView或GridView时会导致内容错乱,比如下面这段代码是在getView中调用的一个方法,如果在使用Holder时,只在if(position == mSelectPosition)分支下给项背景设置属性,在滑动过程中会导致本来不应该是蓝色背景的项背景变成蓝色了:

private void setFileInfo( FileInfo fileInfo, int position ){
    mFileHolder.mFilePathTxtView.setText( fileInfo.getFilePath( ) );
    mFileHolder.mVoiceTypeTxtView.setText( fileInfo.getVoiceType( ) );
    mFileHolder.mFileItemLayout.setTag(position);

    if(position == mSelectPosition){
        mFileHolder.mFileItemLayout.setBackgroundColor( Color.BLUE );
    }else{
        // 每一项的else分值也需要为项设置背景,否则会在滑动过程中导致内容错乱
        mFileHolder.mFileItemLayout.setBackgroundColor( Color.RED );
    }
}

??这个问题虽然很容易处理,但在开发的过程中经常遇到,比如根据判断条件为每一项设置不同的背景、头像或者文本时,稍有不注意或者偷懒都会导致意想不到的bug,特别是对于每一项需要同时修改多个属性的时候就要更加注意了,一定要保证每个条件分值下都为项的每个视图都设置属性了。

setTag

??在上面那段代码中增加监听项点击事件,由于用到了缓存,所以不能通过getId来获取具体点击了哪一项,通常我们都是通过给每一项的layout设置tag,在监听回调中getTag来获取具体点击了哪一项,比如下面这样:

private void setFileInfo( FileInfo fileInfo, int position ){
    mFileHolder.mFilePathTxtView.setText( fileInfo.getFilePath( ) );
    mFileHolder.mVoiceTypeTxtView.setText( fileInfo.getVoiceType( ) );

    // 为每一项setTag,在onClick方法中通过v.getTag()方法获取点击了哪一项
    mFileHolder.mFileItemLayout.setTag(position);
    mFileHolder.mFileItemLayout.setOnClickListener(new OnClickListener( ) {
        @Override
        public void onClick(View v) {
            int position = ( Integer )v.getTag( );
            mSelectPosition = position;
            if( null != mOnFileListOnClickListener ){
                mOnFileListOnClickListener.onItemClick(mFileInfoList.get(position));
            }

            notifyDataSetChanged( );
        }
    });

    if(position == mSelectPosition){
        mFileHolder.mFileItemLayout.setBackgroundColor( Color.BLUE );
    }else{
        mFileHolder.mFileItemLayout.setBackgroundColor( Color.RED );
    }
}

??如果按上述这种方式编码,在程序运行后点击某一项你会发现程序会挂掉,会报下面的异常:

02-01 15:57:49.340: E/AndroidRuntime(1949): java.lang.ClassCastException: java.lang.Integer cannot be cast to com.uperone.view.FileListAdapter$FileHolder
02-01 15:57:49.340: E/AndroidRuntime(1949): at com.uperone.view.FileListAdapter.getView(FileListAdapter.java:72)

??具体原因也是因为Holder缓存导致的,因为在getView方法中,我是通过为convertView设置tag来做到Holder重用的:

@Override
public View getView(int position, View convertView, ViewGroup parent) {
    if( null == convertView ){
        convertView = mLayoutInflater.inflate( R.layout.list_file_item_layout, null);

        mFileHolder = new FileHolder();
        mFileHolder.mFileItemLayout = ( LinearLayout )convertView.findViewById(R.id.fileItemLayoutId);
        mFileHolder.mFilePathTxtView = ( TextView )convertView.findViewById(R.id.filePathTxtId);
        mFileHolder.mVoiceTypeTxtView = ( TextView )convertView.findViewById(R.id.voiceTypeTxtId);
        mFileHolder.mFileItemLayout.setClickable(true);
        convertView.setTag( mFileHolder );
    }else{
        mFileHolder = ( FileHolder )convertView.getTag( );
    }

    if( !isDataEmpty() ){
        setFileInfo( mFileInfoList.get( position ), position );
    }

    return convertView;
}

??在点击ListView中的某一项时,由于设置每一项内容的setFileInfo方法是在convertView.getTag()后调用的,所以在系统调用getView()方法更新ListView时,convertView取到的tag是mHolder.mFileItemLayout设置的tag,使用了Holder,就不能在每项中有多个视图设置tag和取tag。

??对于上述的异常,通常的解决方式是在ListView实例化的地方,通过setOnItemClickListener方法监听每一项的点击,避免tag冲突的问题。

时间: 2024-07-28 21:33:22

在Adapter中使用Holder的那些坑的相关文章

ViewPager的Adapter中视图重用

ViewPager的PagerAdapter不像ListView/GridView的BaseAdapter,它是没有内部视图重用机制的,也就是说我先inflate出来一个,然后调用destroyItem后,这个视图就被抛弃了,如果需要更多的视图,则需要再次inflate.如果ViewPager中的所有视图基本相同,就存在内存的浪费了.这里使用一个非常简单的方法实现视图的重用: List<View> mViewList = new ArrayList<View>(); public

自定义Adapter中实现startActivityForResult的分析

最近几天在做文件上传的时候,想在自定义Adapter中启动activity时也返回Intent数据,于是想到了用startActivityForResult,可是用mContext怎么也调不出这个方法,只能调用startActivity这个方法,于是在网上搜一下,可以利用一个方式可以间接的解决这个问题,果断贴代码: Intent mIntent = new Intent(mContext,clazz);((Activity) mContext).startActivityForResult(mI

【Fine原创】JMeter分布式测试中踩过的那些坑

最近因为项目需要,研究了性能测试的相关内容,并且最终选用了jmeter这一轻量级开源工具.因为一直使用jmeter的GUI模式进行脚本设计,到测试执行阶段工具本身对资源的过量消耗给性能测试带来了瓶颈,一般线程加到100左右就会出现工具本身无法支撑的问题,广泛了解解决办法后,发现分布式部署测试机仍是首选方案. 关于如何配置jmeter分布式部署测试机很多博客上已经描述得很详细了,这里就不再赘述,可以参考虫师的博客: http://www.cnblogs.com/fnng/archive/2012/

phonegap开发app中踩过的那些坑

把遇到的问题列出来,如果有解决方案的,偶也会写下来,如果大家有更好解决方法的,欢迎留言噢 phonegap 2.9无法触发deviceready事件 亲们可以看下控制台有木有报错,如果有提示cordova_plugins.json 404 (Not Found) ,就在www目录下新建个空文件,命名为cordova_plugins.json就好了,cordova初始化的时候会请求这个文件,但po主还没发现这个文件有啥用,但是没有这个文件的话,cordova初始化失败,自然不会触发devicere

python中json.dumps使用的坑以及字符编码

我们知道,python中的字符串分普通字符串和unicode字符串,一般从数据库中读取的字符串会自动被转换为unicode字符串 下面回到重点,使用json.dumps时,一般的用法为: >>> obj={"name":"测试"} >>> json.dumps(obj)'{"name": "\\u6d4b\\u8bd5"}' >>> print json.dumps(obj

关于在eclipse中配置tomcat的各种坑

先说在windows下的,java环境什么的就不再记录了,记住装java ee之前,先要装好java se这样java ee才能顺利安装. 主要是安装好tomcat之后,在eclipse中进行配置的时候,在servers窗口最下面双击”创建服务器 ”的英文标记后,根据自己的tomcat版本,一步一步next进行创建,注意中间过程中jre的版本要调整跟自己的环境变量使用的一致的那个jre.还有一点注意的,配置好之后,还要注意一下,要是还运行不了的话,双击配置好的tomcatX,在弹出的配置页面中,

Java中日期格式化YYYY-DD的坑

摘自:https://www.cnblogs.com/tonyY/p/12153335.html Java中日期格式化YYYY-DD的坑 2020-01-05 19:27  兔子托尼啊  阅读(115)  评论(0)  编辑  收藏 写这篇博文是记录下跨年的bug.去年隔壁组的小伙伴就是计算两个日期之间间隔的天数,因为跨年的原因计算有误. 当时测试组的小姐姐也没有模拟出来这种场景,导致上生产环境直接影响线上的数据. 今天逛技术论论坛正好遇到Java日期的操作bug. 1 yyyy 和 YYYY

iOS项目中Json转Model的坑

Json转Model json转model,是个开发都会遇到过.都已经9102年了,谁还不会用个第三方框架搞.拿起键盘就是干!打开podfile,把大名顶顶的YYModel写上,pod install一下.再用上ESJsonFormat,直接根据json,都能把model生成好. 特殊处理 啥?返回的字段值不是我们所需的在日常开发中,经常会遇到一些接口字段返回的值,并不是我所需要的类型的情况,这个时候,我们都会对这个字段进行处理.举个栗子: 123456 /** 错误代码 */@property

浅析android适配器adapter中的那些坑

做项目中遇到的,折磨了我将近两天,今天把经验分享出来.让大家以后少走点弯路,好了.简单来说一下什么是android的适配器,怎样定义.怎样添加适配器的重用性.怎样去减少程序的耦合性 适配器顾名思义是用来做适配的,可是他是怎样做适配的.机制是什么.作用是什么,好,相信写java的都是知道什么是面向对象编程吧,这里用面向对象来解释非常好理解.事实上适配器是把每一个对象放在其空间上然后在andorid页面显示,如今再来理解,对象事实上就是数据的抽象体吧,而页面显示通常是xml也就是视图吧,那么数据以视