【Android适配器系列】BaseAdapter学习笔记



慕客网-Android必学-BaseAdapter的使用与优化-学习笔记


什么是数据适配器

数据适配器是数据源与视图(View)之间的桥梁,建立了两者之间的适配关系。数据的来源是各种各样的,但View能显示的格式却是有一定要求的,数据适配器是把各种各样的数据源转化成为View能显示的数据格式。

优点:

将数据的来源与数据的显示进行了解耦,降低程序的耦合性,提高可扩展性。

BaseAdapter是Android各种各样适配器里最通用的适配器。

BaseAdapter基本结构

  • public int getCount() :适配器中 数据集中 数据的个数,即ListView需要显示的数据个数
  • public Object getItem(int position) : 获取数据集中与指定索引对应的数据项
  • public long getItemId(int position) : 获取指定行对应的ID
  • public View getView(int position, View convertView, ViewGroup parent) :获取每一个Item的显示内容

在使用BaseAdapter时,我们需要自己创建一个类继承BaseAdapter,然后Eclipse会提醒我们实现上述四个方法,当给ListView设置了我们自己写的Adapter后,ListView内部会调用上述四个方法。


BaseAdapter的逗比式

这是BaseAdapter使用的基本形式,下面是实现步骤:

  • 新建Android工程 MyBaseAdapter,准备界面布局

    修改activity_main,将TextView改为ListView,如下:

<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"
    android:paddingBottom="@dimen/activity_vertical_margin"
    android:paddingLeft="@dimen/activity_horizontal_margin"
    android:paddingRight="@dimen/activity_horizontal_margin"
    android:paddingTop="@dimen/activity_vertical_margin"
    tools:context="com.mybaseadapter.MainActivity" >

    <ListView
        android:id="@+id/lv_main"
        android:layout_width="wrap_content"
        android:layout_height="wrap_content"
        android:text="@string/hello_world" />

</RelativeLayout>

创建单条数据项的Layout布局:item.xml

<?xml version="1.0" encoding="utf-8"?>
<RelativeLayout xmlns:android="http://schemas.android.com/apk/res/android"
    android:layout_width="match_parent"
    android:layout_height="wrap_content"
    android:orientation="vertical" >

    <ImageView
        android:id="@+id/iv_image"
        android:layout_width="64dp"
        android:layout_height="64dp"
        android:src="@drawable/ic_launcher"
       />

    <TextView
        android:id="@+id/tv_title"
        android:layout_width="match_parent"
        android:layout_height="30dp"
        android:layout_toRightOf="@id/iv_image"
        android:text="Title"
        android:gravity="center"
        android:textSize="25sp"
/>

     <TextView
        android:id="@+id/tv_content"
        android:layout_width="match_parent"
        android:layout_height="30dp"
        android:layout_toRightOf="@id/iv_image"
        android:layout_below="@id/tv_title"
        android:text="Content"
        android:gravity="center_vertical"
        android:textSize="20sp"
/>
</RelativeLayout>
  • 创建Bean对象,里面有三个属性与上面的Item相对应,里面封装的是单条的数据
package com.mybaseadapter;

public class ItemBean {
 public int itemImageResid;
 public String itemTitle;
 public String itemContent;

 public ItemBean(int itemImageResid, String itemTitle, String itemContent) {
  super();
  this.itemImageResid = itemImageResid;
  this.itemTitle = itemTitle;
  this.itemContent = itemContent;
 }
}
  • 创建MyAdapter继承BaseAdapter,实现我们需要实现的四个方法
package com.mybaseadapter;

import java.util.List;

import android.content.Context;
import android.view.LayoutInflater;
import android.view.View;
import android.view.ViewGroup;
import android.widget.BaseAdapter;
import android.widget.ImageView;
import android.widget.TextView;

public class MyAdapter extends BaseAdapter{

 private List<ItemBean> mList;
 private LayoutInflater mInflater;

 /**
  * 关联数据源与数据适配器
  * @param list
  */
 public MyAdapter(Context context, List<ItemBean> list){
  mList = list;
  mInflater = LayoutInflater.from(context);
 }

 @Override
 public int getCount() {
  // TODO Auto-generated method stub
  return mList.size();
 }

 @Override
 public Object getItem(int position) {
  // TODO Auto-generated method stub
  return mList.get(position);
 }

 @Override
 public long getItemId(int position) {
  // TODO Auto-generated method stub
  return position;//对应的索引项
 }

 @Override
 public View getView(int position, View convertView, ViewGroup parent) {
  // TODO Auto-generated method stub
  // 返回每一项的显示内容

  /*********  逗比式 **************/
  /**
   * 无视convertView和parent,没有考虑ListView的感受,没有使用缓存机制
   * 每次都是创建一个新的View, 并在View中找到相应的控件去设置相应的值,
   * 完全没用到缓存机制,对资源造成了极大浪费
   */
  //创建View
  View view = this.mInflater.inflate(R.layout.item, null);
  ImageView image = (ImageView) view.findViewById(R.id.iv_image);
  TextView title = (TextView) view.findViewById(R.id.tv_title);
  TextView content = (TextView) view.findViewById(R.id.tv_title);
  //关联数据
  image.setImageResource(mList.get(position).itemImageResid);
  title.setText(mList.get(position).itemTitle);
  content.setText(mList.get(position).itemContent);
  return view;
 }

}
  • 创建测试数据,用适配器连接ListView和测试数据
package com.mybaseadapter;

import java.util.ArrayList;
import java.util.List;

import android.os.Bundle;
import android.support.v7.app.ActionBarActivity;
import android.widget.ListView;

public class MainActivity extends ActionBarActivity {

    @Override
    protected void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        setContentView(R.layout.activity_main);
        //创建测试数据
        List<ItemBean> itemBeanList = new ArrayList<ItemBean>();
        for(int i = 0; i < 20; i++ ){
         itemBeanList.add(new ItemBean(
           R.drawable.ic_launcher,
           "标题 " + i,
           "内容 " + i
         ));
        }

        ListView lv = (ListView) findViewById(R.id.lv_main);
        MyAdapter adapter = new MyAdapter(this, itemBeanList);
        lv.setAdapter(adapter);
    }
}
  • 总结

    之所以称此种模式是逗比式,是因为在MyAdapter里,每次都是创建一个新的View, 并在View中找到相应的控件去设置相应的值,完全没用到ListView的缓存机制,造成了资源的极大浪费。


BaseAdapter的普通式

普通式是逗比式的升级版,升级的地方就是在MyAdapter的getView里,逗比式里没有使用到ListView的缓存机制,在普通式里这个缺点会被弥补。

下面是普通式的getView方法代码:

public View getView(int position, View convertView, ViewGroup parent) {
/**********普通方法***************/
  if (convertView == null){ //判断convertView是否已被缓存,通过这个判断就避免创建大量的View,从而大大地节省了资源
   convertView = this.mInflater.inflate(R.layout.item, null);
  }
  ImageView image = (ImageView) convertView.findViewById(R.id.iv_image);
  TextView title = (TextView) convertView.findViewById(R.id.tv_title);
  TextView content = (TextView) convertView.findViewById(R.id.tv_title);
  //关联数据
  image.setImageResource(mList.get(position).itemImageResid);
  title.setText(mList.get(position).itemTitle);
  content.setText(mList.get(position).itemContent);

  return convertView;
  /********************************/

 }

普通式与逗比式的差别在于普通式使用的View是系统缓存的convertView,通过判断可以避免创建大量的View,从而大量的节省了资源。

但findViewById仍然会浪费大量的时间,这也是为什么这个方法只能称为普通式的原因。


BaseAdapter的文艺式

在getView里面的两个耗时:

- 创建大量的View

- findViewById查找控件

我们通过普通式解决了第一个耗时操作,下面就开始着手解决第二个耗时操作。

这个方法由2013年Google开发者大会上的某个开发者提出,具体过程分为:

1. 创建ViewHolder内部类,里面包含ListView单行界面布局的所有控件变量;

2. 在getView中先准备变量viewHolder,然后判断convertView是否为空,为空则转第3步,否则转第4步;

3. 使用Inflater创建convertView,实例化viewHolder,使用findViewById实例化viewHolder里的三个控件,接下来,就是最重要的,使用setTag方法将convertView与viewHolder关联起来(具体看代码),因为convertView会被系统缓存,所以同时ViewHolder也被缓存;

4. 使用getTag方法取出viewHolder,然后用数据填充viewHolder里的控件,而不用再先使用findViewById查找控件后赋值,从而节省了大量的时间。

下面是MyAdapter类getView方法文艺式实现的代码:

public View getView(int position, View convertView, ViewGroup parent) {
/*************文艺式***************/
  ViewHolder viewHolder = null;
  if (convertView == null){
   convertView = this.mInflater.inflate(R.layout.item, null);
   viewHolder = new ViewHolder();
   viewHolder.image = (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);
   //将ViewHolder与convertView进行关联
   convertView.setTag(viewHolder);
  }else{
   viewHolder = (ViewHolder) convertView.getTag();
  }
  viewHolder.image.setImageResource(mList.get(position).itemImageResid);
  viewHolder.title.setText(mList.get(position).itemTitle);
  viewHolder.content.setText(mList.get(position).itemContent);
  return convertView;
  /*********************************/

 }

 class ViewHolder{
  public ImageView image;
  public TextView  title;
  public TextView  content;
 }

通过这种做法,就避免了大量的findViewById,从而大大地节省了时间。


总结

逗比式、普通式、文艺式是循序渐进逐步优化的,我在其中可以学到了我们应该对控件的内部机制何相关设计模式有一定了解,这样才能理解那些API为什么要这样设计,这样才能做到灵活运用。另外,有时候也可尝试一下另辟蹊径,文艺式里使用setTag来缓存ViewHolder就属于此。

版权声明:本文为博主原创文章,未经博主允许不得转载。

时间: 2024-07-31 00:55:06

【Android适配器系列】BaseAdapter学习笔记的相关文章

Android(java)学习笔记205:网易新闻客户端应用编写逻辑过程

1.我们的项目需求是编写一个新闻RSS浏览器,RSS(Really Simple Syndication)是一种描述和同步网站内容的格式,是使用最广泛的XML应用.RSS目前广泛用于网上新闻频道,blog和wiki,主要的版本有0.91, 1.0, 2.0.使用RSS订阅能更快地获取信息,网站提供RSS输出,有利于让用户获取网站内容的最新更新.网络用户可以在客户端借助于支持RSS的聚合工具软件,在不打开网站内容页面的情况下阅读支持RSS输出的网站内容. 例如如下的网易RSS订阅: 2.由于我们这

C#程序员学习Android开发系列之学习路线图

通过前面的3篇博客已经简单的介绍了Android开发的过程并写了一个简单的demo,了解了Android开发的环境以及一些背景知识. 接下来这篇博客不打算继续学习Android开发的细节,先停一下,明确一下接下来的学习目标以及学习路线. 一.对Android开发的基本认识 1.Android原生开发是基于Java语言的,由于我比较擅长C#,所以对Java语言本身不太熟练,需要加强Java语言基础的练习,这一块我会穿插到具体的知识点练习当中,并且在必要的地方给出与C#语言的对比(其实基本上在语法层

[转载]Android Bitmap和Canvas学习笔记

http://blog.chinaunix.net/uid-20771867-id-3053339.html [转载]Android Bitmap和Canvas学习笔记,布布扣,bubuko.com

Android(java)学习笔记167:Java中操作文件的类介绍

1.File类:对硬盘上的文件和目录进行操作的类.    File类是文件和目录路径名抽象表现形式  构造函数:        1) File(String pathname)       Creates a new File instance by converting the given pathname string into an abstract pathname. 2)File(File parent, String child)       Creates a new File i

Android(java)学习笔记204:自定义SmartImageView(继承自ImageView,扩展功能为自动获取网络路径图片)

1.有时候Android系统配置的UI控件,不能满足我们的需求,Android开发做到了一定程度,多少都会用到自定义控件,一方面是更加灵活,另一方面在大数据量的情况下自定义控件的效率比写布局文件更高. 2.下面我们是自定义一个SmartImageView继承自ImageView,扩展了ImageView的功能:     步骤: • 新建一个SmartImageView类,让继承自ImageView(放置特定的包下): • 实现SmartImageView类下的构造方法,最好全部实现,这个不容易出

Android(java)学习笔记227:服务(service)之服务的生命周期(service)

1.之前我们在Android(java)学习笔记171:Service生命周期 (2015-08-18 10:56)说明过,可以回头看看: 2.接下来就简单概括性描述一下: (1)start的方式开启服务,服务会执行onCreate方法 (2)如果服务已经被创建,就不会再去执行onStartCommand()  (onStart过时) (3)停止服务,服务会执行onDestory(): (4)如果服务已经停止,多次调用stopService()无效的

Android(java)学习笔记207:开源项目使用之gif view

1. 由于android没有自带的gif动画,我在Android(java)学习笔记198:Android下的帧动画(Drawable Animation) 播客中提到可以使用AnimationView(帧动画)方法先将一个gif动画利用软件分割成若干静态图片,然后按照一定的时间间隔和播放顺序一帧一帧播放图片,从而给用户体验仿佛是动画gif播放的效果 2.其实网上的开源项目中有更为完善的gif动画播放的代码,下面以此为例进一步了解开源项目的使用: (1)在https://github.com/网

Android(java)学习笔记233: 远程服务的应用场景(移动支付案例)

一. 移动支付:       用户需要在移动终端提交账号.密码以及金额等数据 到 远端服务器.然后远端服务器匹配这些信息,进行逻辑判断,进而完成交易,返回交易成功或失败的信息给移动终端.用户提交账号.密码以及金额等数据都是比较敏感的数据,这些数据不能让外界获取.       阿里等等支付宝平台把支付的逻辑封装起来,只给我们提供一个方法去调用,这样提高了安全性.当我们用户提交账号.密码以及金额等数据,点击"支付"的时候,支付宝平台已经调用方法加密数据(这个支付逻辑是远程服务,为了安全,防

Android(java)学习笔记160:Framework运行环境之启动Zygote

前面Android(java)学习笔记159提到Dalvik虚拟机启动初始化过程,就下来就是启动zygote进程: zygote进程是所有APK应用进程的父进程:每当执行一个Android应用程序,Zygote就会孵化一个子线程去执行该应用程序(系统内部执行dvz指令完成的)  Å特别注意:系统提供了一个app_process进程,它会自动启动ZygoteInit.java和SystemServer.java这两个类,app_process进程本质上是使用dalvikvm启动ZygoteInit

ios/mac/COCOA系列 -- UIALertVIew 学习笔记

最近在学习ios开发,学习的书籍<ios7 Pragramming cookbook>,做笔记的目的以后方便查看.笔记形式是小例子,将书上的例子书写完整. UIAlertViewClass 的使用场景 1,向用户以一个警告的形式显示信息. 2,让用户确认一些动作 3,让用户输入用户名和密码 4,让用户输入一些文本,这些文本在程序被使用 例1   实现简单显示一些警告信息 新建一个 Single View Application 简单工程,工程名字维AlterView,扩展前缀CB   代码如下