有時候我們需要做一個類似下圖的列表,如下,每隔幾個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的類型值,所以列表一直報錯,這裏爲了防止其他和我一樣踩坑的人不知所云,特意記錄下來,後面的看不懂沒關係,只要注意前面紅色標記的位置就可以了