详解Android ViewCompat的作用

详解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这个类加载时就已经根据当前系统版本实例化了, 不需要再判断版本了.

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

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

时间: 2024-09-20 01:00:25

详解Android ViewCompat的作用的相关文章

基于 SurfaceView 详解 android 幸运大转盘,附带实例app

基于 SurfaceView 详解 android 幸运大转盘,附带实例app       首先说一下,幸运大转盘,以及SurfaceView是在看了也为大神的博客,才有了比较深刻的理解,当然这里附上这位大神的博客地址:博客地址,有兴趣的话你可以去看看,里面有很多的例子.至于我为什么要写这篇博客?,原因之一:加强自己的理解,原因之二:大神的博客就是大神的博客,跳转的太快,基础不好的,很难理解.还有就是一天在实验室太无聊了,没事写写东西.这里我再来更加基础的分析一下.写的不好,原谅.有什么写的不对

详解Android中Intent对象与Intent Filter过滤匹配过程_Android

如果对Intent不是特别了解,可以参见博文<详解Android中Intent的使用方法>,该文对本文要使用的action.category以及data都进行了详细介绍.如果想了解在开发中常见Intent的使用,可以参见<Android中Intent习惯用法>. 本文内容有点长,希望大家可以耐心读完. 本文在描述组件在manifest中注册的Intent Filter过滤器时,统一用intent-filter表示. 一.概述 我们知道,Intent是分两种的:显式Intent和隐式

详解Android主流框架不可或缺的基石

探索Android软键盘的疑难杂症 深入探讨Android异步精髓Handler 详解Android主流框架不可或缺的基石 站在源码的肩膀上全解Scroller工作机制 Android多分辨率适配框架(1)- 核心基础 Android多分辨率适配框架(2)- 原理剖析 Android多分辨率适配框架(3)- 使用指南 自定义View系列教程00–推翻自己和过往,重学自定义View 自定义View系列教程01–常用工具介绍 自定义View系列教程02–onMeasure源码详尽分析 自定义View

实例详解Android Selector和Shape的用法_Android

shape和selector是Android UI设计中经常用到的,比如我们要自定义一个圆角Button,点击Button有些效果的变化,就要用到shape和selector.可以这样说,shape和selector在美化控件中的作用是至关重要的. 1:Selector drawable的item中可以有以下属性: android:drawable="@[package:]drawable/drawable_resource" android:state_pressed=["

详解Android更改APP语言模式的实现过程_Android

一.效果图 二.描述 更改Android项目中的语言,这个作用于只用于此APP,不会作用于整个系统 三.解决方案 (一)布局文件 <LinearLayout xmlns:android="http://schemas.android.com/apk/res/android" xmlns:tools="http://schemas.android.com/tools" android:layout_width="match_parent" a

实例详解Android Selector和Shape的用法

shape和selector是Android UI设计中经常用到的,比如我们要自定义一个圆角Button,点击Button有些效果的变化,就要用到shape和selector.可以这样说,shape和selector在美化控件中的作用是至关重要的. 1:Selector drawable的item中可以有以下属性: android:drawable="@[package:]drawable/drawable_resource" android:state_pressed=["

详解Android中Handler的内部实现原理_Android

本文主要是对Handler和消息循环的实现原理进行源码分析,如果不熟悉Handler可以参见博文<详解Android中Handler的使用方法>,里面对Android为何以引入Handler机制以及如何使用Handler做了讲解. 概括来说,Handler是Android中引入的一种让开发者参与处理线程中消息循环的机制.我们在使用Handler的时候与Message打交道最多,Message是Hanlder机制向开发人员暴露出来的相关类,可以通过Message类完成大部分操作Handler的功

实例详解Android文件存储数据方式_Android

总体的来讲,数据存储方式有三种:一个是文件,一个是数据库,另一个则是网络.下面通过本文给大家介绍Android文件存储数据方式. 1.文件存储数据使用了Java中的IO操作来进行文件的保存和读取,只不过Android在Context类中封装好了输入流和输出流的获取方法. 创建的存储文件保存在/data/data/<package name>/files文件夹下. 2.操作. 保存文件内容:通过Context.openFileOutput获取输出流,参数分别为文件名和存储模式. 读取文件内容:通

详解Android 目录结构

详解Android 目录结构 工欲善其事,必先利其器.在开发Android项目之前,让我们先对Android的目录结构有一个清楚的认识. 在Eclipse中新建一个Android Project,名称为"AndroidTest",则项目结构如图: 1.src目录 文件夹下放置项目的所有包及源文件(.java). 2.gen目录 文件夹中包含了一个R.java类文件,通过R.java类中的注释可以看出,R.java文件是由aapt工具根据项目中的资源文件来自动生成的.R.java文件是项