详解Android ViewCompat的作用
发布时间 - 2026-01-11 02:10:45 点击率:次详解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
# ViewCompat
# ViewCompat的作用
# Android RecyclerView显示Item布局不一致解决办法
# Android绘制验证码的实例代码
# Android用Scroller实现一个可向上滑动的底部导航栏
# Android 解决WebView无法上传文件的问题
# Android动画之小球拟合动画实例
# Android文件下载功能实现代码
# Android上使用grpc的方法教程
# Android中应用前后台切换监听的实现详解
# 都是
# 几个
# 是在
# 又有
# 重写
# 使用这个
# 新建一个
# 的人
# 的是
# 是一个
# 就会
# 我要
# 让你
# 放在
# 就在
# 出了
# 不需要
# 有很多
# 则是
# 不懂
相关栏目:
【
网站优化151355 】
【
网络推广146373 】
【
网络技术251813 】
【
AI营销90571 】
相关推荐:
linux top下的 minerd 木马清除方法
北京网页设计制作网站有哪些,继续教育自动播放怎么设置?
Laravel如何设置自定义的日志文件名_Laravel根据日期或用户ID生成动态日志【技巧】
Laravel如何创建自定义Artisan命令?(代码示例)
免费视频制作网站,更新又快又好的免费电影网站?
Win11怎样安装网易有道词典_Win11安装词典教程【步骤】
海南网站制作公司有哪些,海口网是哪家的?
Laravel如何实现登录错误次数限制_Laravel自带LoginThrottles限流配置【方法】
桂林网站制作公司有哪些,桂林马拉松怎么报名?
如何在宝塔面板中创建新站点?
如何自定义safari浏览器工具栏?个性化设置safari浏览器界面教程【技巧】
Edge浏览器如何截图和滚动截图_微软Edge网页捕获功能使用教程【技巧】
Laravel如何使用Service Provider服务提供者_Laravel依赖注入与容器绑定【深度】
DeepSeek是免费使用的吗 DeepSeek收费模式与Pro版本功能详解
常州企业网站制作公司,全国继续教育网怎么登录?
佛山网站制作系统,佛山企业变更地址网上办理步骤?
用v-html解决Vue.js渲染中html标签不被解析的问题
高端企业智能建站程序:SEO优化与响应式模板定制开发
Laravel如何处理跨站请求伪造(CSRF)保护_Laravel表单安全机制与令牌校验
如何在 Go 中优雅地映射具有动态字段的 JSON 对象到结构体
悟空识字如何进行跟读录音_悟空识字开启麦克风权限与录音
Laravel Eloquent模型如何创建_Laravel ORM基础之Model创建与使用教程
Laravel如何实现用户密码重置功能?(完整流程代码)
html5怎么画眼睛_HT5用Canvas或SVG画眼球瞳孔加JS控制动态【绘制】
php后缀怎么变mp4格式错误_修改扩展名提示格式不对怎么办【技巧】
laravel怎么在请求结束后执行任务(Terminable Middleware)_laravel Terminable Middleware请求结束任务执行方法
西安专业网站制作公司有哪些,陕西省建行官方网站?
Python高阶函数应用_函数作为参数说明【指导】
香港服务器租用费用高吗?如何避免常见误区?
如何在IIS服务器上快速部署高效网站?
如何用JavaScript实现文本编辑器_光标和选区怎么处理
Zeus浏览器网页版官网入口 宙斯浏览器官网在线通道
Windows Hello人脸识别突然无法使用
Laravel如何处理JSON字段_Eloquent原生JSON字段类型操作教程
Laravel如何处理异常和错误?(Handler示例)
1688铺货到淘宝怎么操作 1688一键铺货到自己店铺详细步骤
Laravel如何实现密码重置功能_Laravel密码找回与重置流程
Python文本处理实践_日志清洗解析【指导】
夸克浏览器网页跳转延迟怎么办 夸克浏览器跳转优化
实例解析angularjs的filter过滤器
如何在Ubuntu系统下快速搭建WordPress个人网站?
Laravel如何使用Seeder填充数据_Laravel模型工厂Factory批量生成测试数据【方法】
如何用景安虚拟主机手机版绑定域名建站?
Laravel怎么返回JSON格式数据_Laravel API资源Response响应格式化【技巧】
北京网站制作的公司有哪些,北京白云观官方网站?
手机网站制作与建设方案,手机网站如何建设?
Laravel如何获取当前登录用户信息_Laravel Auth门面使用与Session用户读取【技巧】
如何快速搭建个人网站并优化SEO?
javascript中的try catch异常捕获机制用法分析
黑客入侵网站服务器的常见手法有哪些?

