什么是数据适配器?

下图展示了数据源、适配器、ListView等数据展示控件之间的关系。我们知道,数据源是各种各样的,而ListView所展示数据的格式则是有一定的要求的。数据适配器正是建立了数据源与ListView之间的适配关系,将数据源转换为ListView能够显示的数据格式,从而将数据的来源与数据的显示进行解耦,降低程序的耦合性。这也体现了Android的适配器模式的使用。对于ListView、GridView等数据展示控件有多种数据适配器,本文讲解最通用的数据适配器——BaseAdapter。

.ListView的显示与缓存机制

我们知道,ListView、GridView等控件可以展示大量的数据信息。假如下图中的ListView可以展示100条信息,但是屏幕的尺寸是有限的,一屏幕只能显示下图中的7条。当向上滑动ListView的时候,item1被滑出了屏幕区域,那么系统就会将item1回收到Recycler中,即View缓冲池中,而将要显示的item8则会从缓存池中取出布局文件,并重新设置好item8需要显示的数据,并放入需要显示的位置。这就是ListView的缓冲机制,总结起来就是一句话:需要时才显示,显示完就被会收到缓存。ListView,GridView等数据显示控件通过这种缓存机制可以极大的节省系统资源。

.BaseAdapter

使用BaseAdapter比较简单,主要是通过继承此类来实现BaseAdapter的四个方法:

public int getCount(): 适配器中数据集的数据个数;

public Object getItem(int position): 获取数据集中与索引对应的数据项;

public long getItemId(int position): 获取指定行对应的ID;

public View getView(int position,View convertView,ViewGroup parent): 获取没一行Item的显示内容。

下面通过一个简单示例演示如何使用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的控件设置数据。

Android必学之数据适配器BaseAdapter的更多相关文章

  1. Python爬虫工程师必学——App数据抓取实战 ✌✌

    Python爬虫工程师必学——App数据抓取实战 (一个人学习或许会很枯燥,但是寻找更多志同道合的朋友一起,学习将会变得更加有意义✌✌) 爬虫分为几大方向,WEB网页数据抓取.APP数据抓取.软件系统 ...

  2. Android必学-异步加载+Android自定义View源码【申明:来源于网络】

    Android必学-异步加载+Android自定义View源码[申明:来源于网络] 异步加载地址:http://download.csdn.net/detail/u013792369/8867609 ...

  3. Python爬虫工程师必学APP数据抓取实战✍✍✍

    Python爬虫工程师必学APP数据抓取实战  整个课程都看完了,这个课程的分享可以往下看,下面有链接,之前做java开发也做了一些年头,也分享下自己看这个视频的感受,单论单个知识点课程本身没问题,大 ...

  4. Python爬虫工程师必学——App数据抓取实战

    Python爬虫工程师必学 App数据抓取实战 整个课程都看完了,这个课程的分享可以往下看,下面有链接,之前做java开发也做了一些年头,也分享下自己看这个视频的感受,单论单个知识点课程本身没问题,大 ...

  5. Android学习之Adapter(数据适配器)

    1.定义     数据适配器是AdapterView视图(如ListView - 列表视图控件.Gallery - 缩略图浏览器控件.GridView - 网格控件.Spinner - 下拉列表控件. ...

  6. Android必学——AsyncTask

    第一章  AsyncTask的基本构成 为是么要异步任务 1)Android单线程模型 2)耗时操作放在非主线程中执行 AsyncTask为何而生 1)子线程中跟新UI 2)封装.简化异步操作 pub ...

  7. Android必学之AsyncTask

    AsyncTask,即异步任务,是Android给我们提供的一个处理异步任务的类.通过此类,可以实现UI线程和后台线程进行通讯,后台线程执行异步任务,并把结果返回给UI线程. .为什么需要使用异步任务 ...

  8. android中的万能适配器BaseAdapter的总结

    有时候,列表不光会用来做显示用,我们同样可以在在上面添加按钮.添加按钮首先要写一个有按钮的xml文件,然后自然会想到用上面的方法定义一个适配器,然后将数据映射到布局文件上.但是事实并非这样,因为按钮是 ...

  9. 无废话Android之listview入门,自定义的数据适配器、采用layoutInflater打气筒创建一个view对象、常用数据适配器ArrayAdapter、SimpleAdapter、使用ContentProvider(内容提供者)共享数据、短信的备份、插入一条记录到系统短信应用(3)

    1.listview入门,自定义的数据适配器 <RelativeLayout xmlns:android="http://schemas.android.com/apk/res/and ...

随机推荐

  1. BZOJ2209 [Jsoi2011]括号序列 splay

    原文链接http://www.cnblogs.com/zhouzhendong/p/8093556.html 题目传送门 - BZOJ2209 题解 我太弱了,调出这题感觉都要吐了. 题解懒得写了. ...

  2. fastadmin系统配置

    常规管理--->系统配置--->字典配置-->配置分组-->追加--填上键值-->回车 然后在点上图的+添加自定义的配置项(如果需要删除配置项,需要删除数据库中fa_co ...

  3. 如何让自己的Dev C++用上C++11,c++14标准

      首先确保Dev C++版本是最新的5.11版 其实用C++11和C++14标准的语法去运行还是会出现结果的,最多warning一下 但完美主义者是不允许这样的 我们可以点击菜单栏的“工具”-> ...

  4. HDU 4185 Oil Skimming 【最大匹配】

    <题目链接> 题目大意: 给你一张图,图中有 '*' , '.' 两点,现在每次覆盖相邻的两个 '#' ,问最多能够覆盖几次. 解题分析: 无向图二分匹配的模板题,每个'#'点与周围四个方 ...

  5. UVA 796 Critical Links(模板题)(无向图求桥)

    <题目链接> 题目大意: 无向连通图求桥,并将桥按顺序输出. 解题分析: 无向图求桥的模板题,下面用了kuangbin的模板. #include <cstdio> #inclu ...

  6. CSS-样式篇

    2文本: 1文本缩进:text-indent:理论上只有块级元素才可以设置(百分比是相对于父元素的宽度,负值的话要设置内边距进行抵消,防止超过浏览器边界),行内元素可以通过内边距和外边距来达到同样的效 ...

  7. Saltstack报错小记

    这是之前的一篇文章,由于有小伙伴也遇到同样的错误,就拿出来分享下吧 [root@master ~]# salt 'minion.saltstack.com' state.sls init.pkg[ER ...

  8. BZOJ.2679.Balanced Cow Subsets(meet in the middle)

    BZOJ 洛谷 \(Description\) 给定\(n\)个数\(A_i\).求它有多少个子集,满足能被划分为两个和相等的集合. \(n\leq 20,1\leq A_i\leq10^8\). \ ...

  9. 英语口语练习系列-C13-聚会

    词汇 音频 1. apartment [əˈpɑ:tmənt] n. 公寓 a big / small apartment 一个大的/小的公寓 in an apartment 在公寓 2. arm [ ...

  10. MySql基础笔记(二)Mysql语句优化---索引

    Mysql语句优化--索引 一.开始优化前的准备 一)explain语句 当MySql要执行一个查询语句的时候,它首先会对语句进行语法检查,然后生成一个QEP(Query Execution Plan ...