Android-新手引导层镂空裁剪层

前言

满足自定义蒙层颜色,裁剪区域尺寸,自定义说明布局位置,满足自己项目中所需即可,如下图展示:
在这里插入图片描述
实现原理
1、浮层的位置在activity的DecorView中,DecorView是FrameLayout的子类,所以整个蒙层,都是基于FrameLayout实现
代码如下:

		if (anchor == null) {
            anchor = activity.findViewById(android.R.id.content);
        }
        if (anchor instanceof FrameLayout) {
            mParentView = (FrameLayout) anchor;
        } else {
            FrameLayout frameLayout = new FrameLayout(activity);
            ViewGroup parent = (ViewGroup) anchor.getParent();
            indexOfChild = parent.indexOfChild(anchor);
            parent.removeView(anchor);
            if (indexOfChild >= 0) {
                parent.addView(frameLayout, indexOfChild, anchor.getLayoutParams());
            } else {
                parent.addView(frameLayout, anchor.getLayoutParams());
            }
            ViewGroup.LayoutParams lp = new ViewGroup.LayoutParams
                    (ViewGroup.LayoutParams.MATCH_PARENT, ViewGroup.LayoutParams.MATCH_PARENT);
            frameLayout.addView(anchor, lp);
            mParentView = frameLayout;
        }

2、高亮区域(待裁剪区域)
(1)、先测绘出需要被裁剪的区域:

	@Override
    public RectF getRectF(View parent) {
        if (view == null) {
            throw new IllegalArgumentException("the highlight view is null!");
        }

        if (rectF == null) {
            rectF = new RectF();
            Rect locationInView = ViewUtils.getLocationInView(parent, view);
            rectF.left = locationInView.left - padding;
            rectF.top = locationInView.top - padding;
            rectF.right = locationInView.right + padding;
            rectF.bottom = locationInView.bottom + padding;
        }
        return rectF;
    }

(2)、通过Paint完成绘制,主要通过setXfeemode实现
在这里插入图片描述
代码如下:

		mPaint = new Paint();
        mPaint.setAntiAlias(true);
        PorterDuffXfermode xfermode = new PorterDuffXfermode(PorterDuff.Mode.CLEAR);
        mPaint.setXfermode(xfermode);

        //设置画笔遮罩滤镜,可以传入BlurMaskFilter或EmbossMaskFilter,前者为模糊遮罩滤镜而后者为浮雕遮罩滤镜
        //这个方法已经被标注为过时的方法了,如果你的应用启用了硬件加速,你是看不到任何阴影效果的
        mPaint.setMaskFilter(new BlurMaskFilter(10, BlurMaskFilter.Blur.INNER));
        //关闭当前view的硬件加速
        setLayerType(LAYER_TYPE_SOFTWARE, null);

        //ViewGroup默认设定为true,会使onDraw方法不执行,如果复写了onDraw(Canvas)方法,需要清除此标记
        setWillNotDraw(false);

在onDraw方法中调用:

 	@Override
    protected void onDraw(Canvas canvas) {
        super.onDraw(canvas);
        int backgroundColor = guidePage.getBackgroundColor();
        canvas.drawColor(backgroundColor == 0 ? DEFAULT_BACKGROUND_COLOR : backgroundColor);
        drawHighlights(canvas);
    }

    private void drawHighlights(Canvas canvas) {
        List<HightLightInterface> highLights = guidePage.getHighLights();
        if (highLights != null) {
            for (HightLightInterface highLight : highLights) {
                RectF rectF = highLight.getRectF((ViewGroup) getParent());
                switch (highLight.getShape()) {
                    case CIRCLE:
                        canvas.drawCircle(rectF.centerX(), rectF.centerY(), highLight.getRadius(), mPaint);
                        break;
                    case OVAL:
                        canvas.drawOval(rectF, mPaint);
                        break;
                    case ROUND_RECTANGLE:
                        canvas.drawRoundRect(rectF, highLight.getRound(), highLight.getRound(), mPaint);
                        break;
                    case RECTANGLE:
                    default:
                        canvas.drawRect(rectF, mPaint);
                        break;
                }
            }
        }
    }

3、自定义对高亮区域的说明布局
ps:用注解的方式代替枚举,枚举占用的内存是静态的两倍

	@IntDef({Gravity.START, Gravity.TOP, Gravity.END, Gravity.BOTTOM})
    @Retention(RetentionPolicy.SOURCE)
    @interface LimitGravity {
    }

(1)、根据裁剪区域的rectF对说明布局进行位置调整

	public View getGuideLayout(ViewGroup viewGroup) {
        View view = LayoutInflater.from(viewGroup.getContext()).inflate(layout, viewGroup, false);
        RectF rectF = highLight.getRectF(viewGroup);
        FrameLayout.LayoutParams layoutParams = (FrameLayout.LayoutParams) view.getLayoutParams();
        switch (gravity) {
            case Gravity.START:
                layoutParams.leftMargin = 10;
                layoutParams.topMargin = (int) (rectF.top + rectF.height() / 2);
                layoutParams.rightMargin = (int) (rectF.right + 10);
                break;
            case Gravity.TOP:
                layoutParams.topMargin = (int) (rectF.top / 2);
                break;
            case Gravity.END:
                layoutParams.leftMargin = (int) (rectF.right + 10);
                layoutParams.topMargin = (int) (rectF.top + rectF.height() / 2);
                layoutParams.rightMargin = 10;

                break;
            case Gravity.BOTTOM:
                layoutParams.topMargin = (int) (rectF.bottom + 100);
                break;

        }

        view.setLayoutParams(layoutParams);
        return view;
    }

(2)、通过addView的方式添加到FrameLayout中

	public void addCustomToLayout(GuidePage guidePage) {
        RelativeGuide relativeGuide = guidePage.getRelativeGuide();
        addView(relativeGuide.getGuideLayout((ViewGroup) getParent()));

    }

Github地址:https://github.com/feifan784/StudyProject.git
PS:guide文件下,可进入首页,点击Guide按钮

总结

站在巨人的肩上看世界,去改变世界!
参考文献:https://www.jianshu.com/p/5aa96683d0dc/

  • 1
    点赞
  • 3
    收藏
    觉得还不错? 一键收藏
  • 7
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论 7
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值