ListView 多类型Item样式--ViewType简析

ListView作为传统展示大量数据的基本控件,其回收能力是核心。考虑到数据并不一定是单一的样式,因此,viewTpe使得多样式的列表结构变得简单清晰。

效果图:

img1.png

img2.png

img3.png

代码:

代码很简单,主要就是在adapter里面重写

getViewTypeCount()

getItemViewType(int position)
这两个方法。

MultiStyleListAdapter.java:

public class MultiStyleListAdapter extends AbstractListAdapter {
        private Class[] dataClasses;
    @Override
    public View getView(int position, View view, ViewGroup parent) {
        int viewType=getItemViewType(position);
        if (viewType==0) {
            view = getStyle1View(position, view, parent);
        }else if (viewType==1) {
            view = getStyle2View(position, view, parent);
        } else if(viewType==2){
            view = getStyle3View(position, view, parent);
        }else if(viewType==3){
            view=getStyle4View(position, view, parent);
        }
 
        return view;
    }
 
    public MultiStyleListAdapter(Context context) {
        // TODO Auto-generated constructor stub
        super(context);
        dataClasses=new Class[]{Data1.class,Data2.class,Data3.class,Data4.class};
    }
 
    /**
     * 注意返回的类型:
     * 如果只有1种布局类型,那么返回的type是0;
     * 如果2种类型,必须是0,1
     * 如果3种类型,必须是0,1,2
     * 。。。。
     * 依次类推
     * @param position 根据position返回对应位置的视图类型
     * @return
     */
    @Override
    public int getItemViewType(int position) {
        // TODO Auto-generated method stub
        Object object=getItem(position);
        for(int i=0,size=dataClasses.length;i 
 
viewType分析

好了,代码贴完,可以开始分析了。

大家都知道ListViewGridView都是继承至AbsListView。它的回收能力来自于abslistview。

滚动时,超出屏幕的视图会被扔进回收站,当ListView判定出即将有item进入屏幕时,又会从回收站里面把视图拿出来重用,当然,前提是回收站里有满足要求的视图。否则会创建新实例。这部分源码不是重点,有兴趣的同学自行查看源码或相关文档。

RecycleBin,这就是这个回收站,在ListView实例创建时而被创建。其中有几个比较重要的成员变量

View[] mActiveViews  存的是处于屏幕里的view
ArrayList[] mScrapViews; 存的是所有废弃的view,它就是我们要说的重点。这个数组的每个元素都是一个view的集合,其实也就是每个类型一个集合。
RecycleBin.setViewTypeCount(int viewTypeCount)是在ListViewsetAdapter里面被调用的,这时就会根据type的数量创建对应个数的view集合。
getScrapView()里以viewType取集合,所以,前面所说的adapter里面的getviewtype的返回值必须从0开始,且不间断的自然数。

class RecycleBin {
          //根据viewtype的种类数量创建arraylist的数组
public void setViewTypeCount(int viewTypeCount) {
    if (viewTypeCount < 1) {
        throw new IllegalArgumentException("Can't have a viewTypeCount < 1");
    }
    //noinspection unchecked
    ArrayList[] scrapViews = new ArrayList[viewTypeCount];
    for (int i = 0; i < viewTypeCount; i++) {
        scrapViews[i] = new ArrayList();
    }
    mViewTypeCount = viewTypeCount;
    mCurrentScrap = scrapViews[0];
    mScrapViews = scrapViews;
}
 
//从垃圾堆里重新取得view
private View retrieveFromScrap(ArrayList scrapViews, int position) {
    final int size = scrapViews.size();
    if (size > 0) {
        // See if we still have a view for this position or ID.
        for (int i = 0; i < size; i++) {
            final View view = scrapViews.get(i);
            final AbsListView.LayoutParams params =
                    (AbsListView.LayoutParams) view.getLayoutParams();
 
            if (mAdapterHasStableIds) {
                final long id = mAdapter.getItemId(position);
                if (id == params.itemId) {
                    return scrapViews.remove(i);
                }
            } else if (params.scrappedFromPosition == position) {
                final View scrap = scrapViews.remove(i);
                clearAccessibilityFromScrap(scrap);
                return scrap;
            }
        }
        final View scrap = scrapViews.remove(size - 1);
        clearAccessibilityFromScrap(scrap);
        return scrap;
    } else {
        return null;
    }
}
 
//根据viewtype获取对应类型的view集合
View getScrapView(int position) {
    final int whichScrap = mAdapter.getItemViewType(position);
    if (whichScrap < 0) {
        return null;
    }
    if (mViewTypeCount == 1) {
        return retrieveFromScrap(mCurrentScrap, position);
    } else if (whichScrap < mScrapViews.length) {
        //以viewtype为下标取集合
        return retrieveFromScrap(mScrapViews[whichScrap], position);
    }
    return null;
}
}

AbsListView.java:


//listview里的每个视图就是在这个方法中被创建的
View obtainView(int position, boolean[] isScrap){
    ......
    //从回收站重新取出对应type的view
    final View scrapView = mRecycler.getScrapView(position);
    //再传入getview里重新绑定数据。
    final View child = mAdapter.getView(position, scrapView, this);
    ......
}

AbsListViewobtainview方法创建或者复用回收站里面的view
absListViewtrackMotionScroll()--->mRecycler.addScrapView(child, position);
trackMotionScroll()滚动时会被调用,这里面判断哪些view被废弃。

所以ListView滚动时,判定哪些view超出屏幕,超出就被放入回收站里,等又需要view的时候,listview根据viewType类型从回收站里取出来复用,并回调到adapter的getView()方法里面,从而达到多类型复用的效果。

demo源码:

https://github.com/qinzhen308...

你可能感兴趣的:(android,view,listview)