美文网首页
Android高亮引导实现 HiGuide

Android高亮引导实现 HiGuide

作者: zii4914 | 来源:发表于2018-05-05 19:05 被阅读491次

    前言

    Github上有不少高亮引导的库,比如下面的。但是觉得他们的功能太多,不够简单,跟项目需求不太符合。原本打算直接使用HightLight(效果比较符合需求),但是发现issue上有不少bitmap的oom异常,以及停止更新维护了。所以自己尝试实现一下。

    思路

    看了好几篇文章,大体思路都是在DecorView上添加布局,在遮盖Activity的布局。HiGuide也不例外。

    1. 首先两个最关键的参数,Activity,需要高亮的View。Activity用来获取DecorView,高亮View用来计算高亮位置及区域。
    2. 获取Activity的DecorView,并把遮盖层布局GuideView添加进去,就可以实现遮挡效果。
    3. 在遮盖层GuideView(FrameLayout)内,根据高亮View在屏幕的位置(View.getLocationOnSrceen()),及高亮View的宽高,及配置的形状等参数,使用使用Path进行计算(Path.op()可以进行多个path进行层叠区域计算,跟Paint的Xfermode一样),得到镂空的遮盖区域,在画布上绘制该Path就可以得到有高亮区域的遮盖层。
    4. 想要添加其他的提示布局,Textview或Imageview,或者其他Layout容器,在GuideView内add就可以了,注意配置边距参数以定位。
    5. 点击效果通常分两块,一块是背景层,也就是整个遮盖层,另外一个就是高亮区域。高亮区域,可以在使用View在屏幕的位置,及宽高,就可以创建一个RectF矩形区域,把所有的高亮View的RectF用List保存起来。在GuideView的onTouch中,计算,如果点击的坐标xy在某个高亮RectF矩形内,即为点击了高亮区域。

    关键代码

    • 添加遮盖层
    mRootView = (ViewGroup) ((Activity) mContext).getWindow().getDecorView();
    if (mRootView instanceof FrameLayout) {
          ViewGroup.LayoutParams lp = new ViewGroup.LayoutParams(ViewGroup.LayoutParams.MATCH_PARENT,
              ViewGroup.LayoutParams.MATCH_PARENT);
          mRootView.addView(mGuideView, mRootView.getChildCount(), lp);
        } else {
          FrameLayout frameLayout = new FrameLayout(mContext);
          ViewGroup parent = (ViewGroup) mRootView.getParent();
          parent.removeView(mRootView);
          parent.addView(frameLayout, mRootView.getLayoutParams());
          ViewGroup.LayoutParams lp = new ViewGroup.LayoutParams(
              ViewGroup.LayoutParams.MATCH_PARENT, ViewGroup.LayoutParams.MATCH_PARENT);
          frameLayout.addView(mRootView, lp);
        } 
    
    • 移除遮盖层
    private void remove() {
        ViewGroup parent = (ViewGroup) getParent();
        if (parent instanceof RelativeLayout || parent instanceof FrameLayout) {
          parent.removeView(this);
        } else {
          parent.removeView(this);
          View origin = parent.getChildAt(0);
          ViewGroup graParent = (ViewGroup) parent.getParent();
          graParent.removeView(parent);
          graParent.addView(origin, parent.getLayoutParams());
        }
    
        if (mRemoveCallback != null) {
          mRemoveCallback.callback();
        }
      }
    
    • 高亮区域绘制
     @Override
      protected void onDraw(Canvas canvas) {
        mBgPath.reset();
        mShapePath.reset();
    
        mBgPath.addRect(0, 0, getWidth(), getHeight(), Path.Direction.CW);
    
        for (HightLight hightLight : mOverlay.getHightLightList()) {
          Path shapePath = calcHightLightShapePath(hightLight, mShapePath);
          if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.KITKAT) {
            mBgPath.op(shapePath, Path.Op.XOR);
          }
        }
    
        canvas.drawPath(mBgPath, mPaint);
      }
    
    • 高亮形状计算
    private Path calcHightLightShapePath(HightLight hightLight, Path shapePath) {
        shapePath.reset();
        switch (hightLight.getShape()) {
          case HiGuide.SHAPE_CIRCLE:
            shapePath.addCircle(hightLight.getRectF().centerX(),
                hightLight.getRectF().centerY(),
                hightLight.getRadius(), Path.Direction.CW);
            break;
          case HiGuide.SHAPE_OVAL:
            shapePath.addOval(hightLight.getRectF(), Path.Direction.CW);
            break;
          case HiGuide.SHAPE_RECT:
            shapePath.addRect(hightLight.getRectF(), Path.Direction.CW);
            break;
          default:
            break;
        }
        return shapePath;
      }
    
    • 高亮区域点击
      @Override
      public boolean onTouchEvent(MotionEvent event) {
        if (event.getAction() == MotionEvent.ACTION_UP) {
          //点击高亮
          if (mOverlay.getOnClickHightLightListener() != null) {
            float x = event.getX();
            float y = event.getY();
            for (RectF rectF : mOverlay.getHightLightAreas()) {
              if (rectF.contains(x, y)) {
                mOverlay.getOnClickHightLightListener().onClick(this);
                remove();
                return super.onTouchEvent(event); //点击高亮后不做其他的处理了,直接返回
              }
            }
          }
          //全局点击
          if (mOverlay.getOnClickGuideViewListener() != null) {
            mOverlay.getOnClickGuideViewListener().onClick(this);
          }
          if (mOverlay.isTouchDismiss()) {
            remove();
          }
        }
    
        return true;
      }
    
    • 添加提示布局
     public void addTipsView(Tips tips) {
        View tipsView = LayoutInflater.from(getContext()).inflate(tips.layoutRes, this, false);
    
        LayoutParams lp = (LayoutParams) tipsView.getLayoutParams();
    
        lp.leftMargin = tips.leftMargin;
        lp.topMargin = tips.topMargin;
        lp.rightMargin = tips.rightMargin;
        lp.bottomMargin = tips.bottomMargin;
    
        if (lp.rightMargin != 0) {
          lp.gravity = Gravity.END;
        } else {
          lp.gravity = Gravity.START;
        }
        if (lp.bottomMargin != 0) {
          lp.gravity |= Gravity.BOTTOM;
        } else {
          lp.gravity |= Gravity.TOP;
        }
    
        tipsView.setLayoutParams(lp);
    
        addView(tipsView, lp);
      }
    

    效果

    效果图

    后记

    因为之前看了一篇文章了解到使用Path进行自定义view绘制,而HightLight项目又是使用Bitmap,被不少issue都提到这个,所以打算使用Path替代Bitmap,这个功能并不复杂,按照思路实现就ok。

    源码

    项目Github地址

    相关文章

      网友评论

          本文标题:Android高亮引导实现 HiGuide

          本文链接:https://www.haomeiwen.com/subject/qansrftx.html