京东下625红米note4x京东版 有没有优惠

Android进阶笔记11:ListView篇之ListView显示多种类型的条目(item) - 鸿钧老祖 - 博客园
ListView可以显示多种类型的条目布局,这里写显示两种布局的情况,其他类似.
1. 这是MainActivity,MainActivity的布局就是一个ListView,太简单了这里就不写了,直接来到MainActivity,如下:
1 public class MainActivity extends Activity {
private ListV
private List&People&
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_main);
lv = (ListView) findViewById(R.id.lv);
lists=new ArrayList&&();
//0代表学生,1代表老师
People people1 = new People(0, "10");//类型,钱
People people2 = new People(1, "100");
People people3 = new People(1, "100");
People people4 = new People(0, "10");
lists.add(people1);
lists.add(people2);
lists.add(people3);
lists.add(people4);
lv.setAdapter(new MyAdapter());
class MyAdapter extends BaseAdapter{
public int getCount() {
return lists.size();
public Object getItem(int i) {
return lists.get(i );
public long getItemId(int i) {
public int getItemViewType(int position) {
if(lists.get(position).getType()==0){//当前JavaBean对象的类型
return 0;//学生类型
}else if(lists.get(position).getType()==1){
return 1;//老师类型
return 100;
public int getViewTypeCount() {
return 2;//总共有两个类型
public View getView(int position, View convertView, ViewGroup viewGroup) {
int currentType = getItemViewType(position);//当前类型
if(currentType==0){//学生类型
StudentViewHolder studentViewH
if(convertView==null){
studentViewHolder=new StudentViewHolder();
convertView=View.inflate(MainActivity.this,R.layout.item_lv_student,null);
studentViewHolder.tv0= (TextView) convertView.findViewById(R.id.num_money_stu);
convertView.setTag(studentViewHolder);
studentViewHolder= (StudentViewHolder) convertView.getTag();
//数据填充
studentViewHolder.tv0.setText(lists.get(position).getMoney());
}else if(currentType==1){//老师类型
TeacherViewHolder teacherViewH
if(convertView==null){
teacherViewHolder=new TeacherViewHolder();
convertView=View.inflate(MainActivity.this,R.layout.item_lv_teacher,null);
teacherViewHolder.tv1= (TextView) convertView.findViewById(R.id.num_money_teacher);
convertView.setTag(teacherViewHolder);
teacherViewHolder= (TeacherViewHolder) convertView.getTag();
//数据填充
teacherViewHolder.tv1.setText(lists.get(position).getMoney());
return convertV
/**学生item的Holder*/
class StudentViewHolder {
TextView tv0;
/**老师item的Holder*/
class TeacherViewHolder {
TextView tv1;
2. 上面使用到的bean类如下:
* 区分 学生 和 老师
* type = 1
* type = 0
6 public class People {
/**类型,0表示学生,1表示老师**/
public int
public People(int type, String money) {
this.type =
this.money =
public int getType() {
public void setType(int type) {
this.type =
public String getMoney() {
public void setMoney(String money) {
this.money =
3. ListView的Item的布局如下:
(1)学生Item布局,如下:
1 &?xml version="1.0" encoding="utf-8"?&
2 &LinearLayout xmlns:android="/apk/res/android"
android:layout_width="match_parent"
android:layout_height="match_parent"
android:orientation="horizontal"
android:background="#00ff00"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:text="学生的钱:" /&
android:id="@+id/num_money_stu"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:text="10" /&
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:text="学生布局"
android:id="@+id/button" /&
26 &/LinearLayout&
(2)老师item的布局,如下:
1 &?xml version="1.0" encoding="utf-8"?&
2 &LinearLayout xmlns:android="/apk/res/android"
android:layout_width="match_parent"
android:layout_height="match_parent"
android:orientation="horizontal"
android:background="#f9a5b2"&
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:text="老师的钱:"/&
android:id="@+id/num_money_teacher"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:text="100"/&
17 &/LinearLayout&
4.&结果图:
随笔 - 748使用ListView进行不同布局ItemView的显示 - 推酷
使用ListView进行不同布局ItemView的显示
实现的界面效果如下图所示,没有可以进行布局上的调整,所以看起来丑一点。
上图在一个ListView中显示了两种不同的ItemView,主要是使用BaseAdapter中的getItemViewType()方法,以及getViewTypeCount()方法实现的。
下面来说说详细的实现步骤:
1. 跟以前使用自定义adapter时一样,都需要编写itemView的xml布局文件。只不过在此例中需要编写两个xml布局文件,分别对应了两个不同的itemView。两个xml文件的代码如下:
2.自定义一个Adapter类继承自BaseAdapter。并实现 getItemViewType()方法,以及getViewTypeCount()方法。顾名思义,getItemViewType()方法是用来获取当前需要绘制的itemView的类型,getViewTypeCount()方法是用来获取不同的itemview的种类数,即总共有多少种不同种类的itemView。
自定义适配器的代码如下:
* Created by Kent on .
public class MyAdapter extends BaseAdapter {
private Context mContext =//上下文
private LayoutInflater mInflater =
private List&BaseItem& mData =//要显示的数据
public MyAdapter(Context context, List&BaseItem& data){
this.mInflater = LayoutInflater.from(context);
this.mData =
//添加一个新的Item,并通知listview进行显示刷新
public void addItem(BaseItem newItem){
this.mData.add(newItem);
this.notifyDataSetChanged();
public int getItemViewType(int position) {
return mData.get(position).getItem_type();
public int getViewTypeCount() {
return ItemType.ITEM_TYPE_MAX_COUNT;
public int getCount() {
if(mData == null){
return this.mData.size();
public Object getItem(int i) {
return mData.get(i);
public long getItemId(int i) {
public View getView(int position, View convertView, ViewGroup viewGroup) {
View viewItem1 =
View viewItem2 =
int itemType = this.getItemViewType(position);
if(itemType == ViewHolder1.ITEM_VIEW_TYPE_1){
//第一种item
ViewHolder1 viewHolder1 =
if(convertView == null){
//没有缓存过
viewHolder1 = new ViewHolder1();
viewItem1 = this.mInflater.inflate(R.layout.list_view_item_1, null, false);
viewHolder1.textView = (TextView)viewItem1.findViewById(R.id.
main_activity_list_view_item_1_textview);
viewHolder1.imageView = (ImageView)viewItem1.findViewById(R.id.
main_activity_list_view_item_1_imageview);
viewItem1.setTag(viewHolder1);
convertView = viewItem1;
viewHolder1 = (ViewHolder1)convertView.getTag();
viewHolder1.textView.setText(((ItemBean1) mData.get(position)).getName());
viewHolder1.imageView.setBackgroundResource(R.drawable.ic_launcher);
}else if(itemType == ViewHolder2.ITEM_VIEW_TYPE_2){
//第二种item
ViewHolder2 viewHolder2 =
if(convertView == null){
//没有缓存过
viewHolder2 = new ViewHolder2();
viewItem2 = this.mInflater.inflate(R.layout.list_view_item_2, null, false);
viewHolder2.textView1 = (TextView)viewItem2.findViewById(R.id.
main_activity_list_view_item_2_textview);
viewHolder2.textView2 = (TextView)viewItem2.findViewById(R.id.
main_activity_list_view_item_2_textview_2);
viewItem2.setTag(viewHolder2);
convertView = viewItem2;
viewHolder2 = (ViewHolder2)convertView.getTag();
viewHolder2.textView1.setText(((ItemBean2)mData.get(position)).getName());
viewHolder2.textView2.setText(((ItemBean2)mData.get(position)).getAddress());
return convertV
首先要说一下listview的绘制原理,在listview进行绘制的时候,首先要调用getCount()方法来确定listview的item个数,然后在绘制每个item的时候调用getView方法来进行绘制,而getItem和getItemId是在listview响应用户操作时间时候进行调用的。
这里着重介绍一下getView()方法,这个方法是在绘制每个item时进行调用的,它返回一个View对象,而此View就是需要绘制的View。
public View getView(int position, View convertView, ViewGroup viewGroup) :其中position参数为当前绘制的item的位置,convertView参数为当前要绘制的View,这个参数主要用来缓存,viewGroup参数是当前view的父控件。
关于 getItemViewType()方法,以及getViewTypeCount()方法将在稍后进行讨论。
3. listview数据源:
通常listview里面的数据是来源于一个list集合,而list集合中的元素是Map类型的数据。即:List&Map&String, Object&&每个map类型的数据对应了一个itemview中需要显示的内容。而本例中定义数据源是来自对象的,由于本例要显示不同的item,所以我们自定义出两个Bean,分别对应两种item中的数据。代码如下:
* Created by Kent on .
public class BaseItem {
private int item_type = 0;
public BaseItem(int item_type) {
this.item_type = item_
public int getItem_type() {
return item_
public void setItem_type(int item_type) {
this.item_type = item_
由于每个Item都需要一个type类型的变量来标识当前Item的种类,所以定义了一个基类:BaseItem类,它只包含一个item_type的属性,用来标识item的种类。
两个继承自它的子类:
* Created by Kent on .
public class ItemBean1 extends BaseItem{
private String name =
private String imagePath =
public ItemBean1(int item_type, String name, String imagePath) {
super(item_type);
this.name =
this.imagePath = imageP
public String getName() {
public void setName(String name) {
this.name =
public String getImagePath() {
return imageP
public void setImagePath(String imagePath) {
this.imagePath = imageP
public int getItemType(){
return super.getItem_type();
public void setItemType(int itemType){
super.setItem_type(itemType);
4. MainActivity中的代码:
public class MainActivity extends Activity {
private ListView listView
private MyAdapter myAdapter =
private List&BaseItem& mData =
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_main);
findViewsById();;
addListeners();
private void findViewsById(){
this.listView = (ListView)findViewById(R.id.main_activity_listview);
private void init(){
this.mData = new ArrayList&BaseItem&();
this.mData.add(new ItemBean1(ViewHolder1.ITEM_VIEW_TYPE_1, &name1&, &iamgePath1&));
this.mData.add(new ItemBean1(ViewHolder1.ITEM_VIEW_TYPE_1, &name2&, &iamgePath2&));
this.mData.add(new ItemBean1(ViewHolder1.ITEM_VIEW_TYPE_1, &name2&, &iamgePath2&));
this.mData.add(new ItemBean2(ViewHolder2.ITEM_VIEW_TYPE_2, &name1&, &address1&));
this.mData.add(new ItemBean2(ViewHolder2.ITEM_VIEW_TYPE_2, &name1&, &address1&));
this.mData.add(new ItemBean2(ViewHolder2.ITEM_VIEW_TYPE_2, &name1&, &address1&));
this.mData.add(new ItemBean1(ViewHolder1.ITEM_VIEW_TYPE_1, &name2&, &iamgePath2&));
this.mData.add(new ItemBean2(ViewHolder2.ITEM_VIEW_TYPE_2, &name1&, &address1&));
this.mData.add(new ItemBean1(ViewHolder1.ITEM_VIEW_TYPE_1, &name2&, &iamgePath2&));
this.mData.add(new ItemBean1(ViewHolder1.ITEM_VIEW_TYPE_1, &name2&, &iamgePath2&));
this.mData.add(new ItemBean1(ViewHolder1.ITEM_VIEW_TYPE_1, &name2&, &iamgePath2&));
this.mData.add(new ItemBean2(ViewHolder2.ITEM_VIEW_TYPE_2, &name1&, &address1&));
this.mData.add(new ItemBean2(ViewHolder2.ITEM_VIEW_TYPE_2, &name1&, &address1&));
this.mData.add(new ItemBean1(ViewHolder1.ITEM_VIEW_TYPE_1, &name2&, &iamgePath2&));
this.mData.add(new ItemBean1(ViewHolder1.ITEM_VIEW_TYPE_1, &name2&, &iamgePath2&));
this.mData.add(new ItemBean1(ViewHolder1.ITEM_VIEW_TYPE_1, &name2&, &iamgePath2&));
this.mData.add(new ItemBean1(ViewHolder1.ITEM_VIEW_TYPE_1, &name2&, &iamgePath2&));
this.mData.add(new ItemBean2(ViewHolder2.ITEM_VIEW_TYPE_2, &name1&, &address1&));
this.mData.add(new ItemBean2(ViewHolder2.ITEM_VIEW_TYPE_2, &name1&, &address1&));
this.mData.add(new ItemBean1(ViewHolder1.ITEM_VIEW_TYPE_1, &name2&, &iamgePath2&));
this.mData.add(new ItemBean1(ViewHolder1.ITEM_VIEW_TYPE_1, &name2&, &iamgePath2&));
this.mData.add(new ItemBean1(ViewHolder1.ITEM_VIEW_TYPE_1, &name2&, &iamgePath2&));
this.mData.add(new ItemBean1(ViewHolder1.ITEM_VIEW_TYPE_1, &name2&, &iamgePath2&));
this.mData.add(new ItemBean1(ViewHolder1.ITEM_VIEW_TYPE_1, &name2&, &iamgePath2&));
this.mData.add(new ItemBean2(ViewHolder2.ITEM_VIEW_TYPE_2, &name1&, &address1&));
this.myAdapter = new MyAdapter(this, this.mData);
this.listView.setAdapter(this.myAdapter);
private void addListeners(){
5. ViewHolder,用于ItemView的缓存优化:
* Created by Kent on .
public final class ViewHolder1 {
public static final int ITEM_VIEW_TYPE_1 = 0;
public TextView textView =
public ImageView imageView =
* Created by Kent on .
public final class ViewHolder2 {
public static final int ITEM_VIEW_TYPE_2 = 1;
public TextView textView1 =
public TextView textView2 =
* Created by Kent on .
public class ItemType {
public static final int ITEM_TYPE_MAX_COUNT = 2;
} 6. 实现不同item类型的显示;
首先来看下getItemViewType()方法;
public int getItemViewType(int position) {
return mData.get(position).getItem_type();
} 这个方法返回了当前要绘制的ItemView的类型,类型数据存储在listView的数据源List中的每个bean的itemType属性中。
再来看看getViewTypeCount()方法:
public int getViewTypeCount() {
return ItemType.ITEM_TYPE_MAX_COUNT;
} 这个方法返回了不同Item的种类个数。
最后来看看最重要的getView()方法:
public View getView(int position, View convertView, ViewGroup viewGroup) {
View viewItem1 =
View viewItem2 =
int itemType = this.getItemViewType(position);
if(itemType == ViewHolder1.ITEM_VIEW_TYPE_1){
//第一种item
ViewHolder1 viewHolder1 =
if(convertView == null){
//没有缓存过
viewHolder1 = new ViewHolder1();
viewItem1 = this.mInflater.inflate(R.layout.list_view_item_1, null, false);
viewHolder1.textView = (TextView)viewItem1.findViewById(R.id.
main_activity_list_view_item_1_textview);
viewHolder1.imageView = (ImageView)viewItem1.findViewById(R.id.
main_activity_list_view_item_1_imageview);
viewItem1.setTag(viewHolder1);
convertView = viewItem1;
viewHolder1 = (ViewHolder1)convertView.getTag();
viewHolder1.textView.setText(((ItemBean1) mData.get(position)).getName());
viewHolder1.imageView.setBackgroundResource(R.drawable.ic_launcher);
}else if(itemType == ViewHolder2.ITEM_VIEW_TYPE_2){
//第二种item
ViewHolder2 viewHolder2 =
if(convertView == null){
//没有缓存过
viewHolder2 = new ViewHolder2();
viewItem2 = this.mInflater.inflate(R.layout.list_view_item_2, null, false);
viewHolder2.textView1 = (TextView)viewItem2.findViewById(R.id.
main_activity_list_view_item_2_textview);
viewHolder2.textView2 = (TextView)viewItem2.findViewById(R.id.
main_activity_list_view_item_2_textview_2);
viewItem2.setTag(viewHolder2);
convertView = viewItem2;
viewHolder2 = (ViewHolder2)convertView.getTag();
viewHolder2.textView1.setText(((ItemBean2)mData.get(position)).getName());
viewHolder2.textView2.setText(((ItemBean2)mData.get(position)).getAddress());
return convertV
前面已经说过了,getView是在每个Item进行绘制的时候进行调用的,那么想要进行不同类型Item的显示,在这个方法中根据item的类型进行不同的绘制就可以了。
首先,通过
int itemType = this.getItemViewType(position); 获取到当前要绘制的Item的种类。然后根据不同的种类进行不同的布局导入。
viewItem1 = this.mInflater.inflate(R.layout.list_view_item_1, null, false);
这个方法使用了ViewHolder进行itemView的缓存。有关内容在此不做解释
源码下载地址:http://download.csdn.net/detail/hhzz/8262015
已发表评论数()
请填写推刊名
描述不能大于100个字符!
权限设置: 公开
仅自己可见
正文不准确
标题不准确
排版有问题
主题不准确
没有分页内容
图片无法显示
视频无法显示
与原文不一致查看:9720|回复:6
资深技术经理
ListView实现不同item的方法和原理分析
一问题抛出
Listview是android里面的重要组件,用来显示一个竖向列表,这个没有什么问题;但是有个时候列表里面的item不是一样的,如下图,列表里面应该有3种类型的item
(48.79 KB)
1. 头像在左边的气泡Item ,比如”今天下午我就不出来了,...”
2. 头像在右边的气泡Item,比如”那就等着我发你好吧”
3. 单张图片显示圆角图片item
几种Item的风格是完全不同的,那么怎么实现呢?
二实现方法
实现的方法我这里可以列举出两种
1. 每个Item的布局文件包含气泡,左右头像和圆角图片,然后根据不同的条件做不同的逻辑判断,控制不同Item的显示和隐藏。
比如如果是接受信息,而且是文字的话,就显示左图片和文字;隐藏图片和右边图片,等等。
显而易见,这种方法很繁琐,很笨重,而且会导致item的布局文件很大,从而影响listview的效率(加载xml文件是需要时间的)
2. 使用listview提供的方法,实现的步骤如下:
a. 主要重写ListAdapter的newView(), getItemViewType(),getViewItemTypeCount()几个方法,如下:public class MyCursorAdapter extends CursorAdapter {
publicMyCursorAdapter(Context context, Cursor c, boolean autoRequery) {
super(context, c,autoRequery);
// TODO Auto-generatedconstructor stub
publicMyCursorAdapter(Context context, Cursor c, int flags) {
super(context, c, flags);
// TODO Auto-generatedconstructor stub
public void bindView(Viewarg0, Context arg1, Cursor arg2) {
publicView newView(Context arg0, Cursor arg1, ViewGroup arg2) {
if (接受文字){
return 接受文字view
}else if(发送文字){
return 发送文字view
return 圆角图片view
public int getItemViewType(int position) {
StringitemValue = getCursor().getString(position);
//下面的代码只是模拟判断逻辑
//另外,这个序号是从0开始索引的,由于我们有3种类型的item,所以返回0,1,2,请参考getViewTypeCount()
if(itemValue.equals(&接收文字信息&)){//如果是接受文字信息,则显示布局1
}else if(itemValue.equals(&发送文字信息&)){//如果是发送文字信息,则显示布局2
return 2;//显示单张图片
public intgetViewTypeCount() {
return 3;//有3种类型的item,所以返回3
}嗯,所做的差不多就是这么多,另外就是要准备3个item布局文件,也就是newView里面要调用的3个布局文件
对了,在bindView的时候最好对view进行null的检查,因为3个布局文件里面的view是不同的,或者要分开进行bind,不然有可能会有空指针异常。
三 原理分析
上面第2种实现方法确实比较灵活,那listview是怎么实现的呢?
而且我们知道listview的item是可以复用的,那么为什么它不会复用错位呢?比如第2种类型的item,结果找到了缓存中第1种类型的item,就像本来要显示一个发送图片,结果找到发送文字的item,那么复用的时候肯定有问题,因为发送文字的item中根本没有ImageView,只有TextView来显示文字的。
1. 文件路径
frameworks\base\core\java\android\widget\AbsListView.java
* The data set used to store unused viewsthat should be reused during the next layout
* to avoid creating new ones
final RecycleBin mRecycler = newRecycleBin();
View obtainView(int position,boolean[] isScrap) {
final View scrapView =mRecycler.getScrapView(position);
}在ListView的一个item要显示的时候,就会调用AbsListView.obtainView()方法,比如滑动的时候,滑动出一个Item
AbsListview会向RecycleBin请求一个scrapView,这个RecycleBin是listview里面的一个重要机制,简单来说,就是它缓存了那些不在屏幕内的listview item,相当于一个垃圾箱,然后当有新的item需要显示的时候,它会首先向垃圾箱里面请求一个已经不显示的item,如果有这样的item的话,就直接拿过来,然后调用下bindView,重新bind下数据就可以了。
如果没有这样的item就会调用newView去创建一个item view。
滑动的时候,它会不断地把滑出屏幕的item添加到RecycleBin这个垃圾箱里面。
这样就实现了一个循环,listview不管有多少数据,不管滑动多少次,真正通过newView产生的item view其实就是一个屏幕内最多容纳的item数目,形成一个链条结构,不断回收,不断复用。
那接下来看看mRecycler.getScrapView(position)的实现private ArrayList&View&[] mScrapV
private ArrayList&View& mCurrentS
View getScrapView(int position) {
if (mViewTypeCount ==1) {
return retrieveFromScrap(mCurrentScrap,position);
int whichScrap =mAdapter.getItemViewType(position);
if (whichScrap&= 0 && whichScrap & mScrapViews.length) {
returnretrieveFromScrap(mScrapViews[whichScrap], position);
}这个viewTypeCount就是ListAdapter的getViewTypeCount()方法返回的,默认实现就是返回1,如果没有重写的话,在setAdapter的时候调用,代表的是listview里面会有多少种类型的item,如下:public void setAdapter(ListAdapter adapter) {
super.setAdapter(adapter);
if (mAdapter != null){
mRecycler.setViewTypeCount(mAdapter.getViewTypeCount());
}如果为viewTypeCount==1的话,也就是只有一种类型的item,那么直接从mCurrentScrap里面获取即可。
那如果有多个类型的item的话,怎么办呢?
首先,调用我们重写的getItemViewType(int position)来获取到这种类型的item的索引号
int whichScrap = mAdapter.getItemViewType(position);
然后根据这个索引号whichScrap从mScrapView数组里面获取到一个垃圾箱,然后再从垃圾箱里面去获取这个类型的被回收的Item。
这样就解决了复用错误的问题,比如把第2种类型的item复用了缓存中第1种类型的Item,这样就解决了第三章开头说的那个复用错位的问题。
2. Listview是怎么把一个item添加到垃圾箱?
那么,我们来拿一个简单的情景来举例子,比如滑动的时候
文件路径:
frameworks\base\core\java\android\widget\AbsListView.java
* Track a motion scroll
* @param deltaY Amount tooffset mMotionView. This is the accumulated delta since the motion
began. Positive numbers mean the user'sfinger is moving down the screen.
* @param incrementalDeltaYChange in deltaY from the previous event.
true if we'realready at the beginning/end of the list and have nothing to do.
boolean trackMotionScroll(intdeltaY, int incrementalDeltaY) {
if (down) {//向上滚动
int top =-incrementalDeltaY;
for (int i = 0; i& childC i++) {
final View child= getChildAt(i);
if(child.getBottom() &= top) {
int position= firstPosition +
if (position&= headerViewsCount && position & footerViewsStart) {
// Theview will be rebound to new data, clear any
//system-managed transient state.
if(child.isAccessibilityFocused()) {
child.clearAccessibilityFocus();
mRecycler.addScrapView(child, position);
} else {//向下滚动
int bottom = getHeight() -incrementalDeltaY;
if ((mGroupFlags& CLIP_TO_PADDING_MASK) == CLIP_TO_PADDING_MASK) {
bottom -=listPadding.
for (int i =childCount - 1; i &= 0; i--) {
final View child = getChildAt(i);
if(child.getTop() &= bottom) {
int position= firstPosition +
if (position&= headerViewsCount && position & footerViewsStart) {
// Theview will be rebound to new data, clear any
//system-managed transient state.
if(child.isAccessibilityFocused()) {
child.clearAccessibilityFocus();
mRecycler.addScrapView(child, position);
}如果向上滚动的话,那么就判断item的bottom-滚动距离 &=0?如果是,那么说明这个item还是可见的,不应该添加到垃圾箱;否则就不可见了。
这个判断逻辑要结合下手机屏幕坐标系来理解,如下:
图有点丑,勿见怪,坐标原点(0,0)是在屏幕的左上方,这个有点特别。
那么如果是向上滑动话的,我们要判断某个item是否被滑动出了屏幕,就是判断这个item的bottom – 向上滚动量& 0?比如一个item 的最下面的边界是50px那个地方,然后向上滚动了60px,那么肯定已经滑动出了屏幕,对不对?
也就是50 – 60 = -10 &0
如果只是滑动了40px,那么这个item应该还有10px留着屏幕上面,这个时候肯定不能被回收,因为它对于用户还是可见的。
也就是 50 – 40 = 10 &0
如果刚好滑动了50px,按照listview 的逻辑,这个item也是不回收的。如下:if (child.getBottom() &=top) {
}再排除是否是listview 的header或者footer,如果不是的话,那就是listview的内容item了,应该添加到垃圾箱里面。else {
int position= firstPosition +
if (position&= headerViewsCount && position & footerViewsStart) {
// Theview will be rebound to new data, clear any
//system-managed transient state.
if(child.isAccessibilityFocused()) {
child.clearAccessibilityFocus();
mRecycler.addScrapView(child,position);
}为了清理内存,它会先清理掉这个itemview的一些属性,然后调用mRecycler.addScrapView(child, position);添加到垃圾箱。
那如果是向下滑动呢?
根据上面手机的坐标系,这个时候肯定是判断item的top和整个ListView的高度以及滚动距离。应该是top+ 滚动距离 & 整个ListView的高度,这个时候说明item已经不可见;如果top + 滚动距离 &= 整个ListView的高度,就说明这个item还是可见的。int bottom = getHeight() -incrementalDeltaY;
if (child.getTop() &=bottom) {//仍然可见
}好,分析完这个滚动的计算逻辑后,来看看如何把view添加到垃圾箱的。void addScrapView(View scrap,int position) {
if(mViewTypeCount == 1) {
//如果只有一种item类型,直接添加
mCurrentScrap.add(scrap);
} else {//如果有多种item类型,找到viewType对应的垃圾箱添加
mScrapViews[viewType].add(scrap);
1. 这篇帖子总结Listview中如果有多种类型的item的实现方式和原理。
2. 多个Item实现的原理主要就是AbsListView中有个mScrapViews数组,它的大小对应着Item类型的数目,也就是getItemTypeCount的返回大小。这个mScrapViews里面根据viewType的值,把不同类型的item存放在不同的ArrayList里面;
然后获取的时候再根据这个viewType首先来找到对应的ArrayList垃圾箱,然后再从ArrayList垃圾箱里面找到同一个类型的缓存item,当然如果没有找到,就会调用newView新建。
3. 分析了滚动的情况下,listview判断item是否可见的实现原理,它是根据item的坐标来判断的。
本帖最后由 rongwei84n 于
18:08 编辑
资深技术经理
实现不同item也可以看看这篇博客
实现不同item也可以看看这篇博客
版主,受教了!

我要回帖

更多关于 京东红米note 的文章

 

随机推荐