今天打算跟大家分享一个 用于首次安装 ,用户功能引导页。可能现在有些开源的高亮引导控件无法满足设计的需求,那么大家可以看一看我这篇文章,并不是十分的完善,仅仅是为了给大家提供一个思路,那么下面我写一下实现的思路。

       一,拿到当前activity的decorview  我们的蒙层是加到这个decorview 中 。我们知道每一个activity的页面实际上都是在decorview 中的。而decorview 又是一个framelayout 所以我们的高亮蒙层 放到decorview 中就可以实现遮罩层的效果。

       二,在绘制时根据获取到的decorview 设置你的自定义view的尺寸 。这样保证完全覆盖你的activity。不会有尺寸适配的问题。

       三,构建一个画笔 ,并设置它的xfermode 为 clear, 这个画笔 用于在你的自定义view 上抠一块透明的区域出来 。

       四,根据传入的目标view 计算它在当前屏幕中的坐标。根据它的坐标计算它的中点坐标,然后构建一个高亮区域的矩形rect 用于绘制高亮区域。当然要结合上一步的画笔

       五,根据设计图 ,计算指引文字在高亮区域的相对位置。计算一个坐标。用于绘制指引文字或者图片。

       六 ,根据需求计算一个用于点击区域的 rect  ,当用户触摸时 计算触摸位置如果是符合要求的那么 可以进行隐藏蒙层或者进行其他操作

基本主要实现思路就是上面的六步,然后我会将实现的代码贴在下面 ,我已经将每一步的代码都做了相应的注释。大家如果想看可以按照注释来对照我上面写的思路,来最终设计你们自己的自定义view 来实现你们的需求。



public class GuideViewTest extends View {
    Paint bgPaint, highLightP;
    Bitmap bgBitmap;
    Canvas bgCanvas;
    private int mWidth;
    private int mHeight;
    private FrameLayout decorFrameLayout;

    private int highW;
    private int highH;
    int[] hvLocation=new int[2];
    Rect centerBtnRec =new Rect();
    int centerBtnW=DensityUtil.dip2px(88);
    int centerBtnH=DensityUtil.dip2px(34);
    private View highView;  //高亮提示view
    float originx = 0;
    float originy=0 ;
    float dtx=0;
    float dty=0;


    private int highCornerSize=DensityUtil.dip2px(5);


    public GuideViewTest(Context context) {
        this(context,null);
    }

    public GuideViewTest(Context context, AttributeSet attrs) {
        this(context,attrs,0);

    }

    public GuideViewTest(Context context, AttributeSet attrs, int defStyleAttr) {
        super(context, attrs, defStyleAttr);
        bgPaint = new Paint();
        bgPaint.setAntiAlias(true);

  
        setLayerType(LAYER_TYPE_HARDWARE, null);
        if(mContext instanceof Activity){

            Activity activity = (Activity) mContext;
            //1 . 通过activity 对象获得根布局decorview
            final View decorView = activity.getWindow().getDecorView();
            if(decorView instanceof FrameLayout){
                decorFrameLayout = (FrameLayout) decorView;
                final ViewTreeObserver vto =decorFrameLayout.getViewTreeObserver();

                vto.addOnPreDrawListener(new ViewTreeObserver.OnPreDrawListener() {
                    @Override
                    public boolean onPreDraw() {
                        decorFrameLayout.getViewTreeObserver().removeOnPreDrawListener(this);
                        //2 . 通过decorview 获取整体屏幕的宽高
                        mHeight=decorFrameLayout.getHeight();
                        mWidth=decorFrameLayout.getWidth();
                        centerBtnRec.left=mWidth/2-centerBtnW/2;
                        centerBtnRec.right=mWidth/2+centerBtnW/2;
                        centerBtnRec.top=mHeight/2-centerBtnH/2;
                        centerBtnRec.bottom=mHeight/2+centerBtnH/2;
                        if(mWidth>0&&mHeight>0){
                            //3 . 设置蒙层与屏幕的宽高相等
                            setMeasuredDimension( MeasureSpec.makeMeasureSpec(mWidth,MeasureSpec.EXACTLY),
                                    MeasureSpec.makeMeasureSpec(mHeight,MeasureSpec.EXACTLY));
                            //4 . 创建一个 用于绘制指引 文字的背景画布  宽高与 蒙层相同
                            bgBitmap = Bitmap.createBitmap(mWidth,mHeight, Bitmap.Config.ARGB_8888);
                            bgCanvas = new Canvas(bgBitmap);
                        }
                        return true;
                    }
                });
            }
        }
    }

    @Override
    protected void onMeasure(int widthMeasureSpec, int heightMeasureSpec) {
        super.onMeasure(widthMeasureSpec, heightMeasureSpec);

    }
    @Override
    protected void onDraw(Canvas canvas) {
        super.onDraw(canvas);
        //设置 半透明 背景色
        setBackgroundColor(Color.argb((int) (255*0.4f),0,0,0));
        if(bgCanvas !=null){
            checkParams();
            //创建 高亮画笔
            highLightP = new Paint();
            highLightP.setColor(Color.YELLOW);
            highLightP.setAntiAlias(true);
            //5 .每次绘制前清除背景画布
            clearCanvas(bgCanvas);
            // 根据传入的 高亮目标view 计算它 在屏幕中的坐标
            highView.getLocationInWindow(hvLocation);
            hvLocation[0]=hvLocation[0]+highView.getWidth()/2;
            hvLocation[1]=hvLocation[1]+highView.getHeight()/2;
            // 根据目标view 的坐标以他的中心为中心点 扩大范围 计算出一个 矩形范围 作为高亮显示区域
            RectF rectF = new RectF(hvLocation[0] - highW / 2, hvLocation[1] - highH / 2
                    , hvLocation[0] + highW / 2, hvLocation[1] + highH / 2);
            canvasGuideUI();
            // 8 .设置高亮 画笔 的 mode 为clear  这样可以在背景上 抠出一片 透明区域
            highLightP.setXfermode(new PorterDuffXfermode(PorterDuff.Mode.CLEAR));
            // 9 . 将高亮区域 矩形绘制到 画布上 。
            canvas.drawRoundRect(rectF,highCornerSize,highCornerSize,highLightP);
            // 10 ,将 创建的 背景画布 绘制到画布上 。
            canvas.drawBitmap(bgBitmap,0,0, bgPaint);
        }
    }

    /**
     * 根据页面类型计算坐标
     */
    private void canvasGuideUI() {
                float left;
                float top ;
                Bitmap bitmap = BitmapFactory.decodeResource(getResources(), R.drawable.文字);
                int bmpwidth = bitmap.getWidth();
                //6 .根据实际ui的效果图计算一下 文字与高亮显示区域的坐标即可
                top=hvLocation[1]+1/3*highH-bitmap.getHeight();
                left= hvLocation[0]-highW/2-bmpwidth*(4f/5);
                float bottom = top+bitmap.getHeight();
               
                Paint bmp=new Paint(Paint.ANTI_ALIAS_FLAG);
                //7 .将提示文字绘制到 背景画布上
                bgCanvas.drawBitmap(BitmapFactory.decodeResource(mContext.
                        getResources(),R.drawable.文字),centerBtnRec.left,centerBtnRec.top,bmp);
    }

    @Override
    public boolean onTouchEvent(MotionEvent event) {
        int defClickSize=20;
        switch(event.getAction()){
            case MotionEvent.ACTION_DOWN:
                originx=event.getX();
                originy=event.getY();
                // LogUtil.i(TAG,originx+"+++"+originy);
                break;
            case MotionEvent.ACTION_UP:
                dtx=event.getX()-originx;
                dty=event.getY()-originy;
                //如果触摸和抬起的 x y 坐标偏移量不至于过高那么 认为是点击操作             
                if(Math.abs(dtx)<defClickSize&&Math.abs(dty)<defClickSize){
                    //如果触摸位置在我规定的 点击位置内 处理点击事件
                    if(centerBtnRec.contains((int)event.getX(),(int)event.getY())){
                        //处理点击事件
                        dismiss();
                    }
                }
//                    LogUtil.i(TAG,event.getX()+"=="+(event.getX()-originx));
                break;
        }

        return true;

    }

    private void checkParams() {
        if(highView==null){
            return ;
        }
        if(highW<=0||highW<=0){
            return ;
        }

    }

    /**
     * 默认矩形高亮
     * 设置高亮部分的宽高
     * 传入dp 值
     */
    public GuideViewTest setHighlightSize(int highW,int highH){
        this.highW= DensityUtil.dip2px(highW);
        this.highH= DensityUtil.dip2px(highH);

        return this;
    }

    /**
     * 需要高亮提示的View
     * @param view
     */
    public  GuideViewTest setHighView(View view){
        highView=view;

        return this;
    }

    /**
     * 显示guideview
     */
    public void show(){
        FrameLayout.LayoutParams f=new FrameLayout.LayoutParams(ViewGroup.LayoutParams.MATCH_PARENT,
                ViewGroup.LayoutParams.MATCH_PARENT);
        if(decorFrameLayout!=null)
            decorFrameLayout.addView(GuideViewTest.this,f);
        invalidate();
    }

    /**
     * 隐藏guideview
     */
    public void dismiss(){
        if(decorFrameLayout!=null){
            decorFrameLayout.removeView(this);
        }
        bgCanvas=null;
        bgPaint=null;
        highLightP=null;
        if(bgBitmap!=null)
            bgBitmap.recycle();
        bgBitmap=null;
    }


}

 

Logo

旨在为数千万中国开发者提供一个无缝且高效的云端环境,以支持学习、使用和贡献开源项目。

更多推荐