Android 中listView的多个Item复用问题

最近接触到了listView中多个Item复用的问题。listView使我们Android开发中非常常见的一个控件,可以说任何一款应用都会使用到他,所有呢,这个控件也被别人研究的比较多。常用的一般复用方法,大家应该都清楚。convertView的复用,使用ViewHolder,使用LruCache,异步加载等等。这里就是记录我开发当中遇到的listView的多个item复用的方法和一些处理的坑。。


首先呢,假定我们的listView中总共有2种类型的Item,这2中类型完全不同,我们设定View1,View2。假定我们定义2个ViewHolder。一个TextHolder,一个AudioHolder。

class TextViewHolder {
        public CircleImageView circle_img; 
        public TextView name; 
        public TextView title; 
        public TextView praiseCount; 
        public TextView praiseCount_praised;  
        public TextView tv_time; 
        public TextView tv_play;
        
        public CircleImageView text_hidden_avatar; 
        public TextView answer_detail_text; 
        public LinearLayout text_layout;
        public View zd_audio_layout;
        public View v_miao;
        public ProgressBar pb;
    }

    class AudioViewHolder {
        public CircleImageView circle_img; 
        public TextView tv_time; 
        public TextView name; 
        public TextView title; 
        public TextView audio_prise_num_text; 
        public TextView audio_prise_num_text_prised;  

        public TextView tv_play;
        public RelativeLayout audio_layout;
        public View zd_audio_layout;
        public View v_miao;
        public ProgressBar pb;
    }
我们在getView的方法中复用convertView的代码
 if (convertView == null) {
            switch (type) {
                case AUDIO_TYPE:
                    audioHolder = new AudioViewHolder();
                    convertView = inflater.inflate(
                            R.layout.audio_answer_detail_listview_item, null);
                    audioHolder.circle_img = (CircleImageView) convertView.findViewById(R.id.iv_avatar);
                    audioHolder.name = (TextView) convertView.findViewById(R.id.audio_name);
                    audioHolder.tv_time = (TextView) convertView.findViewById(R.id.tv_time);
                    audioHolder.audio_prise_num_text_prised = (TextView) convertView.findViewById(R.id.audio_prise_num_text_prised);
                    audioHolder.audio_prise_num_text = (TextView) convertView.findViewById(R.id.audio_prise_num_text);
                    audioHolder.title = (TextView) convertView.findViewById(R.id.audio_title);
                    audioHolder.tv_play = (TextView) convertView.findViewById(R.id.tv_play);
                    audioHolder.audio_layout = (RelativeLayout) convertView.findViewById(R.id.audio_layout);
                    
                    audioHolder.zd_audio_layout = convertView.findViewById(R.id.zd_audio_layout);
                    audioHolder.v_miao = convertView.findViewById(R.id.v_miao);
                    audioHolder.pb = (ProgressBar) convertView.findViewById(R.id.pb);
                    convertView.setTag(audioHolder);
                    break;
                case TEXT_TYPE:
                    textHolder = new TextViewHolder();
                    convertView = inflater.inflate(
                            R.layout.text_answer_detail_listview_item, null);
                    textHolder.circle_img = (CircleImageView) convertView.findViewById(R.id.iv_avatar);
                    textHolder.name = (TextView) convertView.findViewById(R.id.text_answer_name);
                    textHolder.tv_time = (TextView) convertView.findViewById(R.id.tv_time);
                    textHolder.praiseCount = (TextView) convertView.findViewById(R.id.praiseCount);
                    textHolder.praiseCount_praised = (TextView) convertView.findViewById(R.id.praiseCount_praised);
                    textHolder.title = (TextView) convertView.findViewById(R.id.text_answer_title);
                    textHolder.tv_play = (TextView) convertView.findViewById(R.id.tv_play);
                    
                    textHolder.text_hidden_avatar = (CircleImageView) convertView.findViewById(R.id.text_hidden_avatar);
                    textHolder.answer_detail_text = (TextView) convertView.findViewById(R.id.answer_detail_text);
                    
                    textHolder.text_layout = (LinearLayout)convertView.findViewById(R.id.text_layout);
                    textHolder.zd_audio_layout = convertView.findViewById(R.id.zd_audio_layout);
                    textHolder.v_miao = convertView.findViewById(R.id.v_miao);
                    textHolder.pb = (ProgressBar) convertView.findViewById(R.id.pb);
                    convertView.setTag(textHolder);
                    break;
            }
        } else {
            switch (type) {
                case AUDIO_TYPE:
                    audioHolder = (AudioViewHolder) convertView.getTag();
                    break;
                case TEXT_TYPE:
                    textHolder = (TextViewHolder) convertView.getTag();
                    break;
            }
        }
这是否很符合逻辑,也很简单是吧,但是运行的时候当你listView中2种View都存在的时候,滑动listview程序就会崩溃,提示啥AudioHolder不能强转为TextHolder,或者TextHolder不能强转为AudioHolder。这明显是复用的问题了。当listview中只有TextHolder或者只有AudioHolder的时候程序当然不会崩溃的。要解决这样的问题有2种方法。但是还是推崇第一种方法,因为这种方法比较通用,简单。


方法一:复写Adapter中的getItemViewType(int position) 和getViewTypeCount()方法

 @Override
    public int getItemViewType(int position) {
        AnswerDetailResponseBean.Answer answer = mData.get(position).answer;
        if (answer.answerType == 0) { // 语音
            return AUDIO_TYPE;
        } else if (answer.answerType == 1) { // 文本
            return TEXT_TYPE;
        }
        return -1;
    }
    @Override
    public int getViewTypeCount() {
        return 2;   //有几种类型的type就返回几种类型 同时上面的getItemViewType也相应的改变
    }
只需要复写这两个方法就ok了。而且是google通用的。我目前还没有弄清楚为什么复写getViewTypeCount方法就能让系统找到我们需要的ViewHolder类型,目前还在研究源码当中,弄清楚再来补充。


方法二:先看代码 后面再解释。

if (convertView == null) {
            switch (type) {
                case AUDIO_TYPE:
                    audioHolder = new AudioViewHolder();
                    convertView = inflater.inflate(
                            R.layout.audio_answer_detail_listview_item, null);
                    audioHolder.circle_img = (CircleImageView) convertView.findViewById(R.id.iv_avatar);
                    audioHolder.name = (TextView) convertView.findViewById(R.id.audio_name);
                    audioHolder.tv_time = (TextView) convertView.findViewById(R.id.tv_time);
                    audioHolder.audio_prise_num_text_prised = (TextView) convertView.findViewById(R.id.audio_prise_num_text_prised);
                    audioHolder.audio_prise_num_text = (TextView) convertView.findViewById(R.id.audio_prise_num_text);
                    audioHolder.title = (TextView) convertView.findViewById(R.id.audio_title);
                    audioHolder.tv_play = (TextView) convertView.findViewById(R.id.tv_play);
                    audioHolder.audio_layout = (RelativeLayout) convertView.findViewById(R.id.audio_layout);
                    
                    audioHolder.zd_audio_layout = convertView.findViewById(R.id.zd_audio_layout);
                    audioHolder.v_miao = convertView.findViewById(R.id.v_miao);
                    audioHolder.pb = (ProgressBar) convertView.findViewById(R.id.pb);
                    convertView.setTag(audioHolder);
                    break;
                case TEXT_TYPE:
                    textHolder = new TextViewHolder();
                    convertView = inflater.inflate(
                            R.layout.text_answer_detail_listview_item, null);
                    textHolder.circle_img = (CircleImageView) convertView.findViewById(R.id.iv_avatar);
                    textHolder.name = (TextView) convertView.findViewById(R.id.text_answer_name);
                    textHolder.tv_time = (TextView) convertView.findViewById(R.id.tv_time);
                    textHolder.praiseCount = (TextView) convertView.findViewById(R.id.praiseCount);
                    textHolder.praiseCount_praised = (TextView) convertView.findViewById(R.id.praiseCount_praised);
                    textHolder.title = (TextView) convertView.findViewById(R.id.text_answer_title);
                    textHolder.tv_play = (TextView) convertView.findViewById(R.id.tv_play);
                    
                    textHolder.text_hidden_avatar = (CircleImageView) convertView.findViewById(R.id.text_hidden_avatar);
                    textHolder.answer_detail_text = (TextView) convertView.findViewById(R.id.answer_detail_text);
                    
                    textHolder.text_layout = (LinearLayout)convertView.findViewById(R.id.text_layout);
                    textHolder.zd_audio_layout = convertView.findViewById(R.id.zd_audio_layout);
                    textHolder.v_miao = convertView.findViewById(R.id.v_miao);
                    textHolder.pb = (ProgressBar) convertView.findViewById(R.id.pb);
                    convertView.setTag(textHolder);
                    break;
            }
        } else {
            switch (type) {
                case AUDIO_TYPE:
                	if(convertView != null && convertView instanceof LinearLayout){
                		audioHolder = (AudioViewHolder) convertView.getTag();
                	}else{
                		if(audioHolder == null)
                			audioHolder = new AudioViewHolder();
                        convertView = inflater.inflate(
                                R.layout.audio_answer_detail_listview_item, null);
                        audioHolder.circle_img = (CircleImageView) convertView.findViewById(R.id.iv_avatar);
                        audioHolder.name = (TextView) convertView.findViewById(R.id.audio_name);
                        audioHolder.tv_time = (TextView) convertView.findViewById(R.id.tv_time);
                        audioHolder.audio_prise_num_text_prised = (TextView) convertView.findViewById(R.id.audio_prise_num_text_prised);
                        audioHolder.audio_prise_num_text = (TextView) convertView.findViewById(R.id.audio_prise_num_text);
                        audioHolder.title = (TextView) convertView.findViewById(R.id.audio_title);
                        audioHolder.tv_play = (TextView) convertView.findViewById(R.id.tv_play);
                        audioHolder.audio_layout = (RelativeLayout) convertView.findViewById(R.id.audio_layout);
                        
                        audioHolder.zd_audio_layout = convertView.findViewById(R.id.zd_audio_layout);
                        audioHolder.v_miao = convertView.findViewById(R.id.v_miao);
                        audioHolder.pb = (ProgressBar) convertView.findViewById(R.id.pb);
                        convertView.setTag(audioHolder);
                	}
                    
                    break;
                case TEXT_TYPE:
                	if(convertView != null && convertView instanceof FrameLayout){
                		textHolder = (TextViewHolder) convertView.getTag();
                	}else{
                		if(textHolder == null)
                			textHolder = new TextViewHolder();
                        convertView = inflater.inflate(
                                R.layout.text_answer_detail_listview_item, null);
                        textHolder.circle_img = (CircleImageView) convertView.findViewById(R.id.iv_avatar);
                        textHolder.name = (TextView) convertView.findViewById(R.id.text_answer_name);
                        textHolder.tv_time = (TextView) convertView.findViewById(R.id.tv_time);
                        textHolder.praiseCount = (TextView) convertView.findViewById(R.id.praiseCount);
                        textHolder.praiseCount_praised = (TextView) convertView.findViewById(R.id.praiseCount_praised);
                        textHolder.title = (TextView) convertView.findViewById(R.id.text_answer_title);
                        textHolder.tv_play = (TextView) convertView.findViewById(R.id.tv_play);
                        
                        textHolder.text_hidden_avatar = (CircleImageView) convertView.findViewById(R.id.text_hidden_avatar);
                        textHolder.answer_detail_text = (TextView) convertView.findViewById(R.id.answer_detail_text);
                        
                        textHolder.text_layout = (LinearLayout)convertView.findViewById(R.id.text_layout);
                        textHolder.zd_audio_layout = convertView.findViewById(R.id.zd_audio_layout);
                        textHolder.v_miao = convertView.findViewById(R.id.v_miao);
                        textHolder.pb = (ProgressBar) convertView.findViewById(R.id.pb);
                        convertView.setTag(textHolder);
                	}
                    break;
            }
        }
就是在else的逻辑里面加了一层判断。代码的46 至48行首先判断了convertView不为null  并且convertView instanceof LinearLayout  ,下面是convertView instanceof FrameLayout  ,为什么这样写呢?假设我们AudioHolder的跟布局是LinearLayout,TextHolder的跟布局是FrameLayout,当我们的listVIew中只有TextHolder或者AudioHolder的时候就会走我们if后面的getTag()的逻辑。但是假设我们一个手机屏幕智能容纳4条Item的数据,而这4条全部是TextHolder的时候,当我们向上滑动的时候,第5条,第6条数据,全部是AudioHolder的数据,这时候想复用,convertView不为null,如果我们不加instanceof的逻辑判断,就会将我们的TextHolder转化为AudioHolder,而这样的强制转换时不允许的,也可以说是将LinearLayout强制转换成FrameLayout这样肯定会出错的,所有加了一层instanceof的判断。这样也可以作为一种方法。


但是方法二,可以在2种,3中item的时候使用,但是当Item种类很多的时候,这种方法也很难继续使用了,因为我们的布局也就那几种(我们可以自定义布局,但是我没有尝试过,你可以去试试),这种方法的适用性不好。所以还是推荐使用第一种方法。

你可能感兴趣的:(Android 中listView的多个Item复用问题)