ListView做爲傳統展現大量數據的基本控件,其回收能力是核心。考慮到數據並不必定是單一的樣式,所以,viewTpe使得多樣式的列表結構變得簡單清晰。java
代碼很簡單,主要就是在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(); } }
好了,代碼貼完,能夠開始分析了。
你們都知道ListView,GridView都是繼承至AbsListView。它的回收能力來自於abslistview。
滾動時,超出屏幕的視圖會被扔進回收站,當ListView斷定出即將有item進入屏幕時,又會從回收站裏面把視圖拿出來重用,固然,前提是回收站裏有知足要求的視圖。不然會建立新實例。這部分源碼不是重點,有興趣的同窗自行查看源碼或相關文檔。
RecycleBin,這就是這個回收站,在ListView實例建立時而被建立。其中有幾個比較重要的成員變量
View[] mActiveViews 存的是處於屏幕裏的view
ArrayList<View>[] mScrapViews; 存的是全部廢棄的view,它就是咱們要說的重點。這個數組的每一個元素都是一個view的集合,其實也就是每一個類型一個集合。
RecycleBin.setViewTypeCount(int viewTypeCount)是在ListView的setAdapter裏面被調用的,這時就會根據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); ...... }
AbsListView的obtainview方法建立或者複用回收站裏面的view
absListView的trackMotionScroll()--->mRecycler.addScrapView(child, position);
trackMotionScroll()滾動時會被調用,這裏面判斷哪些view被廢棄。
因此ListView滾動時,斷定哪些view超出屏幕,超出就被放入回收站裏,等又須要view的時候,listview根據viewType類型從回收站裏取出來複用,並回調到adapter的getView()方法裏面,從而達到多類型複用的效果。