进阶实战:基于android_maskable_layout 源码扩展自定义遮罩ViewGroup的完整指南
进阶实战:基于android_maskable_layout 源码扩展自定义遮罩ViewGroup的完整指南
【免费下载链接】android_maskable_layoutA library that easily allows you to mask layouts/viewgroups项目地址: https://gitcode.com/gh_mirrors/an/android_maskable_layout
android_maskable_layout 是一款轻量级的 Android 遮罩布局开源库,它允许开发者通过简单的 XML 属性或一行代码,为任意 View 和 ViewGroup 添加圆形、星形甚至动画遮罩效果。本文将从源码角度剖析其核心实现,并给出 3 个可落地的扩展方案,帮助你从「会用」进阶到「会改」,打造属于自己的自定义遮罩 ViewGroup。无论你是刚接触 Android 自定义 View 的新手,还是想提升 UI 表现力的普通开发者,这份实战指南都能让你快速上手。
android_maskable_layout 是什么:一个给 ViewGroup 加遮罩的轻量级库
简单来说,android_maskable_layout只做了一件事:把任意 ViewGroup 变成一块「画布」,再用一张遮罩图(Drawable)去裁剪它的子 View 显示区域。它的核心类MaskableFrameLayout继承自系统FrameLayout,因此任何原本能放进 FrameLayout 的布局和控件,都能直接获得遮罩能力。
它的典型使用方式是在 XML 中声明,几乎零代码侵入:
<com.christophesmet.android.views.maskableframelayout.MaskableFrameLayout android:layout_width="100dp" android:layout_height="100dp" app:mask="@drawable/masking_masking_20" app:porterduffxfermode="DST_IN"> <ImageView android:layout_width="match_parent" android:layout_height="match_parent" android:scaleType="centerCrop" android:src="@drawable/unicorn"/> </com.christophesmet.android.views.maskableframelayout.MaskableFrameLayout>对比上面的演示动图就能直观看到:同样一张独角兽图片,无遮罩时是矩形,加上圆形遮罩就变圆,加上星形遮罩就变星形,甚至还能加载 60 帧的逐帧动画遮罩。
核心原理速览:MaskableFrameLayout 源码到底做了什么
扩展自定义遮罩 ViewGroup 之前,先花 3 分钟看懂源码。整个库的核心只有两个文件:
- 核心类:
library/src/main/java/com/christophesmet/android/views/maskableframelayout/MaskableFrameLayout.java - 自定义属性:
library/src/main/res/values/attrs_maskable_framelayout.xml
第一步:把遮罩 Drawable 转成 Bitmap
遮罩本质上是一张与 View 同尺寸的位图。在makeBitmapMask()方法中,库会把传入的 Drawable 绘制到一个ARGB_8888的 Bitmap 上。关键前提是 View 必须先完成测量(宽高大于 0),所以库通过ViewTreeObserver注册了全局布局监听,等尺寸确定后再生成遮罩。
第二步:用 PorterDuff 混合模式完成裁剪
遮罩的显示发生在重写dispatchDraw(Canvas)中(MaskableFrameLayout.java第 207-216 行):先绘制所有子 View,再把遮罩 Bitmap 以指定的PorterDuffXfermode混合上去。默认使用DST_IN,即「保留子 View 中与遮罩重叠的部分」,从而实现形状裁剪。
第三步:通过三个自定义属性控制行为
属性文件定义了mask(遮罩 Drawable)、porterduffxfermode(17 种混合模式枚举)和anti_aliasing(抗锯齿开关),全部通过TypedArray在构造时读取。
扩展前的准备工作:下载源码与工程结构
首先获取源码:
git clone https://gitcode.com/gh_mirrors/an/android_maskable_layout工程采用多模块结构,settings.gradle中声明了library和Sample两个模块,结构清晰:
android_maskable_layout/ ├── library/ # 核心库模块(AAR) │ └── src/main/java/com/christophesmet/android/views/maskableframelayout/ │ └── MaskableFrameLayout.java # 全部核心逻辑 └── Sample/ # 演示工程 ├── MainActivity.java # 动画遮罩触发逻辑 └── res/drawable/animation_mask.xml # 60帧逐帧动画遮罩建议先运行 Sample 模块查看效果,再进入源码研读,效率更高。
三个实战扩展方向:打造你的自定义遮罩 ViewGroup
扩展一:运行时动态更换遮罩形状
原库的 XML 配置是静态的,但setMask()方法支持运行时热更新。你可以继承MaskableFrameLayout并封装自己的 API,比如点击按钮在圆形、星形之间切换:
// 子类中提供公开方法 public void applyCircleMask() { setMask(R.drawable.masking_masking_01); // 圆形遮罩 } public void applyStarMask() { setMask(R.drawable.masking_masking_20); // 星形遮罩 }注意setMask()内部会自动重建 Bitmap 并调用invalidate(),无需手动刷新。
扩展二:让遮罩动起来——AnimationDrawable 动画遮罩
这是本库最亮眼的特性:遮罩本身可以是一段逐帧动画。Sample 中的animation_mask.xml是一个包含 60 帧 PNG 的animation-list(每帧 18ms),源码通过setCallback(this)注册为 View 的 Callback,并重写invalidateDrawable()、scheduleDrawable()驱动动画刷新。
在你的自定义遮罩 ViewGroup 中,可以像 Sample 的MainActivity一样主动触发动画:
Drawable drawable = getDrawableMask(); if (drawable instanceof AnimationDrawable) { AnimationDrawable anim = (AnimationDrawable) drawable; anim.stop(); anim.start(); // 遮罩形状开始逐帧变化 }基于这个思路,你甚至可以用Animator驱动形状渐变,实现「呼吸」或「波纹」等更复杂的动效。
扩展三:借助 PorterDuff 模式玩出更多视觉效果
除了裁剪用的DST_IN,库还支持 17 种混合模式(attrs_maskable_framelayout.xml中已定义枚举)。在扩展时调用setPorterDuffXferMode()即可随时切换:
setPorterDuffXferMode(PorterDuff.Mode.DST_OUT); // 挖空效果 setPorterDuffXferMode(PorterDuff.Mode.MULTIPLY); // 正片叠底 setPorterDuffXferMode(PorterDuff.Mode.SCREEN); // 滤色提亮比如用DST_OUT实现圆形挖孔,配合底层阴影即可做出「异形头像卡片」等高级 UI。
自定义遮罩 ViewGroup 时的三个避坑要点
坑一:必须使用软件层绘制。源码构造方法中调用了setLayerType(LAYER_TYPE_SOFTWARE, null),因为PorterDuffXfermode依赖软件渲染。如果你的自定义 ViewGroup 关闭了软件层,遮罩会直接失效——这是最常见的「为什么没效果」原因。
坑二:宽高为 0 时遮罩静默失败。makeBitmapMask()在宽高不大于 0 时只打日志并返回 null。所以避免在wrap_content且无子 View 的情况下使用,并留意日志中的Can't create a mask提示。
坑三:遮罩 Bitmap 会频繁重建,注意内存回收。源码在onSizeChanged时重建遮罩,并在swapBitmapMask()中回收旧 Bitmap。扩展时若自行持有遮罩引用,务必按相同方式管理,防止内存抖动。
写在最后
从「改一行属性」到「读懂核心源码」,android_maskable_layout 是一个非常适合练习自定义 View 的小而美项目。掌握了它的 Bitmap 遮罩 + PorterDuff 混合模式这两大核心思想,你就拥有了扩展出任意形状、任意动画、任意混合效果的自定义遮罩 ViewGroup 的能力。希望这份完整指南能帮你少踩坑、快上手,写出更精致的 Android 界面。
【免费下载链接】android_maskable_layoutA library that easily allows you to mask layouts/viewgroups项目地址: https://gitcode.com/gh_mirrors/an/android_maskable_layout
创作声明:本文部分内容由AI辅助生成(AIGC),仅供参考
