轻松搞懂自定义蒙层引导原理

蒙层引导在我们项目中一直的做法都是让UI直接切一整张静态图,这样的做法虽然省事,但带来的后果就是适配性太差,还会出现引导图和下面真正的界面不符的情况,让用户感到莫名其妙。因此,就有必要自定义一个蒙层引导视图来解决这个问题。本篇文章主要是对核心原理实现的剖析。

核心原理分析

自定义引导视图(GuideView)其实最主要的是需要解决三个问题:

  1. 引导视图应该是按需加载,在需要展示时浮在整个页面上,展示完毕后从页面视图中移除

  2. 确定需要引导的视图位置,并将之高亮

  3. 支持自定义提示视图,并根据高亮视图的位置摆放

对于第一个问题,很容易想到GuideView应该继承自帧布局,这样可以保证浮在整个页面上。按需加载的话,可以在需要展示引导时,添加到页面的根视图(即DecorView)上,展示完毕后再从根视图中移除即可解决。并且为了让蒙层布满全屏,应该在添加时,指定宽高为Decorview的宽高。

对于第二个问题,高亮视图的位置,可以通过系统提供的获取屏幕位置的API来解决

public static RectF getRectOnScreen(View view) {    if (view == null) {        return new RectF();    }    RectF result = new RectF();    int[] pos    = new int[2];    view.getLocationOnScreen(pos);    result.left = pos[0];    result.top = pos[1];    result.right = result.left + view.getMeasuredWidth();    result.bottom = result.top + view.getMeasuredHeight();    return result;}

获取到高亮位置后,下一步就是如何高亮?通常的做法就是将高亮位置从整个屏幕当中挖出来,改变屏幕其它位置的背景。

如下图所示:

《轻松搞懂自定义蒙层引导原理》 image.png

要实现这样的效果,就需要知道画布裁剪的知识。

裁剪共分为:裁剪路径、裁剪矩形、裁剪区域。裁剪后,只能编辑该区域,其它的区域并没有消失!

这里,我们可以选择裁剪路径。因为一般的引导库为了能够更好地引导用户,会在高亮区周围会绘制一些内容。

而路径(path)很好地封装了由直线和曲线构的几何图形,如添加圆形、矩形、圆角矩形、椭圆形甚至一些复杂图形。因此在确定好高亮区位置后,我们也可以根据位置信息通过path完成一些图片的绘制,如圆形:

mPath.addCircle(mDrawRect.centerX(), mDrawRect.centerY(), radius, Path.Direction.CW);

mDrawRect就是高亮区的位置。注意:这里只是在path中描述了图形的轮廓。

继续说裁剪路径。裁剪路径的API有以下两个。

// 方法1public boolean clipPath(@NonNull Path path)// 方法2public boolean clipPath(@NonNull Path path, @NonNull Region.Op op)

方法1默认调用了方法2,只是第二个参数传的值是 Region.Op.INTERSECT

/** * Intersect the current clip with the specified path. * * @param path The path to intersect with the current clip * @return     true if the resulting clip is non-empty */public boolean clipPath(@NonNull Path path) {    return clipPath(path, Region.Op.INTERSECT);}

下面一张图解释下Region.Op 这个参数。它的作用就是在裁剪下多个区域时,当这些区域有重叠的时候,决定重叠部分该如何处理,多次裁剪之后究竟获得了哪个区域。

《轻松搞懂自定义蒙层引导原理》 image.png

这里我们需要的是先裁剪出高亮区之外的区域,因为要绘制蒙层背景,但不能绘制到高亮区。所以选择的参数应该是Region.Op.DIFFERENCE。

裁剪后,绘制需要的背景。

绘制完背景后,如果有需要,还可通过画布可以将上面的path中描述的图形再绘制出来。

@Overrideprotected void onDraw(Canvas canvas) {    super.onDraw(canvas);    //以下为伪代码    //第一步:裁剪非高亮区    canvas.clipPath(guideShape.getShapePath(), Region.Op.DIFFERENCE);    //第二步:绘制蒙层背景    canvas.drawColor(mBgColor);    //第三步:如果需要,绘制高亮区内容    if (mEnableDrawShape) {         guideShape.onDraw(canvas);    }}

注意:ViewGroup默认是不绘制的,因此这里要绘制的话,需要将开关打开。

//需要重写onDraw 设置为falsesetWillNotDraw(false);

第三个问题,外部可以传入布局Id,也可以直接传入View,然后将提示视图添加到自定义的GuideView中。如果传入的是ID,我们内部可以使用布局解析器解析出来,然后再添加。

添加之后,如何摆放呢?这里我们可以通过Margin和Gravity来确定。

具体规则如下:

《轻松搞懂自定义蒙层引导原理》 image.png
《轻松搞懂自定义蒙层引导原理》 image.png
《轻松搞懂自定义蒙层引导原理》 image.png

到此,一个引导库的核心原理已经全部分析完毕。

其它功能分析

1. 多个引导分步骤如何实现?

可以先定义一个Java Bean对象,用来封装需要的一些参数,如需要引导的视图,提示视图等。然后用集合来保存,每次从集合中取第一个元素中的提示视图,添加到GuideView中,每次添加时,GuideView都要移除所有子视图,保证每次只显示一个引导。同时,添加完毕后,从集合中移除这个元素。这里应该能够想到集合应该用个队列来实现,Java的集合体系中,支持队列的有Deque接口,所有这里可以使用ArrayDeque或者LinkedList。

2. 如何控制引导的显示次数?

这个很容易想到用SP来控制。每次往SP中存取次数,默认只允许一次。当然还可以设定次数上限,或者永久显示用于调试。

【附】相关架构及资料

《轻松搞懂自定义蒙层引导原理》 image

资料领取

点赞+加群免费获取 Android IOC架构设计

加群领取获取往期Android高级架构资料、源码、笔记、视频。高级UI、性能优化、架构师课程、NDK、混合式开发(ReactNative+Weex)微信小程序、Flutter全方面的Android进阶实践技术,群内还有技术大牛一起讨论交流解决问题。

    原文作者:Android高级架构师
    原文地址: https://www.jianshu.com/p/ed53e3d39fff
    本文转自网络文章,转载此文章仅为分享知识,如有侵权,请联系博主进行删除。
点赞