ListView 多類型Item樣式--ViewType簡析

ListView做爲傳統展現大量數據的基本控件,其回收能力是核心。考慮到數據並不必定是單一的樣式,所以,viewTpe使得多樣式的列表結構變得簡單清晰。java

效果圖:

img1.png

img2.png

img3.png

代碼:

代碼很簡單,主要就是在adapter裏面重寫git

getViewTypeCount()github

getItemViewType(int position)
這兩個方法。數組

MultiStyleListAdapter.java:app

public class MultiStyleListAdapter extends AbstractListAdapter<Object> {
        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<size;i++){
            if(object.getClass()== dataClasses[i]){
                return i;
            }
        }
        return 0;
    }
 
    /**
     * @return 返回值是,佈局種類總數
     */
    @Override
    public int getViewTypeCount() {
        // TODO Auto-generated method stub
        return dataClasses.length;
    }
    
 
    private View getStyle1View(final int position, View convertView,
            ViewGroup parent) {
        final Styly1ViewHolder holder;
        if (convertView == null) {
            convertView = mInflater.inflate(R.layout.item_style_1, null);
            holder = new Styly1ViewHolder(convertView);
            convertView.setTag(holder);
        } else {
            holder = (Styly1ViewHolder) convertView.getTag();
        }
        final Data1 item=(Data1)mList.get(position);
        holder.tvIndex.setText("index="+position);
        holder.content.setText(item.content);
        return convertView;
    }
 
    private View getStyle2View(final int position, View convertView,
                               ViewGroup parent) {
        final Styly2ViewHolder holder;
        if (convertView == null) {
            convertView = mInflater.inflate(R.layout.item_style_2, null);
            holder = new Styly2ViewHolder(convertView);
            convertView.setTag(holder);
        } else {
            holder = (Styly2ViewHolder) convertView.getTag();
        }
        final Data2 item=(Data2)mList.get(position);
        holder.tvIndex.setText("index="+position);
        holder.pic.setImageResource(item.img);
        return convertView;
    }
 
    private View getStyle3View(final int position, View convertView,
                               ViewGroup parent) {
        final Styly3ViewHolder holder;
        if (convertView == null) {
            convertView = mInflater.inflate(R.layout.item_style_3, null);
            holder = new Styly3ViewHolder(convertView);
            convertView.setTag(holder);
        } else {
            holder = (Styly3ViewHolder) convertView.getTag();
        }
        final Data3 item=(Data3)mList.get(position);
        holder.tvIndex.setText("index="+position);
        holder.pic1.setImageResource(item.img1);
        holder.pic2.setImageResource(item.img2);
        holder.pic3.setImageResource(item.img3);
        return convertView;
    }
 
    private View getStyle4View(final int position, View convertView,
                               ViewGroup parent) {
        final Styly4ViewHolder holder;
        if (convertView == null) {
            convertView = mInflater.inflate(R.layout.item_style_4, null);
            holder = new Styly4ViewHolder(convertView);
            convertView.setTag(holder);
        } else {
            holder = (Styly4ViewHolder) convertView.getTag();
        }
        final Data4 item=(Data4)mList.get(position);
        holder.tvIndex.setText("index="+position);
        holder.checkBox.setText(item.content);
        holder.checkBox.setChecked(item.isChecked);
        return convertView;
    }
 
 
    class Styly1ViewHolder {
        TextView tvIndex;
        TextView content;
        
        public Styly1ViewHolder(View root){
            content=(TextView)root.findViewById(R.id.content);
            tvIndex=(TextView)root.findViewById(R.id.index);
        }
 
    }
 
    class Styly2ViewHolder {
        TextView tvIndex;
        ImageView pic;
 
        public Styly2ViewHolder(View root){
            pic=(ImageView)root.findViewById(R.id.content);
            tvIndex=(TextView)root.findViewById(R.id.index);
        }
 
    }
 
    class Styly3ViewHolder {
        TextView tvIndex;
        ImageView pic1;
        ImageView pic2;
        ImageView pic3;
 
        public Styly3ViewHolder(View root){
            pic1=(ImageView)root.findViewById(R.id.img1);
            pic2=(ImageView)root.findViewById(R.id.img2);
            pic3=(ImageView)root.findViewById(R.id.img3);
            tvIndex=(TextView)root.findViewById(R.id.index);
        }
 
    }
 
    class Styly4ViewHolder {
        TextView tvIndex;
        CheckBox checkBox;
 
        public Styly4ViewHolder(View root){
            checkBox=(CheckBox)root.findViewById(R.id.checkbox);
            tvIndex=(TextView)root.findViewById(R.id.index);
        }
 
    }
 
 
}

demo用了4個樣式,分別對應4個viewholder,定義4個數據模型,data1,data2,data3,data4,根據viewtype來實例化或複用對應view,並綁定對應數據。ide

注意getViewType返回值,必須從0開始,依次增大。不然要拋ArrayIndexOutOfBoundsException異常,緣由文章後面分析。佈局

AbstractListAdapter.java 繼承至BaseAdapter,只是對它進行部分重寫和封裝,以及使用泛型後的一個基類。this

public abstract class AbstractListAdapter <T> extends BaseAdapter {
 
    protected List<T> mList;
    protected Context mContext;
    private AdapterView mListView;
 
    protected LayoutInflater mInflater;
 
    public AbstractListAdapter(Activity context) {
        this.mContext = context;
        mInflater = LayoutInflater.from(context);
    }
 
    public AbstractListAdapter(Context context) {
        mContext=context;
        mInflater = LayoutInflater.from(context);
    }
 
    @Override
    public Object getItem(int i) {
        return mList == null ? null : mList.get(i);
    }
 
    @Override
    public int getCount() {
        return mList == null ? 0 : mList.size();
    }
 
    @Override
    public long getItemId(int i) {
        return i;
    }
 
    @Override
    public abstract View getView(int i, View view, ViewGroup viewGroup);
 
    public void setList(List<T> list) {
        this.mList = list;
    }
 
    public List<T> getList() {
        return this.mList;
    }
 
    public AdapterView getListView(){
        return mListView;
    }
 
    public void setListView(AdapterView listView){
        mListView = listView;
    }
 
    public Context getContext(){
        return mContext;
    }
 
    public void setList(T[] list){
        if (list == null) return;
        ArrayList<T> arrayList = new ArrayList<T>(list.length);
        for (T t : list) {
            arrayList.add(t);
        }
        setList(arrayList);
    }
 
    public void clear() {
        if (mList != null && mList.size() > 0){
            mList.clear();
        }
    }
 
}

而後添加示例數據:spa

MainActivity.javacode

public class MainActivity extends AppCompatActivity {  
    private ListView mListview;  
    private MultiStyleListAdapter mAdapter;  
  
    @Override  
    protected void onCreate(Bundle savedInstanceState) { 
        super.onCreate(savedInstanceState);  
        setContentView(R.layout.activity_main);  
        mListview=(ListView)findViewById(R.id.listview);            
        mAdapter=new MultiStyleListAdapter(this);  
        mListview.setAdapter(mAdapter);  
        createData();  
    }  
  
    private void createData(){  
        List<Object> dataAll=new ArrayList<>();  
        dataAll.add(new Data1("小白兔和大灰狼的故事"));  
        dataAll.add(new Data1("話說小白兔遇到大灰狼"));  
        dataAll.add(new Data2(R.drawable.img1));  
        dataAll.add(new Data2(R.drawable.img2));  
        dataAll.add(new Data1("小白兔說大灰狼大灰狼 你快問我是否是小白兔"));  
        dataAll.add(new Data3(R.drawable.img3,R.drawable.img4,R.drawable.img5));  
        dataAll.add(new Data4("你快問啊快問啊!!!!",true));  
        dataAll.add(new Data1("大灰狼說 你是否是小白兔啊?"));  
        dataAll.add(new Data2(R.drawable.img6));  
        dataAll.add(new Data2(R.drawable.img7));  
        dataAll.add(new Data1("小白兔很高興 是的是的我是的!!!"));  
        dataAll.add(new Data1("而後"));  
        dataAll.add(new Data1("小白兔又說 大灰狼大灰狼 你快問我是否是長頸鹿"));  
        dataAll.add(new Data2(R.drawable.img8));  
        dataAll.add(new Data1("你快問啊快問啊!!!!"));  
        dataAll.add(new Data1("大灰狼很無奈 好吧。。。那。。。你是否是長頸鹿啊"));  
        dataAll.add(new Data4("小白兔朝他後腦勺一巴掌 你個笨蛋!我都說了我是小白兔了!!!。。。。。",true));  
        dataAll.add(new Data4("撒角度看",true));  
        dataAll.add(new Data3(R.drawable.img9,R.drawable.img10,R.drawable.img11));  
        dataAll.add(new Data1("。。。。。。。。。。。。")); 
        mAdapter.setList(dataAll);  
        mAdapter.notifyDataSetChanged();  
    }  
  
  
}
viewType分析

好了,代碼貼完,能夠開始分析了。

你們都知道ListViewGridView都是繼承至AbsListView。它的回收能力來自於abslistview。

滾動時,超出屏幕的視圖會被扔進回收站,當ListView斷定出即將有item進入屏幕時,又會從回收站裏面把視圖拿出來重用,固然,前提是回收站裏有知足要求的視圖。不然會建立新實例。這部分源碼不是重點,有興趣的同窗自行查看源碼或相關文檔。

RecycleBin,這就是這個回收站,在ListView實例建立時而被建立。其中有幾個比較重要的成員變量

View[] mActiveViews  存的是處於屏幕裏的view
ArrayList<View>[] 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<View>[] scrapViews = new ArrayList[viewTypeCount];
    for (int i = 0; i < viewTypeCount; i++) {
        scrapViews[i] = new ArrayList<View>();
    }
    mViewTypeCount = viewTypeCount;
    mCurrentScrap = scrapViews[0];
    mScrapViews = scrapViews;
}
 
//從垃圾堆裏從新取得view
private View retrieveFromScrap(ArrayList<View> 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...

相關文章
相關標籤/搜索