Android设计模式之适配器(Adapter)模式

本文实例为大家分享了Android适配器模式源码,供大家参考,具体内容如下

1. 模式介绍

1.1模式的定义:

  适配器模式把一个类的接口变换成客户端所期待的另一种接口,从而使原本因接口不匹配而无法在一起工作的两个类能够在一起工作。

1.2模式的使用场景:

  用电源接口做例子,笔记本电脑的电源一般都是接受5V的电压,但是我们生活中的电线电压一般都是220V的输出。这个时候就出现了不匹配的状况,在软件开发中我们称之为接口不兼容,此时就需要适配器来进行一个接口转换。在软件开发中有一句话正好体现了这点:任何问题都可以加一个中间层来解决。这个层我们可以理解为这里的Adapter层,通过这层来进行一个接口转换就达到了兼容的目的。   

2.模式的简单实现

2.1简单实现的介绍:

  在上述电源接口这个示例中,5V电压就是Target接口,220v电压就是Adaptee类,而将电压从220V转换到5V就是Adapter。

2.2类适配器模式:

 /**
 * Target角色
 */
public interface FiveVolt {
  public int getVolt5();
}

/**
 * Adaptee角色,需要被转换的对象
 */
public class Volt220 {
  public int getVolt220() {
    return 220;
  }
}

// adapter角色
public class ClassAdapter extends Volt220 implements FiveVolt {

  @Override
  public int getVolt5() {
    return 5;
  }

}

Target角色给出了需要的目标接口,而Adaptee类则是需要被转换的对象。Adapter则是将Volt220转换成Target的接口。对应的是Target的目标是要获取5V的输出电压,而Adaptee即正常输出电压是220V,此时我们就需要电源适配器类将220V的电压转换为5V电压,解决接口不兼容的问题。

public class Test {
  public static void main(String[] args) {
    ClassAdapter adapter = new ClassAdapter();
    System.out.println("输出电压 : " + adapter.getVolt5());
  }
}

2.3.Android源码中的模式实现

与类的适配器模式一样,对象的适配器模式把被适配的类的API转换成为目标类的API,与类的适配器模式不同的是,对象的适配器模式不是使用继承关系连接到Adaptee类,而是使用代理关系连接到Adaptee类。

从图2可以看出,Adaptee类 ( Volt220 ) 并没有getVolt5()方法,而客户端则期待这个方法。为使客户端能够使用Adaptee类,需要提供一个包装类Adapter。这个包装类包装了一个Adaptee的实例,从而此包装类能够把Adaptee的API与Target类的API衔接起来。Adapter与Adaptee是委派关系,这决定了适配器模式是对象的。

/**
 * Target角色
 */
public interface FiveVolt {
  public int getVolt5();
}

/**
 * Adaptee角色,需要被转换的对象
 */
public class Volt220 {
  public int getVolt220() {
    return 220;
  }
}

// 对象适配器模式
public class ObjectAdapter implements FiveVolt {

  Volt220 mVolt220;

  public ObjectAdapter(Volt220 adaptee) {
    mVolt220 = adaptee;
  }

  public int getVolt220() {
    return mVolt220.getVolt220();
  }

  @Override
  public int getVolt5() {
    return 5;
  }

}

2.4.类适配器和对象适配器的权衡

*类适配器使用对象继承的方式,是静态的定义方式;而对象适配器使用对象组合的方式,是动态组合的方式。

*对于类适配器,由于适配器直接继承了Adaptee,使得适配器不能和Adaptee的子类一起工作,因为继承是静态的关系,当适配器继承了Adaptee后,就不可能再去处理Adaptee的子类了。对于对象适配器,一个适配器可以把多种不同的源适配到同一个目标。换言之,同一个适配器可以把源类和它的子类都适配到目标接口。因为对象适配器采用的是对象组合的关系,只要对象类型正确,是不是子类都无所谓。

*对于类适配器,适配器可以重定义Adaptee的部分行为,相当于子类覆盖父类的部分实现方法。对于对象适配器,要重定义Adaptee的行为比较困难,这种情况下,需要定义Adaptee的子类来实现重定义,然后让适配器组合子类。虽然重定义Adaptee的行为比较困难,但是想要增加一些新的行为则方便的很,而且新增加的行为可同时适用于所有的源。

*对于类适配器,仅仅引入了一个对象,并不需要额外的引用来间接得到Adaptee。对于对象适配器,需要额外的引用来间接得到Adaptee。

建议尽量使用对象适配器的实现方式,多用合成/聚合、少用继承。当然,具体问题具体分析,根据需要来选用实现方式,最适合的才是最好的。

3.Android ListView中的Adapter模式

在开发过程中,ListView的Adapter是我们最为常见的类型之一。一般的用法大致如下:

  // 适配器
public class MyAdapter extends BaseAdapter{

    private LayoutInflater mInflater;
    List<String> mDatas ;

    public MyAdapter(Context context, List<String> datas){
      this.mInflater = LayoutInflater.from(context);
      mDatas = datas ;
    }
    @Override
    public int getCount() {
      return mDatas.size();
    }

    @Override
    public String getItem(int pos) {
      return mDatas.get(pos);
    }

    @Override
    public long getItemId(int pos) {
      return pos;
    }

    // 解析、设置、缓存convertView以及相关内容
    @Override
    public View getView(int position, View convertView, ViewGroup parent) {
      ViewHolder holder = null;
      // Item View的复用
      if (convertView == null) {
        holder = new ViewHolder();
        convertView = mInflater.inflate(R.layout.my_listview_item, null);
        // 获取title
        holder.title = (TextView)convertView.findViewById(R.id.title);
        convertView.setTag(holder);
      } else {
        holder = (ViewHolder)convertView.getTag();
      }
      holder.title.setText(mDatas.get(position));
      return convertView;
    }

  }

这看起来似乎还挺麻烦的,看到这里我们不禁要问,ListView为什么要使用Adapter模式呢?
我们知道,作为最重要的View,ListView需要能够显示各式各样的视图,每个人需要的显示效果各不相同,显示的数据类型、数量等也千变万化。那么如何隔离这种变化尤为重要。

Android的做法是增加一个Adapter层来应对变化,将ListView需要的接口抽象到Adapter对象中,这样只要用户实现了Adapter的接口,ListView就可以按照用户设定的显示效果、数量、数据来显示特定的Item View。
通过代理数据集来告知ListView数据的个数( getCount函数 )以及每个数据的类型( getItem函数 ),最重要的是要解决Item View的输出。Item View千变万化,但终究它都是View类型,Adapter统一将Item View输出为View ( getView函数 ),这样就很好的应对了Item View的可变性。

那么ListView是如何通过Adapter模式 ( 不止Adapter模式 )来运作的呢 ?我们一起来看一看。
ListView继承自AbsListView,Adapter定义在AbsListView中,我们看一看这个类。

public abstract class AbsListView extends AdapterView<ListAdapter> implements TextWatcher,
    ViewTreeObserver.OnGlobalLayoutListener, Filter.FilterListener,
    ViewTreeObserver.OnTouchModeChangeListener,
    RemoteViewsAdapter.RemoteAdapterConnectionCallback {

  ListAdapter mAdapter ;

  // 关联到Window时调用的函数
  @Override
  protected void onAttachedToWindow() {
    super.onAttachedToWindow();
    // 代码省略
    // 给适配器注册一个观察者,该模式下一篇介绍。
    if (mAdapter != null && mDataSetObserver == null) {
      mDataSetObserver = new AdapterDataSetObserver();
      mAdapter.registerDataSetObserver(mDataSetObserver);

      // Data may have changed while we were detached. Refresh.
      mDataChanged = true;
      mOldItemCount = mItemCount
      // 获取Item的数量,调用的是mAdapter的getCount方法
      mItemCount = mAdapter.getCount();
    }
    mIsAttached = true;
  }

  /**
   * 子类需要覆写layoutChildren()函数来布局child view,也就是Item View
   */
  @Override
  protected void onLayout(boolean changed, int l, int t, int r, int b) {
    super.onLayout(changed, l, t, r, b);
    mInLayout = true;
    if (changed) {
      int childCount = getChildCount();
      for (int i = 0; i < childCount; i++) {
        getChildAt(i).forceLayout();
      }
      mRecycler.markChildrenDirty();
    }

    if (mFastScroller != null && mItemCount != mOldItemCount) {
      mFastScroller.onItemCountChanged(mOldItemCount, mItemCount);
    }
    // 布局Child View
    layoutChildren();
    mInLayout = false;

    mOverscrollMax = (b - t) / OVERSCROLL_LIMIT_DIVISOR;
  }

  // 获取一个Item View
  View obtainView(int position, boolean[] isScrap) {
    isScrap[0] = false;
    View scrapView;
    // 从缓存的Item View中获取,ListView的复用机制就在这里
    scrapView = mRecycler.getScrapView(position);

    View child;
    if (scrapView != null) {
      // 代码省略
      child = mAdapter.getView(position, scrapView, this);
      // 代码省略
    } else {
      child = mAdapter.getView(position, null, this);
      // 代码省略
    }

    return child;
  }
 }

AbsListView定义了集合视图的框架,比如Adapter模式的应用、复用Item View的逻辑、布局Item View的逻辑等。子类只需要覆写特定的方法即可实现集合视图的功能,例如ListView。

ListView中的相关方法。

  @Override
  protected void layoutChildren() {
    // 代码省略

    try {
      super.layoutChildren();
      invalidate();
      // 代码省略
      // 根据布局模式来布局Item View
      switch (mLayoutMode) {
      case LAYOUT_SET_SELECTION:
        if (newSel != null) {
          sel = fillFromSelection(newSel.getTop(), childrenTop, childrenBottom);
        } else {
          sel = fillFromMiddle(childrenTop, childrenBottom);
        }
        break;
      case LAYOUT_SYNC:
        sel = fillSpecific(mSyncPosition, mSpecificTop);
        break;
      case LAYOUT_FORCE_BOTTOM:
        sel = fillUp(mItemCount - 1, childrenBottom);
        adjustViewsUpOrDown();
        break;
      case LAYOUT_FORCE_TOP:
        mFirstPosition = 0;
        sel = fillFromTop(childrenTop);
        adjustViewsUpOrDown();
        break;
      case LAYOUT_SPECIFIC:
        sel = fillSpecific(reconcileSelectedPosition(), mSpecificTop);
        break;
      case LAYOUT_MOVE_SELECTION:
        sel = moveSelection(oldSel, newSel, delta, childrenTop, childrenBottom);
        break;
      default:
        // 代码省略
        break;
      }

  }

  // 从上到下填充Item View [ 只是其中一种填充方式 ]
  private View fillDown(int pos, int nextTop) {
    View selectedView = null;

    int end = (mBottom - mTop);
    if ((mGroupFlags & CLIP_TO_PADDING_MASK) == CLIP_TO_PADDING_MASK) {
      end -= mListPadding.bottom;
    }

    while (nextTop < end && pos < mItemCount) {
      // is this the selected item?
      boolean selected = pos == mSelectedPosition;
      View child = makeAndAddView(pos, nextTop, true, mListPadding.left, selected);

      nextTop = child.getBottom() + mDividerHeight;
      if (selected) {
        selectedView = child;
      }
      pos++;
    }

    return selectedView;
  }

  // 添加Item View
  private View makeAndAddView(int position, int y, boolean flow, int childrenLeft,
      boolean selected) {
    View child;

    // 代码省略
    // Make a new view for this position, or convert an unused view if possible
    child = obtainView(position, mIsScrap);

    // This needs to be positioned and measured
    setupChild(child, position, y, flow, childrenLeft, selected, mIsScrap[0]);

    return child;
  }

ListView覆写了AbsListView中的layoutChilden函数,在该函数中根据布局模式来布局Item View。Item View的个数、样式都通过Adapter对应的方法来获取,获取个数、Item View之后,将这些Item View布局到ListView对应的坐标上,再加上Item View的复用机制,整个ListView就基本运转起来了。

当然这里的Adapter并不是经典的适配器模式,但是却是对象适配器模式的优秀示例,也很好的体现了面向对象的一些基本原则。这里的Target角色和Adapter角色融合在一起,Adapter中的方法就是目标方法;而Adaptee角色就是ListView的数据集与Item View,Adapter代理数据集,从而获取到数据集的个数、元素。

通过增加Adapter一层来将Item View的操作抽象起来,ListView等集合视图通过Adapter对象获得Item的个数、数据元素、Item View等,从而达到适配各种数据、各种Item视图的效果。因为Item View和数据类型千变万化,Android的架构师们将这些变化的部分交给用户来处理,通过getCount、getItem、getView等几个方法抽象出来,也就是将Item View的构造过程交给用户来处理,灵活地运用了适配器模式,达到了无限适配、拥抱变化的目的。

4.杂谈

优点与缺点

优点

更好的复用性
  系统需要使用现有的类,而此类的接口不符合系统的需要。那么通过适配器模式就可以让这些功能得到更好的复用。

更好的扩展性
  在实现适配器功能的时候,可以调用自己开发的功能,从而自然地扩展系统的功能。

缺点

过多的使用适配器,会让系统非常零乱,不易整体进行把握。比如,明明看到调用的是A接口,其实内部被适配成了B接口的实现,一个系统如果太多出现这种情况,无异于一场灾难。因此如果不是很有必要,可以不使用适配器,而是直接对系统进行重构。

以上就是本文的全部内容,希望对大家的学习有所帮助,也希望大家多多支持我们。

(0)

相关推荐

  • Android设计模式系列之组合模式

    Android中对组合模式的应用,可谓是泛滥成粥,随处可见,那就是View和ViewGroup类的使用.在android UI设计,几乎所有的widget和布局类都依靠这两个类. 组合模式,Composite Pattern,是一个非常巧妙的模式.几乎所有的面向对象系统都应用到了组合模式. 1.意图 将对象View和ViewGroup组合成树形结构以表示"部分-整体"的层次结构(View可以做为ViewGroup的一部分). 组合模式使得用户对单个对象View和组合对象ViewGrou

  • Android中的设计模式

    一般来说,常用的设计模式有以下八种:单例.工厂.观察者.代理.命令.适配器.合成.访问者 单例模式:目的是为了让系统中只有一个调用对象,缺点是单例使其他程序过分依赖它,而且不同单例运行在不同进程中,使得维护困难: 工厂模式:生产固定的一些东西,如抽象类,缺点是产品修改麻烦:如喜欢动作片和爱情片的人分别向服务器发出同一个请求,就可以得到他们想看的影片集,相当于不同对象进行同一请求,需求均得到满足. 观察者模式:就是多个对象对一个对象进行监控,如缓存: 代理模式:自己的事交给别人去做,分别返回结果即

  • Android开发中的MVC设计模式浅析

    Android开发中的MVC设计模式的理解 1. Android系统中分层的理解: (1).在Android的软件开发工作中,应用程序的开发人员主要是应用Android Application Framework层封装好的Api进行快速开发. (2).在Android框架的四个层次中,下层为上层服务,上层需要下层的支持,上层需要调用下层的服务. (3).这种分层的方式带来极大的稳定性.灵活性和可扩展性,使得不同层的开发人员可以按照规范专心特定层的开发. (4). Android的官方建议应用程序

  • Android设计模式系列之工厂方法模式

    工厂方法模式,往往是设计模式初学者入门的模式,的确,有人称之为最为典型最具启发效果的模式. android中用到了太多的工厂类,其中有用工厂方法模式的,当然也有很多工厂并不是使用工厂方法模式的,只是工具管理类. 今天以ThreadFactory举例说明一下简单工厂模式和工厂方法模式. 工厂方法模式,Factory Method,简单的方式,不简单的应用. 1.意图 定义一个用于创建对象的接口,让子类决定实例化哪个类.工厂方式模式使一个类的实例化延迟到其子类. 热门词汇:虚构造器 延迟 创建对象

  • Android设计模式之Builder模式详解

    Builder模式使用链式结构创建复杂对象,将过程与结果分开,创建过程中可以自行组合. 使用场景 一个对象,不同组合,不同顺序生成不同的结果 优点:封装性更规范,程序调用不用关系内部细节,注重结果即可 缺点:如果builder对象过多,会加大内存消耗 public class TabInfoBean { private int count;//Tab的个数 必选 private int currentTab;//默认选中的tab 必选 private String[] tabText;//文字必

  • android开发设计模式之——单例模式详解

    单例模式是设计模式中最常见也最简单的一种设计模式,保证了在程序中只有一个实例存在并且能全局的访问到.比如在Android实际APP 开发中用到的 账号信息对象管理, 数据库对象(SQLiteOpenHelper)等都会用到单例模式.下面针对一些例子分析一下我们在开发过程中应用单例模式需要注意的点.  一.作用 单例模式(Singleton):保证一个类仅有一个实例,并提供一个访问它的全局访问点 二.适用场景 1. 应用中某个实例对象需要频繁的被访问. 2. 应用中每次启动只会存在一个实例.如账号

  • Android应用开发中控制反转IoC设计模式使用教程

    1.概述 首先我们来吹吹牛,什么叫IoC,控制反转(Inversion of Control,英文缩写为IoC),什么意思呢? 就是你一个类里面需要用到很多个成员变量,传统的写法,你要用这些成员变量,那么你就new 出来用呗~~ IoC的原则是:NO,我们不要new,这样耦合度太高:你配置个xml文件,里面标明哪个类,里面用了哪些成员变量,等待加载这个类的时候,我帮你注入(new)进去: 这样做有什么好处呢?  回答这个问题,刚好可以回答另一个问题,很多人问,项目分层开发是吧,分为控制层.业务层

  • android设计模式之单例模式详解

    这是我们最常见的一类模式,对这一类模式有一个通用的特点就是: 封装创建的方式和过程. 这里所谓封装就是隐藏的意思,对对象的创建方法和过程不可见,或者是虚拟的过程. 隐藏创建方式,就是如单例,工厂方法,隐藏创建过程则是指builder,原型,至于抽象工厂,我认为他包含了以上两种. 我们想想一个对象的创建有哪些步骤? 1.创建什么东西?--接口定义 2.谁创建?        --决策类or帮助类 3.如何创建?     --how,创建过程 4.什么时候创建?    --创建时机的触发 由此可知,

  • Android 单例模式 Singleton 简单实例设计模式解析

    单例模式 Singleton 简单实例设计模式解析 前言 今天我来全面总结一下Android开发中最常用的设计模式 - 单例模式. 关于设计模式的介绍,可以看下我之前写的:1分钟全面了解"设计模式" 目录 1. 引入 1.1 解决的是什么问题 之前说过,设计模式 = 某类特定问题的解决方案,那么单例模式是解决什么问题的解决方案呢? 含义:单例 =一个实例: 解决的问题:降低对象之间的耦合度 解决方法:单例模式,即实现一个类只有一个实例化对象,并提供一个全局访问点 1.2 实例引入 接下

  • 基于Android设计模式之--SDK源码之策略模式的详解

    策略模式其实特别简单(听到这句话,大家是不是心里一下子放松了?).比如排序,官方告诉大家我这里有一个排序的接口ISort的sort()方法,然后民间各尽其能,实现这个排序的方法:冒泡,快速,堆等等.这些方法就是"不同的策略".然后,某个模块下,需要一个排序方法,但是暂时不能指定具体的sort方法(出于扩展的考虑),就需要使用ISort接口了.最后,具体什么场景下,传入什么具体的sort方法,实现灵活的排序.这就是策略模式!下面,我们分析Android中的动画是如何使用策略模式的. 1.

随机推荐