Android 重写 getViewTypeCount()数组越界异常

时间:2021-07-31 00:44:34

由于自定义listview和多种item布局的需求,我一如既往的在我的适配器中重写了

getViewTypeCount()、getItemViewType(int position)、getView()等方法
在getView()中通过对不同类型值的item进行不同的布局处理,代码片段如下:
@Override
public int getViewTypeCount() {
return 3;
}

@Override
public int getItemViewType(int position) {
return adapterDataList.get(position).getType();
}

@Override
public View getView(int position, View convertView, ViewGroup parent) {
... ...
... ...
switch (type) {
case 21://小图视频
convertView = LayoutInflater.from(context).inflate(R.layout.layout_simple_item_type1, null);
holder.titleType1 = (TextView) convertView.findViewById(R.id.tvItemType1);
holder.imageType1 = (ImageView) convertView.findViewById(R.id.imageItemType1);
holder.assist1 = (TextView) convertView.findViewById(R.id.nice1);
holder.pubTime1 = (TextView) convertView.findViewById(R.id.putTime1);

ViewGroup.LayoutParams paramsImg = holder.imageType1.getLayoutParams();
paramsImg.width = imgWidth1Of3;
paramsImg.height = (int) (imgWidth1Of3 * DisplayUtils.goldenRatio + 0.5f);
holder.imageType1.setLayoutParams(paramsImg);
ViewGroup.LayoutParams paramsText = holder.titleType1.getLayoutParams();
paramsText.width = DisplayUtils.calcMultipleWidth(getContext(), width, 3, 2, 5, 15);

holder.titleType1.setLayoutParams(paramsText);

convertView.setTag(holder);

holder.titleType1.setText(title);
setTitleColor(holder.titleType1, title, true);

imageLoader.displayImage(imgPaths[0], holder.imageType1, options);
holder.assist1.setText(count666);
holder.pubTime1.setText(pubTime);
break;
case 22://大图视频
convertView = LayoutInflater.from(context).inflate(R.layout.layout_simple_item_type2, null);
holder.titleType2 = (TextView) convertView.findViewById(R.id.tvItemType2);
holder.imageType2 = (ImageView) convertView.findViewById(R.id.imageItemType2);
holder.assist2 = (TextView) convertView.findViewById(R.id.nice2);
holder.pubTime2 = (TextView) convertView.findViewById(R.id.putTime2);
holder.viewType2 = convertView.findViewById(R.id.playButton);

TextView picsCountBg = (TextView) convertView.findViewById(R.id.picsCountBg);
TextView picsCountOrTime = (TextView) convertView.findViewById(R.id.picsCount);
if (type == 22) {
holder.viewType2.setVisibility(View.VISIBLE);
String dueTime = VideoInfoHelper.formatLongToTimeStr(dataItem.getDueTime());
picsCountBg.setText(dueTime);
picsCountOrTime.setText(dueTime);
} else {
picsCountBg.setText(dataItem.getImageNum()+"");
picsCountOrTime.setText(dataItem.getImageNum()+"");
}

int imgWidth1Of1 = DisplayUtils.calcMultipleWidth(getContext(), width, 1, 1, 5, 15);
ViewGroup.LayoutParams paramsImgType2 = holder.imageType2.getLayoutParams();
paramsImgType2.width = imgWidth1Of1;
paramsImgType2.height = (int) (imgWidth1Of1 * DisplayUtils.goldenRatio + 0.5f);
holder.imageType2.setLayoutParams(paramsImgType2);
convertView.setTag(holder);

holder.titleType2.setText(title);
setTitleColor(holder.titleType2, title, true);

imageLoader.displayImage(imgPaths[0], holder.imageType2, options);
holder.assist2.setText(count666);
holder.pubTime2.setText(pubTime);
break;
    case 23: ... ...
... ...
... ...
}
 
于是就发生了让人头疼的数组越界异常:
 java.lang.ArrayIndexOutOfBoundsException: length=3; index=12
                                                       at android.widget.AbsListView$RecycleBin.addScrapView(AbsListView.java:7113)
                                                       at android.widget.ListView.measureHeightOfChildren(ListView.java:1262)
                                                       at android.widget.ListView.onMeasure(ListView.java:1162)
                                                       at android.view.View.measure(View.java:15481)
                                                       at android.view.ViewGroup.measureChildWithMargins(ViewGroup.java:5107)
                                                       at android.widget.FrameLayout.onMeasure(FrameLayout.java:310)
                                                       at android.view.View.measure(View.java:15481)
                                                       at android.view.ViewGroup.measureChildWithMargins(ViewGroup.java:5107)
                                                       at android.widget.LinearLayout.measureChildBeforeLayout(LinearLayout.java:1396)
                                                       at android.widget.LinearLayout.measureVertical(LinearLayout.java:681)
... ...
... ...
 
一开始急赶代码给人看效果没来得及仔细查看异常发生在具体哪个引用类中(当然,是没有出现自己项目下的类调用错误行提示,只有API中的)于是就上网搜了一下异常
(机智的加上Android关键字在前再搜索~ 纯Java的数组越界就不在这里讨论了~) 由于异常信息及其相似~害的自己走了不少弯路, 不过也了解了另外一个问题就是textview单行显示不加singleLine属性的BUG ,弯路就不继续描述了,那个问题可见https://code.google.com/p/android/issues/detail?id=33868
后来下班经过debug调试后,问题解决了,但是感觉这个设计有些蠢~   如果没看懂我吐槽了半天要讲什么,没关系,结论来了:
 
getViewTypeCount()  顾名思义就是我们一共需要多少种item的布局 
getItemViewType() 的返回值(int)是区分不同类型的数字  那么问题来了,我现在需要3种布局,于是前者返回3  后者当然是利用postion返回3种不同的类型值 
问题就出在这3种不同的类型值的限制上,目的是区分,正常的逻辑是,只要这3个值不同即可,比如  1、2、3、   100、200、300、
但是越界异常就出现在这里 因为 getItemViewType()返回的值规定必须小于getViewTypeCount()  也就是说如果有3种,那类型必须是0、1、2、 那个找了半天找不到的越界的数组就是类型值和类型数"绑定"的数组  ,将类型返回值写成0、1、2 不报错了, 我再但是~ 这个类型值是我从WEB后台获取数据对象后get到的,如果后台代码不能改或者不方便改的情况下怎么办? 于是我尝试了下将类型数量返回值改成了24 (最大的类型值是23,规定要小于count) 乍一看~ 有歧义,会以为有24种类型呢,但是运行并不会有任何问题~ 问题也可以解决
最后总觉得这样不好,虽然运行时并没有问题,但代码很不友好,于是我再稍加改进,干脆将后台的类型值和getItemViewType()的返回值做一个数据字典的对应转换:
@Overridepublic int getItemViewType(int position) {    int type = adapterDataList.get(position).getType();    if (type == 21) {        return 0;    } else if (type == 22) {        return 1;    } else if (type == 23) {        return 2;    }    return 0;//default}

这样同时避免不产生歧义也不需要重构WEB后台代码的情况下解决了问题。