什么是数据适配器?java
下图展现了数据源、适配器、ListView等数据展现控件之间的关系。咱们知道,数据源是各类各样的,而ListView所展现数据的格式则是有必定的要求的。数据适配器正是创建了数据源与ListView之间的适配关系,将数据源转换为ListView可以显示的数据格式,从而将数据的来源与数据的显示进行解耦,下降程序的耦合性。这也体现了Android的适配器模式的使用。对于ListView、GridView等数据展现控件有多种数据适配器,本文讲解最通用的数据适配器——BaseAdapter。android
.ListView的显示与缓存机制缓存
咱们知道,ListView、GridView等控件能够展现大量的数据信息。假以下图中的ListView能够展现100条信息,可是屏幕的尺寸是有限的,一屏幕只能显示下图中的7条。当向上滑动ListView的时候,item1被滑出了屏幕区域,那么系统就会将item1回收到Recycler中,即View缓冲池中,而将要显示的item8则会从缓存池中取出布局文件,并从新设置好item8须要显示的数据,并放入须要显示的位置。这就是ListView的缓冲机制,总结起来就是一句话:须要时才显示,显示完就被会收到缓存。ListView,GridView等数据显示控件经过这种缓存机制能够极大的节省系统资源。app
.BaseAdapteride
使用BaseAdapter比较简单,主要是经过继承此类来实现BaseAdapter的四个方法:布局
public int getCount(): 适配器中数据集的数据个数;性能
public Object getItem(int position): 获取数据集中与索引对应的数据项;优化
public long getItemId(int position): 获取指定行对应的ID;this
public View getView(int position,View convertView,ViewGroup parent): 获取没一行Item的显示内容。spa
下面经过一个简单示例演示如何使用BaseAdapter。
1.建立布局文件
activity_main.xml
<?xml version="1.0" encoding="utf-8"?> <RelativeLayout xmlns:android="http://schemas.android.com/apk/res/android" xmlns:tools="http://schemas.android.com/tools" android:layout_width="match_parent" android:layout_height="match_parent" tools:context="com.cbt.learnbaseadapter.MainActivity"> <ListView android:id="@+id/lv_main" android:layout_width="match_parent" android:layout_height="match_parent" /> </RelativeLayout>
item.xml (ListView中每条信息的显示布局)
<RelativeLayout xmlns:android="http://schemas.android.com/apk/res/android" android:layout_width="match_parent" android:layout_height="wrap_content"> <ImageView android:id="@+id/iv_image" android:src="@mipmap/ic_launcher" android:layout_width="60dp" android:layout_height="60dp"/> <TextView android:id="@+id/tv_title" android:layout_width="match_parent" android:layout_height="30dp" android:layout_toEndOf="@id/iv_image" android:text="Title" android:gravity="center" android:textSize="25sp"/> <TextView android:id="@+id/tv_content" android:layout_width="wrap_content" android:layout_height="wrap_content" android:layout_toEndOf="@id/iv_image" android:layout_below="@id/tv_title" android:text="Content" android:textSize="20sp"/> </RelativeLayout>
2.建立数据源
ItemBean.java
package com.cbt.learnbaseadapter; /** * Created by caobotao on 15/12/20. */ public class ItemBean { public int itemImageResId;//图像资源ID public String itemTitle;//标题 public String itemContent;//内容 public ItemBean(int itemImageResId, String itemTitle, String itemContent) { this.itemImageResId = itemImageResId; this.itemTitle = itemTitle; this.itemContent = itemContent; } }
经过此Bean类,咱们就将要显示的数据与ListView的布局内容一一对应了,每一个Bean对象对应ListView的一条数据。这种方法在ListView中使用的很是普遍。
MainActivity.java
package com.cbt.learnbaseadapter; import android.support.v7.app.AppCompatActivity; import android.os.Bundle; import android.widget.ListView; import java.util.ArrayList; import java.util.List; public class MainActivity extends AppCompatActivity { ListView mListView ; @Override protected void onCreate(Bundle savedInstanceState) { super.onCreate(savedInstanceState); setContentView(R.layout.activity_main); List<ItemBean> itemBeanList = new ArrayList<>(); for (int i = 0;i < 20; i ++){ itemBeanList.add(new ItemBean(R.mipmap.ic_launcher, "标题" + i, "内容" + i)); } mListView = (ListView) findViewById(R.id.lv_main); //设置ListView的数据适配器 mListView.setAdapter(new MyAdapter(this,itemBeanList)); } }
3.建立BaseAdapter
经过上面的讲解,咱们知道继承BaseAdapter须要从新四个方法:getCount、getItem、getItemId、getView。其中前三个都比较简单,而getView稍微比较复杂。一般重写getView有三种方式,这三种方法性能方面有很大的不一样。接下来咱们使用此三种方式分别实现MyAdapter。
第一种:逗比式
package com.cbt.learnbaseadapter; import android.content.Context; import android.view.*; import android.widget.BaseAdapter; import android.widget.ImageView; import android.widget.TextView; import java.util.List; /** * Created by caobotao on 15/12/20. */ public class MyAdapter extends BaseAdapter{ private List<ItemBean> mList;//数据源 private LayoutInflater mInflater;//布局装载器对象 // 经过构造方法将数据源与数据适配器关联起来 // context:要使用当前的Adapter的界面对象 public MyAdapter(Context context, List<ItemBean> list) { mList = list; mInflater = LayoutInflater.from(context); } @Override //ListView须要显示的数据数量 public int getCount() { return mList.size(); } @Override //指定的索引对应的数据项 public Object getItem(int position) { return mList.get(position); } @Override //指定的索引对应的数据项ID public long getItemId(int position) { return position; } @Override //返回每一项的显示内容 public View getView(int position, View convertView, ViewGroup parent) { //将布局文件转化为View对象 View view = mInflater.inflate(R.layout.item,null); /** * 找到item布局文件中对应的控件 */ ImageView imageView = (ImageView) view.findViewById(R.id.iv_image); TextView titleTextView = (TextView) view.findViewById(R.id.tv_title); TextView contentTextView = (TextView) view.findViewById(R.id.tv_content); //获取相应索引的ItemBean对象 ItemBean bean = mList.get(position); /** * 设置控件的对应属性值 */ imageView.setImageResource(bean.itemImageResId); titleTextView.setText(bean.itemTitle); contentTextView.setText(bean.itemContent); return view; } }
为何称这种getView的方式是逗比式呢?
经过上面讲解,咱们知道ListView、GridView等数据展现控件有缓存机制,而这种方式每次调用getView时都是经过inflate建立一个新的View对象,而后在此view中经过findViewById找到对应的控件,彻底没有利用到ListView的缓存机制。这种方式没有通过优化处理,对资源形成了极大的浪费,效率是很低的。
第二种:普通式
public View getView(int position, View convertView, ViewGroup parent) {//若是view未被实例化过,缓存池中没有对应的缓存 if (convertView == null) { convertView = mInflater.inflate(R.layout.item,null); } /** * 找到item布局文件中对应的控件 */ ImageView imageView = (ImageView) convertView.findViewById(R.id.iv_image); TextView titleTextView = (TextView) convertView.findViewById(R.id.tv_title); TextView contentTextView = (TextView) convertView.findViewById(R.id.tv_content); //获取相应索引的ItemBean对象 ItemBean bean = mList.get(position); /** * 设置控件的对应属性值 */ imageView.setImageResource(bean.itemImageResId); titleTextView.setText(bean.itemTitle); contentTextView.setText(bean.itemContent); return convertView; }
此方式充分使用了ListView的缓存机制,若是view没有缓存才建立新的view,效率相比于逗比式提高了不少。可是,当ListView很复杂时,每次调用findViewById都会去遍历视图树,因此findViewById是很消耗时间的,咱们应该尽可能避免使用findViewById来达到进一步优化的目的。
第三种:文艺式
public View getView(int position, View convertView, ViewGroup parent) { ViewHolder viewHolder; //若是view未被实例化过,缓存池中没有对应的缓存 if (convertView == null) { viewHolder = new ViewHolder(); // 因为咱们只须要将XML转化为View,并不涉及到具体的布局,因此第二个参数一般设置为null convertView = mInflater.inflate(R.layout.item, null); //对viewHolder的属性进行赋值 viewHolder.imageView = (ImageView) convertView.findViewById(R.id.iv_image); viewHolder.title = (TextView) convertView.findViewById(R.id.tv_title); viewHolder.content = (TextView) convertView.findViewById(R.id.tv_content); //经过setTag将convertView与viewHolder关联 convertView.setTag(viewHolder); }else{//若是缓存池中有对应的view缓存,则直接经过getTag取出viewHolder viewHolder = (ViewHolder) convertView.getTag(); } // 取出bean对象 ItemBean bean = mList.get(position); // 设置控件的数据 viewHolder.imageView.setImageResource(bean.itemImageResId); viewHolder.title.setText(bean.itemTitle); viewHolder.content.setText(bean.itemContent); return convertView; } // ViewHolder用于缓存控件,三个属性分别对应item布局文件的三个控件 class ViewHolder{ public ImageView imageView; public TextView title; public TextView content; }
此方式不只利用了ListView的缓存机制,并且使用ViewHolder类来实现显示数据视图的缓存,避免屡次调用findViewById来寻找控件,以达到优化程序的目的。因此,你们在平时的开发中应当尽可能使用这种方式进行getView的实现。
总结一下用ViewHolder优化BaseAdapter的总体步骤:
>1 建立bean对象,用于封装数据;
>2 在构造方法中初始化的数据List;
>3 建立ViewHolder类,建立布局映射关系;
>4 判断convertView,为空则建立,并设置tag,不为空则经过tag取出ViewHolder;
>5 给ViewHolder的控件设置数据。