自定义View-----蒙版引导

先看看效果吧
这里写图片描述

看完图可以更直接的知道这个是干啥的,不过只实现了几种情况,其他的情况可以自己去计算去写。
实现的情况如下图(根据我的需求只写了这几个):
这里写图片描述

1、自定义View

package com.example.a_0102.mylearn.demo;

import android.content.Context;
import android.graphics.Canvas;
import android.graphics.Color;
import android.graphics.DashPathEffect;
import android.graphics.Paint;
import android.graphics.PorterDuff;
import android.graphics.PorterDuffXfermode;
import android.graphics.RectF;
import android.util.AttributeSet;
import android.util.Log;
import android.view.View;
import android.widget.RelativeLayout;


/**
 * Created by ws on 2018/8/21.
 * 必须添加到RelativeLayout上,否则不能出现盖住的效果
 */

public class MyView extends RelativeLayout {
    private Context context;
    private int mBackgroundColor;//蒙版的背景色
    private Paint mPaint;//画高亮区域的画笔
    private Paint mLinePaint;//画线的画笔
    private View mShowView;//高亮的View
    private View mengbanView;//添加的蒙版View
    private View mIndexView;//需要和高亮的View连线的View
    private float startX, startY, stopX, stopY, centerX, centerY;//划线的,起点、中间点、结束点
    private int height;//状态栏的高度
    private int type;//画线的类型
    private int offest;//不是从中点画的偏差
    private int mIndexResId;//说明的View的资源ID

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

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

    public MyView(Context context, AttributeSet attrs, int defStyleAttr) {
        super(context, attrs, defStyleAttr);
        this.context = context;
        this.mBackgroundColor = Color.parseColor("#cc000000");
        this.init();
    }

    private void init() {
        this.mPaint = new Paint();
        //设置画虚线的画笔
        this.mLinePaint = new Paint();
        mLinePaint.setColor(Color.parseColor("#ffffff"));
        mLinePaint.setStrokeWidth(3);
        mLinePaint.setPathEffect(new DashPathEffect(new float[]{5, 5}, 0));

        this.mPaint.setAntiAlias(true);
        PorterDuffXfermode xfermode = new PorterDuffXfermode(PorterDuff.Mode.CLEAR);
        this.mPaint.setXfermode(xfermode);
//        this.mPaint.setMaskFilter(new BlurMaskFilter(10.0F, BlurMaskFilter.Blur.INNER));//模糊
        this.setLayerType(1, (Paint) null);//关闭单个view的硬件加速:
        this.setClickable(true);
        //ViewGroup默认情况下,出于性能考虑,会被设置成WILL_NOT_DROW,这样,ondraw就不会被执行了。
        // 如果我们想重写一个viewgroup的ondraw方法,有两种方法:
        // 1,构造函数中,给viewgroup设置一个颜色。
        // 2,构造函数中,调用setWillNotDraw(false),去掉其WILL_NOT_DRAW flag。
        // 在viewgroup初始化的时候,它调用了一个私有方法:initViewGroup,它里面会有一句setFlags(WILLL_NOT_DRAW,DRAW_MASK);
        // 相当于调用了setWillNotDraw(true),所以说,对于ViewGroup,他就认为是透明的了,如果我们想要重写onDraw,就要调用setWillNotDraw(false)。
        this.setWillNotDraw(false);
        //状态栏的高度
        height = getStatusBarHeight(context);
    }

    protected void onDraw(Canvas canvas) {

        canvas.drawColor(this.mBackgroundColor);
        int[] location = new int[2];

        //获取View在屏幕的位置
        mShowView.getLocationOnScreen(location);
        RectF showRectF = new RectF();
        showRectF.left = (float) location[0];
        showRectF.top = (float) location[1] - height;
        showRectF.right = (float) (location[0] + mShowView.getWidth());
        showRectF.bottom = (float) (location[1] + mShowView.getHeight()) - height;

        RectF indexRectF = new RectF();
        if (mengbanView != null) {
            mIndexView = mengbanView.findViewById(mIndexResId);
            mIndexView.getLocationOnScreen(location);
            indexRectF.left = (float) location[0];
            indexRectF.top = (float) location[1] - height;
        }

        //画高亮的矩形
        canvas.drawRect(showRectF, this.mPaint);
        //画线
        switch (type) {
            case 0://高亮区域在右上,向左向下画线(从中点画)
                startY = showRectF.top + mShowView.getHeight() / 2;
                stopX = indexRectF.left + mIndexView.getWidth() / 2;
                stopY = indexRectF.top;
                startX = showRectF.left;
                centerX = stopX;
                centerY = startY;
                canvas.drawLine(startX, startY, centerX, centerY, mLinePaint);
                canvas.drawLine(centerX, centerY, stopX, stopY, mLinePaint);
                break;
            case 1://画一条线,高亮区域在上面(和2的雷同)
                startX = showRectF.left + mShowView.getWidth() / 2;
                stopX = startX;
                startY = showRectF.top + mShowView.getHeight();
                stopY = indexRectF.top;
                canvas.drawLine(startX, startY, stopX, stopY, mLinePaint);
                break;
            case 2://画一条线,高亮区域在下面
                startX = showRectF.left + mIndexView.getWidth() / 2;
                stopX = startX;
                startY = showRectF.top;
                stopY = indexRectF.top + mIndexView.getHeight();
                canvas.drawLine(startX, startY, stopX, stopY, mLinePaint);
                break;
            case 3://高亮区域在下面,两条线,不再高亮区域的中间开始画-|(和4的是左右相反的)
                startX = showRectF.right - offest;
                startY = showRectF.top;
                stopX = indexRectF.left + mIndexView.getWidth();
                stopY = indexRectF.top + mIndexView.getHeight() / 2;
                centerX = startX;
                centerY = stopY;
                Log.e("--------->","3333");
                canvas.drawLine(startX, startY, centerX, centerY, mLinePaint);
                canvas.drawLine(centerX, centerY, stopX, stopY, mLinePaint);
                break;
            case 4://高亮区域在下面,不是从中间连线的|-
                startX = showRectF.left + offest;
                startY = showRectF.top;
                stopX = indexRectF.left;
                stopY = indexRectF.top + mIndexView.getHeight() / 2;
                centerX = startX;
                centerY = stopY;
                canvas.drawLine(startX, startY, centerX, centerY, mLinePaint);
                canvas.drawLine(centerX, centerY, stopX, stopY, mLinePaint);
                break;
            case 5://一条线,高亮的在下面,不是从中间开始画的(1,2是中间开始画的)
                startX = showRectF.left + offest;
                stopX = startX;
                startY = showRectF.top;
                stopY = indexRectF.top + mIndexView.getHeight();
                canvas.drawLine(startX, startY, stopX, stopY, mLinePaint);
                break;
            case 6:
                startX = showRectF.left;
                startY = showRectF.top + mShowView.getHeight() / 2;
                stopX = showRectF.left-offest;
                stopY = indexRectF.top + mIndexView.getHeight();
                centerX = stopX;
                centerY = startY;
                canvas.drawLine(startX, startY, centerX, centerY, mLinePaint);
                canvas.drawLine(centerX, centerY, stopX, stopY, mLinePaint);
                break;
        }
    }

    public MyView setOffest(int offest) {
        this.offest = offest;
        return this;
    }

    //修改高亮的View,同一个蒙版下不同的高亮部分
    public MyView changeView(View changeView){
        this.mShowView = changeView;
        invalidate();//执行onDraw方法
        return this;
    }

    //修改蒙版View
    public MyView changeMengbanView(View mengbanView,int mIndexResId){
        removeAllViews();
        this.mengbanView = mengbanView;
        this.mIndexResId = mIndexResId;
        invalidate();
        return this;
    }
    public MyView setShowView(View showView) {
        this.mShowView = showView;
        return this;
    }

    public MyView setmBackgroundColor(int mBackgroundColor) {
        this.mBackgroundColor = mBackgroundColor;
        return this;
    }

    /**
     * 设置蒙版View,并指定哪个是说明作用的View
     * @param mengbanView
     * @param mIndexResId
     * @return
     */
    public MyView setMengbanView(View mengbanView,int mIndexResId) {
        this.mengbanView = mengbanView;
        this.mIndexResId = mIndexResId;
        return this;
    }

    public MyView setType(int type) {
        this.type = type;
        return this;
    }

    public void dismiss(){
        removeAllViews();
    }

    public int getStatusBarHeight(Context context) {
        int height = 38;
        int resourceId = context.getResources().getIdentifier("status_bar_height", "dimen", "android");
        if (resourceId > 0) {
            height = context.getResources().getDimensionPixelSize(resourceId);
        }

        Log.e("-------->", "状态栏的高度:" + height);
        return height;
    }

}

2、使用

package com.example.a_0102.mylearn.demo;
import android.support.v7.app.AppCompatActivity;
import android.os.Bundle;
import android.view.LayoutInflater;
import android.view.View;
import android.widget.LinearLayout;
import android.widget.RelativeLayout;
import android.widget.ScrollView;
import android.widget.TextView;

import com.example.a_0102.mylearn.R;

public class ViewActivity extends AppCompatActivity {

    LinearLayout layout;
    RelativeLayout rl_top;
    RelativeLayout rl;
    ScrollView scrollView;
    TextView tv_text;
    TextView tv_scroll;
    private View view;

    @Override
    protected void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        setContentView(R.layout.activity_view2);
        layout = findViewById(R.id.ll_kuang);
        rl_top = findViewById(R.id.rl_top);
        rl = findViewById(R.id.rl);
        tv_text = findViewById(R.id.tv_text);
        tv_scroll = findViewById(R.id.tv_scroll);
        scrollView = findViewById(R.id.scrollview);

        //创建蒙版层
        view = LayoutInflater.from(this).inflate(R.layout.layout_mengban, null, false);

        final MyView myView = new MyView(this)
                .setMengbanView(view, R.id.tv_index)//蒙版层的View
                .setShowView(tv_text)//需要高亮的View,需要指引的View
                .setType(0)//设置画线的类型
                .setOffest(30);//设置画线的偏移量
        //设置宽高
        final RelativeLayout.LayoutParams params = new RelativeLayout.LayoutParams(-1, -1);
        myView.addView(view, params);
        //将蒙版的View添加到View上
        rl.addView(myView);

        //同一个蒙版上的另一个高亮区域
        final TextView tv_next = view.findViewById(R.id.tv_next);
        tv_next.setOnClickListener(new View.OnClickListener() {
            @Override
            public void onClick(View v) {
                myView.setType(1);
                myView.changeView(layout);//需要高亮的View,需要指引的View
            }
        });

        //取消蒙版
        final TextView tv_cancel = view.findViewById(R.id.tv_know);
        tv_cancel.setOnClickListener(new View.OnClickListener() {
            @Override
            public void onClick(View v) {
                myView.dismiss();

            }
        });

        //给蒙版上的View设置监听事件,换另一种样式的蒙版
        TextView tv_click = view.findViewById(R.id.tv_click);
        tv_click.setOnClickListener(new View.OnClickListener() {
            @Override
            public void onClick(View v) {
                int local[] = new int[2];
                tv_scroll.getLocationOnScreen(local);
                scrollView.scrollBy(0, local[1]);
                view = LayoutInflater.from(ViewActivity.this).inflate(R.layout.layout_mengban2, null, false);
                TextView tv_know = view.findViewById(R.id.tv_index);
                myView.changeMengbanView(view, R.id.tv_index);//蒙版层的View
                myView.setShowView(tv_scroll)//需要高亮的View,需要指引的View
                        .setType(5);//设置画线的类型
                tv_know.setOnClickListener(new View.OnClickListener() {
                    @Override
                    public void onClick(View v) {
                        rl.removeView(myView);
                    }
                });
                TextView tv_cancel = view.findViewById(R.id.tv_cancel);
                tv_cancel.setOnClickListener(new View.OnClickListener() {
                    @Override
                    public void onClick(View v) {
                        myView.dismiss();
                    }
                });
                myView.addView(view, params);
            }
        });

    }

}

3、涉及到的布局
activity_view2.xml

<?xml version="1.0" encoding="utf-8"?>
<RelativeLayout xmlns:android="http://schemas.android.com/apk/res/android"
    android:id="@+id/rl"
    android:layout_width="match_parent"
    android:layout_height="match_parent">

    <ScrollView
        android:id="@+id/scrollview"
        android:layout_width="match_parent"
        android:layout_height="match_parent">

        <RelativeLayout
            android:layout_width="match_parent"
            android:layout_height="1000dp">
            <LinearLayout
                android:id="@+id/ll_kuang"
                android:layout_width="wrap_content"
                android:layout_height="wrap_content"
                android:layout_centerHorizontal="true"
                android:layout_marginTop="100dp">
                <ImageView
                    android:layout_width="73dp"
                    android:layout_height="32dp"
                    android:background="@mipmap/ic_launcher" />
            </LinearLayout>


            <TextView
                android:id="@+id/tv_text"
                android:layout_width="wrap_content"
                android:layout_height="wrap_content"
                android:layout_alignParentRight="true"
                android:layout_marginRight="30dp"
                android:layout_marginTop="300dp"
                android:background="#ddff00"
                android:padding="20dp"
                android:text="对比" />

            <TextView
                android:id="@+id/tv_scroll"
                android:layout_width="wrap_content"
                android:layout_height="wrap_content"
                android:layout_centerHorizontal="true"
                android:layout_marginTop="800dp"
                android:gravity="center"
                android:text="滑动到这了么"
                android:textColor="#ff00ff"
                android:textSize="20sp" />
        </RelativeLayout>

    </ScrollView>

</RelativeLayout>

layout_mengban.xml

<?xml version="1.0" encoding="utf-8"?>
<RelativeLayout xmlns:android="http://schemas.android.com/apk/res/android"
    android:layout_width="match_parent"
    android:layout_height="match_parent"
    android:orientation="vertical">

    <TextView
        android:id="@+id/tv_click"
        android:layout_width="wrap_content"
        android:layout_height="wrap_content"
        android:layout_centerHorizontal="true"
        android:layout_marginTop="60dp"
        android:text="点这里试试"
        android:textColor="#ffffff" />
    <TextView
        android:id="@+id/tv_index"
        android:layout_width="wrap_content"
        android:layout_height="wrap_content"
        android:layout_alignParentBottom="true"
        android:layout_centerHorizontal="true"
        android:layout_marginBottom="120dp"
        android:text="说明这个View是干啥的"
        android:textColor="#ffffff" />


    <TextView
        android:id="@+id/tv_next"
        android:layout_width="wrap_content"
        android:layout_height="wrap_content"
        android:layout_alignParentBottom="true"
        android:layout_centerHorizontal="true"
        android:layout_marginBottom="80dp"
        android:text="下一步"
        android:textColor="#ffffff" />

    <TextView
        android:id="@+id/tv_know"
        android:layout_width="wrap_content"
        android:layout_height="wrap_content"
        android:layout_alignParentBottom="true"
        android:layout_centerHorizontal="true"
        android:layout_marginBottom="40dp"
        android:text="知道了"
        android:textColor="#ffffff" />
</RelativeLayout>

layout_mengban2.xml

<?xml version="1.0" encoding="utf-8"?>
<RelativeLayout xmlns:android="http://schemas.android.com/apk/res/android"
    android:layout_width="match_parent"
    android:layout_height="match_parent">

    <TextView
        android:id="@+id/tv_index"
        android:layout_width="wrap_content"
        android:layout_height="wrap_content"
        android:text="这个是告诉你怎么弄"
        android:textColor="#ffffff"
        android:layout_marginTop="30dp"
        android:layout_centerHorizontal="true"/>
    <TextView
        android:id="@+id/tv_cancel"
        android:layout_width="wrap_content"
        android:layout_height="wrap_content"
        android:layout_centerHorizontal="true"
        android:layout_below="@id/tv_index"
        android:layout_marginTop="20dp"
        android:textColor="#ffffff"
        android:text="知道了"/>
</RelativeLayout>

4、参考
Android Paint Xfermode 学习小结

主要思路来源自下面的这个类库
推荐一个好用小巧的Android引导蒙版(浮层)库

猜你喜欢

转载自blog.csdn.net/hello_1s/article/details/82019431
今日推荐