listview的重要属性viewtype,实现多类型item样式

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

直接贴代码,再分析。

哦,在这之前,先效果图:




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

getViewTypeCount()
getItemViewType(int position)
这两个方法。

MultiStyleListAdapter.java:

<pre name="code" class="java">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,并绑定对应数据。

注意getViewType返回值,必须从0开始,依次增大。否则要抛ArrayIndexOutOfBoundsException异常,原因文章后面分析。


AbstractListAdapter.java 继承至BaseAdapter,只是对它进行部分重写和封装,以及使用泛型后的一个基类。

还是把贴出来吧,免得对新手造成疑惑。

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();
        }
    }

}

然后添加示例数据:

MainActivity.java

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 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 Data3(R.drawable.img6,R.drawable.img7,R.drawable.img8));
        dataAll.add(new Data1("萨达姆分,吗,"));
        dataAll.add(new Data2(R.drawable.img9));
        dataAll.add(new Data2(R.drawable.img10));
        dataAll.add(new Data2(R.drawable.img11));
        dataAll.add(new Data1("暗红色的尽快和圣诞节快放假"));
        dataAll.add(new Data1("跨境开理发店了"));
        dataAll.add(new Data1("1434"));
        dataAll.add(new Data1("地方"));
        dataAll.add(new Data1("主线程执行"));
        dataAll.add(new Data4("ansdklma,sdm,",true));
        dataAll.add(new Data4("撒角度看",true));
        dataAll.add(new Data3(R.drawable.img12,R.drawable.img13,R.drawable.img14));
        dataAll.add(new Data1("暗红色的尽快和圣诞节快放假"));
        dataAll.add(new Data1("13123123"));
        dataAll.add(new Data1("154667678"));
        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;
}
} 

listview里的每个视图就是在这个方法中被创建的
absListView.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方法里面,从而达到多类型复用的效果

demo源码:
https://github.com/qinzhen308/ListviewExploreDemo
http://download.csdn.net/detail/qinzhen308/9603718

  • 0
    点赞
  • 3
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值