连接AdapterView视图和数据源的桥梁:Adapter适配器

Posted AellenLei

tags:

篇首语:本文由小常识网(cha138.com)小编为大家整理,主要介绍了连接AdapterView视图和数据源的桥梁:Adapter适配器相关的知识,希望对你有一定的参考价值。

BaseAdapter是一种使用频率较高的适配器,因为它可以通过自定义最大程度扩展满足各种情景下的使用。我们不仅需要知道适配器的使用,进一步我们也需要了解适配器的原理。

问题是最好的学习方式,下面主要讨论这么几个问题:

Q1.ListView中每个Item的创建

Q2.ListView中Item的复用

Q3.ListView中屏幕显示的Item与复用生成Item之间的关系

  首先写一个简单的demo.

技术分享
 1 package com.aellenlei.baseadapterdemo;
 2 
 3 import android.support.v7.app.AppCompatActivity;
 4 import android.os.Bundle;
 5 import android.widget.ListView;
 6 
 7 import java.util.ArrayList;
 8 import java.util.List;
 9 
10 public class MainActivity extends AppCompatActivity {
11 
12     @Override
13     protected void onCreate(Bundle savedInstanceState) {
14         super.onCreate(savedInstanceState);
15         setContentView(R.layout.activity_main);
16 
17         //1.findViewById
18         ListView listView = (ListView) findViewById(R.id.listView);
19 
20         //2.初始化数据源
21         List<ItemBean> list = new ArrayList<>();
22         for (int i = 0; i < 5; i++) {
23             ItemBean itemBean = new ItemBean();
24             itemBean.postion = i;
25             itemBean.title = "title " + i;
26             itemBean.content = "content " + i;
27             list.add(itemBean);
28         }
29 
30         //3.初始化适配器
31         ItemBeanAdapter itemBeanAdapter = new ItemBeanAdapter(list, getApplicationContext());
32 
33         //4.ListView绑定适配器
34         listView.setAdapter(itemBeanAdapter);
35     }
36 }
MainActivity.java
技术分享
 1 package com.aellenlei.baseadapterdemo;
 2 
 3 import android.content.Context;
 4 import android.util.Log;
 5 import android.view.LayoutInflater;
 6 import android.view.View;
 7 import android.view.ViewGroup;
 8 import android.widget.BaseAdapter;
 9 import android.widget.CheckBox;
10 import android.widget.ImageView;
11 import android.widget.TextView;
12 
13 import java.util.List;
14 
15 /**
16  * User AellenLei
17  * NAME ItemBeanAdapter
18  * DATE 2016/3/7
19  */
20 public class ItemBeanAdapter extends BaseAdapter {
21 
22     private List<ItemBean> mData;
23     private Context mContext;
24 
25     public ItemBeanAdapter(List<ItemBean> mData, Context mContext) {
26         this.mData = mData;
27         this.mContext = mContext;
28     }
29 
30     @Override
31     public int getCount() {
32         return mData == null ? 0 : mData.size();
33     }
34 
35     @Override
36     public ItemBean getItem(int position) {
37         return mData.get(position);
38     }
39 
40     @Override
41     public long getItemId(int position) {
42         return position;
43     }
44 
45     @Override
46     public View getView(int position, View convertView, ViewGroup parent) {
47 
48         Log.d("msg", position + "," + getItem(position).postion + "," + getItem(position).title + ".  "
49                 + ((convertView == null) ? ("covertView = null") :
50                 (((TextView) convertView.findViewById(R.id.item_title)).getText().toString()))
51         );
52 
53 
54         View ret;
55 
56         if (convertView != null) {
57             ret = convertView;
58         } else {
59             ret = LayoutInflater.from(mContext).inflate(R.layout.item, null);
60             ViewHolder holder = new ViewHolder();
61             holder.itemIcon = (ImageView) ret.findViewById(R.id.item_icon);
62             holder.itemTitle = (TextView) ret.findViewById(R.id.item_title);
63             holder.itemContent = (TextView) ret.findViewById(R.id.item_content);
64             holder.itemDate = (TextView) ret.findViewById(R.id.item_date);
65             holder.itemChecked = (CheckBox) ret.findViewById(R.id.item_check);
66             ret.setTag(holder);
67         }
68 
69         ViewHolder viewHolder = (ViewHolder) ret.getTag();
70 
71         ItemBean itemBean = getItem(position);
72 
73         viewHolder.itemIcon.setImageResource(R.mipmap.ic_launcher);
74         viewHolder.itemTitle.setText(itemBean.title);
75         viewHolder.itemContent.setText(itemBean.content);
76         viewHolder.itemDate.setText("yyyy-MM-dd");
77         viewHolder.itemChecked.setChecked(itemBean.check);
78 
79         return ret;
80     }
81 
82     private static class ViewHolder {
83         private ImageView itemIcon;
84         private TextView itemTitle;
85         private TextView itemContent;
86         private TextView itemDate;
87         private CheckBox itemChecked;
88     }
89 }
ItemBeanAdapter
技术分享
 1 package com.aellenlei.baseadapterdemo;
 2 
 3 /**
 4  * User AellenLei
 5  * NAME ItemBean
 6  * DATE 2016/3/7
 7  */
 8 public class ItemBean {
 9     public int postion;
10     public String url;
11     public String title;
12     public String content;
13     public String date;
14     public boolean check;
15 }
ItemBean
技术分享
 1 <?xml version="1.0" encoding="utf-8"?>
 2 <RelativeLayout xmlns:android="http://schemas.android.com/apk/res/android"
 3     xmlns:tools="http://schemas.android.com/tools"
 4     android:layout_width="match_parent"
 5     android:layout_height="match_parent"
 6     android:paddingBottom="@dimen/activity_vertical_margin"
 7     android:paddingLeft="@dimen/activity_horizontal_margin"
 8     android:paddingRight="@dimen/activity_horizontal_margin"
 9     android:paddingTop="@dimen/activity_vertical_margin"
10     tools:context=".MainActivity">
11 
12     <ListView
13         android:id="@+id/listView"
14         android:layout_width="match_parent"
15         android:layout_height="match_parent"/>
16 </RelativeLayout>
activity_main.xml
技术分享
 1 <?xml version="1.0" encoding="utf-8"?>
 2 <RelativeLayout xmlns:android="http://schemas.android.com/apk/res/android"
 3     android:layout_width="match_parent"
 4     android:layout_height="match_parent"
 5     android:padding="8dp">
 6 
 7     <ImageView
 8         android:id="@+id/item_icon"
 9         android:layout_width="wrap_content"
10         android:layout_height="wrap_content"
11         android:src="@mipmap/ic_launcher"
12         android:padding="4dp"/>
13 
14     <TextView
15         android:id="@+id/item_title"
16         android:layout_width="wrap_content"
17         android:layout_height="wrap_content"
18         android:textSize="20sp"
19         android:textStyle="bold"
20         android:layout_toRightOf="@id/item_icon"
21         android:layout_alignTop="@id/item_icon"/>
22     <TextView
23         android:id="@+id/item_content"
24         android:layout_width="wrap_content"
25         android:layout_height="wrap_content"
26         android:textSize="16sp"
27         android:layout_below="@id/item_title"
28         android:layout_toRightOf="@id/item_icon"
29         android:layout_alignBottom="@id/item_icon"/>
30 
31     <TextView
32         android:id="@+id/item_date"
33         android:layout_width="wrap_content"
34         android:layout_height="wrap_content"
35         android:layout_toRightOf="@id/item_title"
36         android:layout_alignParentRight="true"
37         android:gravity="right"/>
38     <CheckBox
39         android:id="@+id/item_check"
40         android:layout_width="wrap_content"
41         android:layout_height="wrap_content"
42         android:text="@null"
43         android:layout_below="@id/item_date"
44         android:layout_alignParentRight="true"
45         android:gravity="center"
46         android:layout_alignBottom="@id/item_content"/>
47 </RelativeLayout>
item.xml

 由于该Demo比较简单,不需多讲相信都可以看懂。

 ItemBeanAdapter.java的getView方法中有这么一句:

  技术分享

  分别打印的是ListView中每一个Item在ListView中的位置(默认从0开始,下同),该Item显示的数据源中指定位置数据的poistion和title,convertView是否为空若不为空打印convertView之前显示的title.

 当数据源中的数据条数为5时,Logcat的日志:

  技术分享

  UI:

  技术分享

 

 当数据源中的数据条数为10是,Logcat的日志:

  技术分享

  UI:

  技术分享

  

  根据这两种情况的测试,可以大概回答第一个问题:

Q1.ListView中每个Item的创建

A1.Adapter第一次创建的Items的数量是由手机屏幕的大小(可测试)和数据源数据的条数来决定的,也就是屏幕实际显示多少个Item就创建几个item的,不多创建新的item也不少创建新的item,items数量是屏幕实际显示数目的取整。将下面的完整结合来看,第一次创建的Item是最基本的Item,它的数量是确定的,以后新的item无论是向上滑动出现还是向下滑动出现都是复用第一次创建的items中的某个item。

 

Q2.ListView中Item的复用

A2:最核心的代码就是Adapter中的getView方法,它返回的是一个已经绑定好数据的view,而系统仅仅只是将这个view在屏幕的指定位置绘制出来。

  技术分享

  代码不是固定死的,当然你可以有自己的写法,但是原理总是相同的:

    A2:当ListView第一次创建一屏幕的items时,covertView始终为null(代码测试很容易得出),所以当covertView为空时,就需要将第一次创建一屏幕的items的每个item“初始化”,这里的“初始化”是将covertView和ViewHolder绑定起来,注意不论是将covertView和ViewHolder绑定起来还是ret和ViewHolder绑定起来,它们的本质是一样的,最后返回值是已经与ViewHolder绑定的View视图,当掌握了covertView的复用写法,可以说是基本上item的复用的写法也掌握了。

 

    注意下面一种情况,当ListView向上滑动,且item0完全不见,item7和item8出现(下图item8已经出现,只是没有完全显示)的情况:

    技术分享

    此时Logcat打印的日志:

    技术分享  

    根据之前的第一次创建items打印的日志比较:item7仍然是新创建的,但是item8却是复用的,item8复用的是item0(完全根据日志得出的)。(PS 可能此处有疑问,下面会分析)

    A2:当listView向上滑动或者是向下滑动的时候,此时可能会出现item复用的情况(注意此时可能会出现复用的情况,不一定或出现哦)。若covertView不为空,就可以之前在该covertView初始化或复用中通过getTag方法,取出与之绑定的ViewHolder,从而实现减少findViewById的时间,findViewById是需要耗费时间的,当listView显示大量的数据,此时的findViewByid可以极大的提高效率。

 

    最后分析总结前面的,可以回答第三个问题:

Q3.ListView中屏幕显示的Item与复用生成Item之间的关系

A3:ListView实际创建item的数量是由手机屏幕的大小和数据源的数据数量来决定的,准确的将这是不准确的或是错误的。

   ListView在整个复用过程中本质上实际创建item的数量(这里所指的全部是最原始最本质的item)是由手机屏幕的大小、数据源的数据数量和每个item实际的大小来决定的(当然这里不考虑其他更为负责的情况,而是假定每个item的大小相同)。

   用可以唯一衡量确定的话说是:本质上items的数量是当第一个item完全消失后,此时Adapter总共创建的items数量,从本质上来说,这就是ListView在整个复用过程中复用的item的数量。假如从0,1,...,n-1(n为最原始的item数量)来看,当ListView向上滑动时,复用tem的顺序是按顺序复用0,1,...,n-1,每次复用一个;当ListView向下滑动时,复用的顺序是按照逆序的,从n-1,n-2,..,0,也是一个一个复用的。

  当然还有更为复杂的情况或者说从更为本质也就是源码的角度分析,这里暂不考虑,而是从一种最为表象或者最最最基本最最简单来分析ListView与Adapter。

 

 

以上是关于连接AdapterView视图和数据源的桥梁:Adapter适配器的主要内容,如果未能解决你的问题,请参考以下文章

AD活动目录FSMO五大角色

在层次结构中找不到匹配的视图:可从类分配:类 android.widget.AdapterView

Android高级控件--AdapterView与Adapter

Android AdapterView 点击监听参数-位置&id

MVVM框架

MVPMVC和MVVM框架介绍