Android笔记——BaseAdapter的使用

      Android中的适配器(Adapter)是数据与视图(View)之间的桥梁,用于对要显示的数据进行处理,并通过绑定到组件进行数据的显示。

  BaseAdapter是Android应用程序中经常用到的基础数据适配器的基类,它实现了Adapter接口。其主要用途是将一组数据传到像ListView、Spinner、Gallery及GridView等UI显示组件进行显示。我们经常使用的ListView 的adapter(即SimpleAdapter),是继承自BaseAdapter基类的。BaseAdapter是一个基类,没有实现绑定数据的功能。而SimpleAdapter实现了基本控件的绑定,如TextView,Button,ImageView等。并已经为我们实现好了数据优化工作。

  这些适配器使用相同组件动态绑定数据的方式进行优化。为什么需要优化呢?因为如果我们有上亿个(较多个)项目要显示怎么办?为每个项目创建一个新视图?这不可能,因为内存有限制。实际上Android为你缓存了视图。Android中有个叫做Recycler的构件,下图是他的工作原理:

       

      如果你有10亿个项目(item),其中只有可见的项目存在内存中,其他的在Recycler中。其实我的理解Recyler就是一个队列,用来存储不在屏幕范围内的item,如果item滚出屏幕范围,那么就入队,这里的滚出是完全滚出,即边界等也要完全滚出。如果新的item要滚进来,那么android系统的framework就会查看Recyler是否含有可以重复使用的View,如果有那么就重新设置该View 的数据源,然后显示,即出队。那么这么多的item其实只需要占用一定空间的内存,这个内存大小是多少呢?我的感觉是手机屏幕所包含的item的个数,再加上1,然后乘以每个item占用的内存。但是最后我发现是加上2.可能是为了使得缓存更大吧。。。。但是为什么加上2,大家应该理解,如果你不理解,那你就把滚动list的过程好好想一想。那个队列无非就是一个缓存罢了,因为我们的目的是通过那个缓存来重复使用那些已经创建的View。

     使用BaseAdapter必须写一个类继承它,同时BaseAdapter是一个抽象类,继承它必须实现它的方法。BaseAdapter的灵活性就在于它要重写很多方法,看一下有哪些方法,如图4-35所示为继承自BaseAdapter的SpeechListAdapter所实现的方法,其中最重要的即为getView()方法。这些方法都有什么作用呢?我们通过分析ListView的原理来为读者解答。  

     当系统开始绘制ListView的时候,首先调用getCount()方法。得到它的返回值,即ListView的长度。然后系统调用getView()方法,根据这个长度逐一绘制ListView的每一行。也就是说,如果让getCount()返回1,那么只显示一行。而getItem()和getItemId()则在需要处理和取得Adapter中的数据时调用。那么getView如何使用呢?如果有10000行数据,就绘制10000次?这肯定会极大的消耗资源,导致ListView滑动非常的慢,那应该怎么做呢?通过一个例子来讲解如何在使用BaseAdapter的时候优化ListView的显示。例子中将上一节中的ImageView换成Button,并且处理Button的点击事件,其中对ListView的显示做了优化。

     在BaseAdapter中需要实现一个继承自BaseAdapter的类,并且重写里面的很多方法,例如

 1 class MyAdapter extends BaseAdapter
 2     {
 3         private Context context;
 4         public MyAdapter(Context context)
 5         {
 6             this.context = context;
 7         }
 8         @Override
 9         public int getCount() {
10             // How many items are in the data set represented by this Adapter.(在此适配器中所代表的数据集中的条目数)
11             return 0;
12         }
13 
14         @Override
15         public Object getItem(int position) {
16             // Get the data item associated with the specified position in the data set.(获取数据集中与指定索引对应的数据项)
17             return null;
18         }
19 
20         @Override
21         public long getItemId(int position) {
22             // Get the row id associated with the specified position in the list.(取在列表中与指定索引对应的行id)
23             return 0;
24         }
25 
26         @Override//用来刷新它所在的ListView的.在每一次item从屏幕外滑进屏幕内的时候,或者程序刚开始的时候创建第一屏item的时候调用
27         public View getView(int position, View convertView, ViewGroup parent) {
28             // Get a View that displays the data at the specified position in the data set.
29             return null;
30         }
31         
32     }

      这里面没什么难度,但是这个getView方法必须好好处理,也是最麻烦的

      第一种:没有任何处理,不建议这样写。如果数据量少看将就,但是如果列表项数据量很大的时候,会每次都重新创建View,设置资源,严重影响性能,所以从一开始就不要用这种方式

 1 @Override
 2         public View getView(int position, View convertView, ViewGroup parent) {
 3             View item = mInflater.inflate(R.layout.list_item, null);
 4             ImageView img = (ImageView)item.findViewById(R.id.img) 
 5             TextView title = (TextView)item.findViewById(R.id.title);
 6             TextView info = (TextView)item.findViewById(R.id.info);
 7             img.setImageResource(R.drawable.ic_launcher);
 8             title.setText("Hello");
 9             info.setText("world");
10             
11             return item;
12         }

      第二种ListView优化:通过缓存convertView,这种利用缓存contentView的方式可以判断如果缓存中不存在View才创建View,如果已经存在可以利用缓存中的View,提升了性能

 1 public View getView(int position, View convertView, ViewGroup parent) {
 2             if(convertView == null)
 3             {
 4                 convertView = mInflater.inflate(R.layout.list_item, null);
 5             }
 6             
 7             ImageView img = (ImageView)convertView.findViewById(R.id.img) 
 8             TextView title = (TextView)convertView.findViewById(R.id.title);
 9             TextView info = (TextView)ConvertView.findViewById(R.id.info);
10             img.setImageResource(R.drawable.ic_launcher);
11             title.setText("Hello");
12             info.setText("world");
13             
14             return convertView;
15         }

      第三种ListView优化:通过convertView+ViewHolder来实现,ViewHolder就是一个静态类,使用 ViewHolder 的关键好处是缓存了显示数据的视图(View),加快了 UI 的响应速度。

      当我们判断 convertView == null  的时候,如果为空,就会根据设计好的List的Item布局(XML),来为convertView赋值,并生成一个viewHolder来绑定converView里面的各个View控件(XML布局里面的那些控件)。再用convertView的setTag将viewHolder设置到Tag中,以便系统第二次绘制ListView时从Tag中取出。(看下面代码中)

      如果convertView不为空的时候,就会直接用convertView的getTag(),来获得一个ViewHolder。

 1 //在外面先定义,ViewHolder静态类
 2     static class ViewHolder
 3     {
 4         public ImageView img;
 5         public TextView title;
 6         public TextView info;
 7     }
 8 //然后重写getView
 9         @Override
10         public View getView(int position, View convertView, ViewGroup parent) {
11             ViewHolder holder;
12             if(convertView == null)
13             {
14                 holder = new ViewHolder();
15                 convertView = mInflater.inflate(R.layout.list_item, null);
16                 holder.img = (ImageView)item.findViewById(R.id.img) 
17                 holder.title = (TextView)item.findViewById(R.id.title);
18                 holder.info = (TextView)item.findViewById(R.id.info);
19                 convertView.setTag(holder);
20             }else
21             {
22                 holder = (ViewHolder)convertView.getTag();
23                 holder.img.setImageResource(R.drawable.ic_launcher);
24                 holder.title.setText("Hello");
25                 holder.info.setText("World");
26             }
27             
28             return convertView;
29         }

     到这里,可能会有人问ViewHolder静态类结合缓存convertView与直接使用convertView有什么区别吗,是否重复了

     在这里,官方给出了解释

     提升Adapter的两种方法

To work efficiently the adapter implemented here uses two techniques:
-It reuses the convertView passed to getView() to avoid inflating View when it is not necessary

(译:重用缓存convertView传递给getView()方法来避免填充不必要的视图)
-It uses the ViewHolder pattern to avoid calling findViewById() when it is not necessary

(译:使用ViewHolder模式来避免没有必要的调用findViewById():因为太多的findViewById也会影响性能)
ViewHolder类的作用
-The ViewHolder pattern consists in storing a data structure in the tag of the view
returned by getView().This data structures contains references to the views we want to bind data to,
thus avoiding calling to findViewById() every time getView() is invoked

(译:ViewHolder模式通过getView()方法返回的视图的标签(Tag)中存储一个数据结构,这个数据结构包含了指向我们

要绑定数据的视图的引用,从而避免每次调用getView()的时候调用findViewById())

原文地址:https://www.cnblogs.com/wugu-ren/p/6133211.html