关于ListView的getItemViewType()这个方法的踩坑叙述

来源:互联网 发布:软件企业证书有效期 编辑:程序博客网 时间:2024/05/01 21:34

有时候我们需要做一个类似下图的列表,如下,每隔几个item就需要一个标签来区分不同的数据类型,这个时候就需要用到getItemViewType()来做区分了

BaseAdapter中有2个方法:

1.getItemViewType(int position);//得到当前item的类型

2.getViewTypeCount()//得到不同的item的总数,下面图上的类型是2种

//下面贴一段代码(因为完整的项目需要关系很多代码,所以只贴Adapter的代码)

package com.yy.ent.mobile.ui.live.livelist;import android.content.Context;import android.content.Intent;import android.view.View;import android.view.ViewGroup;import android.widget.LinearLayout;import android.widget.TextView;import com.yy.ent.cherry.Cherry;import com.yy.ent.cherry.ext.image.CircleImageView;import com.yy.ent.cherry.ext.image.ImageConfig;import com.yy.ent.cherry.ext.image.ImageManager;import com.yy.ent.cherry.ext.image.RecycleImageView;import com.yy.ent.mobile.entity.livelist.Lives;import com.yy.ent.mobile.ui.base.XBaseAdapter;import com.yy.ent.mobile.ui.live.widget.RayRelative;import com.yy.ent.mobile.ui.personal.OthersActivity;import com.yy.ent.mobile.ui.util.NavigationUtils;import com.yy.ent.mobile.ui.util.StringUtils;import com.yy.ent.mobile.ui.util.ViewHelper;import com.yy.ent.show.ui.R; public class ShowLiveAdapter extends XBaseAdapter<Lives> {    public static final String MOBILE_LIVE_ITEM_CLICK = "mobile_live_item_click";    private static final int VIEW_COUNT = 2;    public static final int LIVE_HEADER = 0;    public static final int LIVE_CONTENT = 1;    public static final int HEADER_TAG = 110;    private String TAG = ShowLiveAdapter.class.getSimpleName();    public ShowLiveAdapter(Context context, int resource) {        super(context, resource);    }    @Override    public int getItemViewType(int position) {        if (list.get(position).status == HEADER_TAG) {            return LIVE_HEADER;        } else {            return LIVE_CONTENT;        }    }    @Override    public int getViewTypeCount() {        return VIEW_COUNT;    }    @Override    public View getYView(int i, View itemView, ViewGroup viewGroup) {        ViewHolder holder = null;        int type = getItemViewType(i);        if (itemView == null) {            switch (type) {                case LIVE_HEADER:                    TextView hot = new TextView(context);                    hot.setText("热门直播:");                    hot.setTextColor(context.getResources().getColor(R.color.link_color));//                    if (list.size() <= 1) {//                        hot.setVisibility(View.GONE);//                    }else {//                        hot.setVisibility(View.VISIBLE);//                    }                    itemView = hot;                    break;                case LIVE_CONTENT:                    itemView = listContainer.inflate(itemViewResource, null);                    holder = new ViewHolder();                    holder.anchorNick = (TextView) itemView.findViewById(R.id.live_author);                    holder.liveTitle = (TextView) itemView.findViewById(R.id.live_title);                    holder.liveAddress = (TextView) itemView.findViewById(R.id.live_address);                    holder.head = (CircleImageView) itemView.findViewById(R.id.live_head);                    holder.liveGuset = (TextView) itemView.findViewById(R.id.live_guest_num);                    holder.jobTag = (LinearLayout) itemView.findViewById(R.id.live_job_tag);                    holder.liveCover = (RecycleImageView) itemView.findViewById(R.id.live_work_cover);                    holder.cutTime = (TextView) itemView.findViewById(R.id.live_cut_time);                    holder.liveProgress = (RayRelative) itemView.findViewById(R.id.live_time_progress);                    itemView.setTag(holder);                    break;            }        } else {            if (type == LIVE_CONTENT) {                holder = (ViewHolder) itemView.getTag();            }        }        if (type == LIVE_CONTENT) {            Lives lives = list.get(i);            holder.cutTime.setText(StringUtils.formatTime(lives.countdown));            holder.liveGuset.setText(lives.online + "人在观看");            holder.anchorNick.setText(lives.anchorNick);            holder.liveTitle.setText(lives.title);            holder.liveAddress.setText(lives.location);            ImageManager.instance().loadImage(lives.anchorAvatar, holder.head, ImageConfig.defaultImageConfig(), R.drawable.video_default_cover);            ImageManager.instance().loadImage(lives.coverUri, holder.liveCover, getWidth(), 300, R.drawable.video_default_cover);            if (holder.jobTag.getChildCount() > 0) {                holder.jobTag.removeAllViews();            }            for (int j = 0; j < lives.liveTag.length; j++) {                TextView tv = (TextView) listContainer.inflate(R.layout.layout_label,                        holder.jobTag, false);                tv.setText(lives.liveTag[j]);                holder.jobTag.addView(tv);            }            holder.liveProgress.setProgress(getProgress(lives.liveDuration, lives.countdown));            ItemOnClick onClick = new ItemOnClick(lives);            holder.liveCover.setOnClickListener(onClick);            holder.head.setOnClickListener(onClick);        }        return itemView;    }    private int getProgress(long liveDuration, long countdown) {        float rate = (float)countdown/ liveDuration ;        int width = getWidth();        int progress = (int) (width * rate);        return progress;    }    public int getWidth() {        return ViewHelper.getDisplayMetrics(context).widthPixels;    }    class ItemOnClick implements View.OnClickListener {        private Lives item;        ItemOnClick(Lives item) {            this.item = item;        }        @Override        public void onClick(View view) {            if (view.getId() == R.id.live_work_cover) {                if (StringUtils.canClick()) {                    if (item.status == 0) {                        Cherry.notityUI(MOBILE_LIVE_ITEM_CLICK, item);                    } else {                        NavigationUtils.startToLiveReviewActivity(context, item                        );                    }                }            }            if (view.getId() == R.id.live_head) {                Intent intent = new Intent(context, OthersActivity.class);                intent.putExtra("personal_activity_arg_uid", item.anchorId + "");                context.startActivity(intent);            }        }    }    class ViewHolder {        public RayRelative liveProgress;        public TextView liveTitle;        public TextView liveAddress;        public CircleImageView head;        public RecycleImageView liveCover;        public LinearLayout jobTag;        public TextView liveGuset;        public TextView cutTime;        public TextView anchorNick;    }}

如上,有2个常量参数LIVE_HEADER和LIVE_CONTENT,它们分别代表了2种item的类型,再看看它们的值分别是0和1,注意:这里的值必须是0和1,不能大于1,也不能为其他的数字,如2,3,0x1等等,否则你在getItemViewType()的时候就会报java.lang.ArrayIndexOutOfBoundsException这个异常,因为你的getViewTypeCount返回的数量是2,所以只能有2种类型,也就是0或者1,如果你的类型设置为2或者3,就会超出了类型数量, 至于具体原因就要看看android系统的源码了.


如果出现异常,这个时候报的异常是这样的(只贴了主要的异常部分):

java.lang.ArrayIndexOutOfBoundsException: length=2; index=10            at android.widget.AbsListView$RecycleBin.addScrapView(AbsListView.java:6792)            at android.widget.ListView.measureHeightOfChildren(ListView.java:1275)            at android.widget.ListView.onMeasure(ListView.java:1175)            at com.yy.ent.mobile.ui.widget.FixListView.onMeasure(FixListView.java:35)
然后依次定位最下面的35行,到super.onMeasure(widthMeasureSpec, expandSpec);这行代码,也就是测量item的大小时异常

然后是ListView内部的onMeasure()方法:

 @Override    protected void onMeasure(int widthMeasureSpec, int heightMeasureSpec) {        // Sets up mListPadding        super.onMeasure(widthMeasureSpec, heightMeasureSpec);        int widthMode = MeasureSpec.getMode(widthMeasureSpec);        int heightMode = MeasureSpec.getMode(heightMeasureSpec);        int widthSize = MeasureSpec.getSize(widthMeasureSpec);        int heightSize = MeasureSpec.getSize(heightMeasureSpec);        int childWidth = 0;        int childHeight = 0;        int childState = 0;        mItemCount = mAdapter == null ? 0 : mAdapter.getCount();        if (mItemCount > 0 && (widthMode == MeasureSpec.UNSPECIFIED ||                heightMode == MeasureSpec.UNSPECIFIED)) {            final View child = obtainView(0, mIsScrap);            measureScrapChild(child, 0, widthMeasureSpec);            childWidth = child.getMeasuredWidth();            childHeight = child.getMeasuredHeight();            childState = combineMeasuredStates(childState, child.getMeasuredState());            if (recycleOnMeasure() && mRecycler.shouldRecycleViewType(                    ((LayoutParams) child.getLayoutParams()).viewType)) {                mRecycler.addScrapView(child, 0);            }        }        if (widthMode == MeasureSpec.UNSPECIFIED) {            widthSize = mListPadding.left + mListPadding.right + childWidth +                    getVerticalScrollbarWidth();        } else {            widthSize |= (childState&MEASURED_STATE_MASK);        }        if (heightMode == MeasureSpec.UNSPECIFIED) {            heightSize = mListPadding.top + mListPadding.bottom + childHeight +                    getVerticalFadingEdgeLength() * 2;        }        if (heightMode == MeasureSpec.AT_MOST) {            // TODO: after first layout we should maybe start at the first visible position, not 0            heightSize = measureHeightOfChildren(widthMeasureSpec, 0, NO_POSITION, heightSize, -1);        }        setMeasuredDimension(widthSize , heightSize);        mWidthMeasureSpec = widthMeasureSpec;            }
这里计算listview的没一个item的高度,宽度,请看最下面的倒数第3行代码:measureHeightOfChildren(widthMeasureSpec, 0, NO_POSITION, heightSize, -1);//测量子控件的宽高,然后经过一系列的方法及接口调用(这里楼主偷懒,代码太多啦)就会走到AbsListView的getScrapView(int position) 这个方法,这里就是最终得到的item的view类型.

到这个方法里面去

 /**         * @return A view from the ScrapViews collection. These are unordered.         */        View getScrapView(int position) {            if (mViewTypeCount == 1) {                return retrieveFromScrap(mCurrentScrap, position);            } else {                final int whichScrap = mAdapter.getItemViewType(position);                if (whichScrap >= 0 && whichScrap < mScrapViews.length) {                    return retrieveFromScrap(mScrapViews[whichScrap], position);                }            }            return null;        }

看到mAdapter.getItemViewType(position)这行代码了吗,其中的position就是你传入的item的下标,返回值就是你之前定义的类型0,1,再看看下面的if判断是不是就一目了然了,其实mScrapViews.length=getViewTypeCount(),所以你这里如果传入是3,4的话,if条件就不会成立了,view也就无法进行绘制,mScrapViews是一个ArrayList,取到的值大于getViewTypeCount(),那么就会造成数组越界了.

        因为凭自己的感觉去定义了ItemViewType的类型值,所以列表一直报错,这里为了防止其他和我一样踩坑的人不知所云,特意记录下来,后面的看不懂没关系,只要注意前面红色标记的位置就可以了



0 0
原创粉丝点击