详解Android ViewCompat的作用

所属分类: 软件编程 / Android 阅读数: 57
收藏 0 赞 0 分享

详解Android ViewCompat的作用

ViewCompat类主要是用来提供兼容性的, 比如我最近看的比较的多的canScrollVertically方法, 在ViewCompat里面针对几个版本有不同的实现, 原理上还是根据版本判断, 有时甚至还要判断传入参数的类型. 但是要注意的是, ViewCompat仅仅让你调用不崩溃, 并不保证你调用的结果在不同版本的机器上一致.

关于如何优雅的组织代码, ViewCompat类的结构非常适合我们参考.

ViewCompat里面定义了一个接口, 这个接口列出了所有它支持的方法

interface ViewCompatImpl {
    public boolean canScrollHorizontally(View v, int direction);
    public boolean canScrollVertically(View v, int direction);
    public int getOverScrollMode(View v);
    public void setOverScrollMode(View v, int mode);
    ......
}

ViewCompat类并非是在方法层面进行版本判断然后调用不同的方法, 而是在类的层面上做的, 也就是说在调用方法时并没有判断版本的调用, 因为一台手机的版本在开机到关机期间是不可能发生变化的, 所以只需要判断一次, 而这次判断放在了类的静态初始化块里.

static final ViewCompatImpl IMPL;
  static {
    final int version = android.os.Build.VERSION.SDK_INT;
    if (version >= 21) {
      IMPL = new LollipopViewCompatImpl();
    } else if (version >= 19) {
      IMPL = new KitKatViewCompatImpl();
    } else if (version >= 17) {
      IMPL = new JbMr1ViewCompatImpl();
    } else if (version >= 16) {
      IMPL = new JBViewCompatImpl();
    } else if (version >= 14) {
      IMPL = new ICSViewCompatImpl();
    } else if (version >= 11) {
      IMPL = new HCViewCompatImpl();
    } else if (version >= 9) {
      IMPL = new GBViewCompatImpl();
    } else if (version >= 7) {
      IMPL = new EclairMr1ViewCompatImpl();
    } else {
      IMPL = new BaseViewCompatImpl();
    }
  }

这样我们就得到了针对各个版本的不同实现.

但是有些方法的实现在跨越几个版本的时候是不变的, 有些方法又有可能每次都变, 如何实现高效的代码复用呢? 那就是继承+重写.

比如BaseViewCompatImpl这个类是基类, 实现ViewCompatImpl接口, 把所有的方法都实现一次

static class BaseViewCompatImpl implements ViewCompatImpl {
    ......
    public boolean canScrollHorizontally(View v, int direction) {
      return (v instanceof ScrollingView) &&
        canScrollingViewScrollHorizontally((ScrollingView) v, direction);
    }
    public boolean canScrollVertically(View v, int direction) {
      return (v instanceof ScrollingView) &&
          canScrollingViewScrollVertically((ScrollingView) v, direction);
    }
    ......
    @Override
    public boolean isOpaque(View view) {
      final Drawable bg = view.getBackground();
      if (bg != null) {
        return bg.getOpacity() == PixelFormat.OPAQUE;
      }
      return false;
    }
    ......
    }

但是这些实现基本上都是空的, 或者无效的, 或者是一些workaround, 这也很正常, 因为确实不可能让每个方法都做到兼容, 只能尽量让他的版本支持多一点, 兼容性方法本来就有很多问题. 以上面这三个方法为例, 前两个方法都是api 14出现的方法, 在14以下基本上等于是直接返回了false(这里低版本是仅对ScollingView提供了支持, ScollingView有三个基类, 其中一个是RecyclerView), google显然没有想到什么好的方法在低版本提供对这个方法的支持, 所以干脆就在api小于14时一直使用这个实现, 而isOpaque则是类似workaround的方法, 在api 7时, isOpaque被正式添加到View类中, 所以在api 7我们可以直接调View的isOpaque, 那么应该怎么写代码呢? 应当新建一个类, 继承BaseViewCompatImpl, 重写isOpaque方法, 也就是下面这样:

static class EclairMr1ViewCompatImpl extends BaseViewCompatImpl {
    @Override
    public boolean isOpaque(View view) {
      return ViewCompatEclairMr1.isOpaque(view);
    }

    ......
  }

而其他没有更好兼容方案的方法我们都不管, 那么api 9如果某些方法又有了更好的实现, 或者可以直接调用系统的api了, 就再新建一个类GBViewCompatImpl, 这个类需要继承EclairMr1ViewCompatImpl.

同理, 我们在api 14对应的类ICSViewCompatImpl中自然就会看到canScrollHorizontally和canScrollVertically的新的实现, 而ICSViewCompatImpl必然继承自HCViewCompatImpl.

就这样慢慢的演化, 像串铜钱一样, 每一个新的类对应一个新的版本(版本之间不需要连续), 同时继承自前一个版本的类, 在实现类的继承树上越接近叶子, 这个实现类的能力就越强.

最后看一下我们在代码里面使用这个类时的调用代码, 比如我要调用canScrollVertically方法, 那么我的代码一定是ViewCompat. canScrollVertically(v, dy), 看看这个方法对应的代码

public static boolean canScrollHorizontally(View v, int direction) {
    return IMPL.canScrollHorizontally(v, direction);
} 

ViewCompat相当于是一个中介, 它自己其实什么都不懂, 但是它认识一个懂的人IMPL, 它将所有的调用都交给了IMPL, 而IMPL在ViewCompat这个类加载时就已经根据当前系统版本实例化了, 不需要再判断版本了.

关于具体的使用请查看 官方文档

感谢阅读,希望能帮助到大家,谢谢大家对本站的支持!

更多精彩内容其他人还在看

Android样式和主题之选择器的实例讲解

今天小编就为大家分享一篇关于Android样式和主题之选择器的实例讲解,小编觉得内容挺不错的,现在分享给大家,具有很好的参考价值,需要的朋友一起跟随小编来看看吧
收藏 0 赞 0 分享

Android应用动态修改主题的方法示例

今天小编就为大家分享一篇关于Android应用动态修改主题的方法示例,小编觉得内容挺不错的,现在分享给大家,具有很好的参考价值,需要的朋友一起跟随小编来看看吧
收藏 0 赞 0 分享

Flutter 网络请求框架封装详解

这篇文章主要介绍了Flutter 网络请求框架封装详解,小编觉得挺不错的,现在分享给大家,也给大家做个参考。一起跟随小编过来看看吧
收藏 0 赞 0 分享

Flutter倒计时/计时器的实现代码

这篇文章主要介绍了Flutter倒计时/计时器的实现代码,文中通过示例代码介绍的非常详细,对大家的学习或者工作具有一定的参考学习价值,需要的朋友们下面随着小编来一起学习学习吧
收藏 0 赞 0 分享

Android使用google breakpad捕获分析native cash

这篇文章主要介绍了Android使用google breakpad捕获分析native cash 的相关知识,非常不错,具有一定的参考借鉴价值,需要的朋友可以参考下
收藏 0 赞 0 分享

详解Flutter WebView与JS互相调用简易指南

这篇文章主要介绍了详解Flutter WebView与JS互相调用简易指南,分为JS调用Flutter和Flutter调用JS,小编觉得挺不错的,现在分享给大家,也给大家做个参考。一起跟随小编过来看看吧
收藏 0 赞 0 分享

Android开发实现ListView和adapter配合显示图片和文字列表功能示例

这篇文章主要介绍了Android开发实现ListView和adapter配合显示图片和文字列表功能,涉及Android使用ListView结合adapter适配器实现图文显示功能相关的布局、解析、权限控制等操作技巧,需要的朋友可以参考下
收藏 0 赞 0 分享

Android文字基线Baseline算法的使用讲解

今天小编就为大家分享一篇关于Android文字基线Baseline算法的使用讲解,小编觉得内容挺不错的,现在分享给大家,具有很好的参考价值,需要的朋友一起跟随小编来看看吧
收藏 0 赞 0 分享

Flutter自定义实现神奇动效的卡片切换视图的示例代码

这篇文章主要介绍了Flutter自定义实现神奇动效的卡片切换视图的示例代码,小编觉得挺不错的,现在分享给大家,也给大家做个参考。一起跟随小编过来看看吧
收藏 0 赞 0 分享

Android实现自定义滑动刻度尺方法示例

这篇文章主要给大家介绍了关于Android实现自定义滑动刻度尺的相关资料,文中通过示例代码介绍的非常详细,对各位Android开发者们具有一定的参考学习价值,需要的朋友们下面来一起学习学习吧
收藏 0 赞 0 分享
查看更多