尧图网站设计 尧图网站设计YAOTU DESIGN
ARTICLE DETAIL

资讯详情

深耕网站设计与一线实操的经验洞察。

深入源码:android_maskable_layout 如何用dispatchDraw与Bitmap实现布局遮罩?

深入源码:android_maskable_layout 如何用dispatchDraw与Bitmap实现布局遮罩? 深入源码android_maskable_layout 如何用dispatchDraw与Bitmap实现布局遮罩【免费下载链接】android_maskable_layoutA library that easily allows you to mask layouts/viewgroups项目地址: https://gitcode.com/gh_mirrors/an/android_maskable_layout想把 Android 布局裁剪成圆形头像、星形徽章、气泡形状……android_maskable_layout就是为此而生的轻量级开源库。它提供的MaskableFrameLayout容器只需几行 XML 配置就能给任意子 View 加上遮罩效果还支持逐帧动画遮罩和多达 18 种 PorterDuff 混合模式。本文将从零拆解它用 dispatchDraw 与 Bitmap 实现布局遮罩的完整原理并给出可直接照抄的接入步骤全程尽量少贴代码让你像读故事一样看懂源码。遮罩原理总览一次绘制里的二次加工先建立一个直觉在 Android 中给布局加遮罩本质上就是先正常画出子 View再在它上面叠加一张镂空的遮罩图。MaskableFrameLayout的聪明之处在于它没有去拦截每个子 View 的绘制而是把遮罩 Drawable 预先渲染成一张和布局等大的 Bitmap然后在dispatchDraw这个画完子 View的钩子里用PorterDuffXfermode混合模式把这张 Bitmap 合成上去。整个过程只有两样东西一张 Bitmap、一个绘制回调。构造方法里的三处关键伏笔在MaskableFrameLayout的构造方法construct()中埋下了三个决定成败的设置setLayerType(LAYER_TYPE_SOFTWARE, null)强制使用软件绘制层。因为 PorterDuff 混合模式在硬件加速层上很多不生效这是源码注释里明确写的Only works for software layers。创建 Paint为后续绘制遮罩准备画笔并支持可选的反锯齿开关anti_aliasing。读取自定义属性从MaskableLayout样式声明中读取mask遮罩资源、porterduffxfermode混合模式、anti_aliasing三个属性。属性定义位于library/src/main/res/values/attrs_maskable_framelayout.xml。核心步骤一MaskableFrameLayout 如何把 Drawable 转成 Bitmap遮罩必须是一张和布局等宽高的位图。这一步由makeBitmapMask()完成先Bitmap.createBitmap(width, height, ARGB_8888)创建透明位图再把 Drawable 的边界设为整个布局区域最后用 Canvas 把 Drawable 画进这张 Bitmap。核心代码只有四行Bitmap mask Bitmap.createBitmap(w, h, Bitmap.Config.ARGB_8888); Canvas canvas new Canvas(mask); drawable.setBounds(0, 0, w, h); drawable.draw(canvas);注意如果布局还没有测量完成宽或高为 0这里会直接返回 null 并打印日志所以生成 Bitmap 的时机非常讲究见下文的何时重建遮罩。核心步骤二dispatchDraw 里的临门一脚这是整个库的灵魂。dispatchDraw是 ViewGroup 分发绘制子 View 的回调源码在library/src/main/java/com/christophesmet/android/views/maskableframelayout/MaskableFrameLayout.java中完整逻辑只有短短几行Override protected void dispatchDraw(Canvas canvas) { super.dispatchDraw(canvas); // 1. 正常绘制所有子 View if (mFinalMask ! null mPaint ! null) { mPaint.setXfermode(mPorterDuffXferMode); // 2. 挂上混合模式 canvas.drawBitmap(mFinalMask, 0f, 0f, mPaint); // 3. 绘制遮罩 Bitmap mPaint.setXfermode(null); // 4. 用完即卸防止污染后续绘制 } }拆开看就四步先super.dispatchDraw(canvas)让子 View 正常上屏然后给画笔设置PorterDuffXfermode默认DST_IN即目标像素 源遮罩与目标内容相交的部分从而把内容抠成遮罩的形状接着把预先做好的遮罩 Bitmap 画上去最后立刻把 Xfermode 置空避免影响之后的其他绘制。这样一个先画内容、再画遮罩的顺序就完成了布局遮罩效果。何时重建遮罩onSizeChanged 与布局监听遮罩 Bitmap 依赖布局尺寸所以尺寸一变就得重做。源码里通过两处兜底onSizeChanged()当布局宽高改变时自动调用setSize()重建遮罩 Bitmap。registerMeasure()布局刚 inflate 出来时宽高还是 0所以用ViewTreeObserver.addOnGlobalLayoutListener()等第一次布局完成后再生成遮罩并在回调里及时移除监听避免重复执行。这两个机制保证了遮罩永远和布局一样大、一样新。进阶玩法AnimationDrawable 逐帧动画遮罩这个库最酷的特性是支持动画遮罩。它在initMask()里检测到遮罩是AnimationDrawable时会把自己注册为 Drawable 的 Callback并重写了invalidateDrawable()、scheduleDrawable()、unscheduleDrawable()三个回调——每当动画帧切换时就用新的一帧重建 Bitmap 并invalidate()触发重绘。Sample 工程里的animation_mask.xml就是一个包含 60 帧的动画列表配合MainActivity里的press me按钮即可播放出遮罩形状连续变化的炫酷效果。18 种 PorterDuff 混合模式怎么选源码里用getModeFromInteger()把porterduffxfermode属性值映射到 Android 原生PorterDuff.Mode共支持 18 种ADD、CLEAR、DARKEN、DST、DST_ATOP、DST_IN、DST_OUT、DST_OVER、LIGHTEN、MULTIPLY、OVERLAY、SCREEN、SRC、SRC_ATOP、SRC_IN、SRC_OUT、SRC_OVER、XOR。日常遮罩需求选DST_IN就够了想做颜色叠加、变亮变暗等滤镜效果可以尝试MULTIPLY、SCREEN、DARKEN等玩法很多。快速上手XML 配置三步走第一步在布局里用MaskableFrameLayout包住目标 View并指定遮罩资源和混合模式com.christophesmet.android.views.maskableframelayout.MaskableFrameLayout android:layout_width100dp android:layout_height100dp app:maskdrawable/masking_masking_01 app:porterduffxfermodeDST_IN ImageView android:layout_widthmatch_parent android:layout_heightmatch_parent android:scaleTypecenterCrop android:srcdrawable/unicorn/ /com.christophesmet.android.views.maskableframelayout.MaskableFrameLayout第二步在代码中也可以动态更换遮罩调用setMask(drawableRes)或setMask(drawable)配合setPorterDuffXferMode()随时切换混合模式。第三步如果遮罩是AnimationDrawable像MainActivity那样start()即可播放动画。完整示例布局见Sample/src/main/res/layout/activity_main.xml。源码文件导航核心实现MaskableFrameLayout.java自定义属性library/src/main/res/values/attrs_maskable_framelayout.xml示例页面Sample/src/main/java/com/christophesmet/android/views/MainActivity.java动画遮罩资源Sample/src/main/res/drawable/animation_mask.xml想亲手运行 Sample 工程看效果可以 clone 仓库https://gitcode.com/gh_mirrors/an/android_maskable_layout。总结android_maskable_layout用不到 400 行代码就把布局遮罩这件看似复杂的事讲得清清楚楚先把遮罩 Drawable 渲染成 Bitmap再在 dispatchDraw 里用 PorterDuffXfermode 完成合成。理解了这个套路你甚至可以自己动手写一个圆角布局、气泡布局或者异形控件。如果你也想让 Android 布局拥有任意形状不妨现在就试试它吧【免费下载链接】android_maskable_layoutA library that easily allows you to mask layouts/viewgroups项目地址: https://gitcode.com/gh_mirrors/an/android_maskable_layout创作声明:本文部分内容由AI辅助生成(AIGC),仅供参考
返回列表