引导图(GuideView)

GuideView

目标图

ImageView imageView = new ImageView(this);
imageView.setImageResource(R.drawable.xxx);
RelativeLayout.LayoutParams params = new RelativeLayout.LayoutParams(ViewGroup.LayoutParams.WRAP_CONTENT, ViewGroup.LayoutParams.WRAP_CONTENT);
imageView.setLayoutParams(params);

TextView textView = new TextView(this);
textView.setText("欢迎使用");
textView.setTextColor(ContextCompat.getColor(this, R.color.background));

方法

GuideView.Builder
        // 初始化
        .newInstance(this)
        // 目标图
        .setTargetView(cvDataEntryFillOutTheForm)
        // 引导图(ImageView或TextView)
        .setCustomGuideView(iv)
        // 引导图状
        .setShape(GuideView.MyShape.RECTANGULAR)
        // 引导图相对目标图位
        .setDirection(GuideView.Direction.RIGHT_TOP)   
        // 引导图圆心(默目标图中心)
        .setCenter(300, 300)
        // 引导图半径
        .setRadius(32)        
        // 引导图偏移
        .setOffset(200, 60)    
        // 背景色(默透明)
        .setBgColor(ContextCompat.getColor(this, R.color.shadow))
        // 点
        .setOnClickListener(new GuideView.OnClickCallback() {
            @Override
            public void onClickedGuideView() {
				guideView.hide();
            }
        })      
        // 仅显一次
        .showOnce()
        // Builder模式(返引导图实例)
        .build()
        // 显示
        .show();

使用

GuideView

package com.zsp.library.guide.guideview;

import android.app.Activity;
import android.content.Context;
import android.graphics.Bitmap;
import android.graphics.Canvas;
import android.graphics.Paint;
import android.graphics.PorterDuff;
import android.graphics.PorterDuffXfermode;
import android.graphics.RectF;
import android.view.Gravity;
import android.view.View;
import android.view.ViewGroup;
import android.view.ViewTreeObserver;
import android.widget.FrameLayout;
import android.widget.RelativeLayout;
import android.widget.TextView;

import androidx.core.content.ContextCompat;

import com.zsp.library.R;

import timber.log.Timber;

/**
 * @decs: 引导视图
 * @author: 郑少鹏
 * @date: 2019/9/11 16:29
 */
public class GuideView extends RelativeLayout implements ViewTreeObserver.OnGlobalLayoutListener {
    private final String TAG = getClass().getSimpleName();
    /**
     * 上下文
     */
    private Context context;
    /**
     * 目标图前缀(SHOW_GUIDE_PREFIX + targetView.getId()作为存于SP文件key)
     */
    private static final String SHOW_GUIDE_PREFIX = "show_guide_on_view";
    private boolean firstShow = true;
    private boolean onlyOnce;
    boolean needDraw = true;
    /**
     * 引导图偏移量
     */
    private int xOffset, yOffset;
    /**
     * 目标图外切圆半径
     */
    private int radius;
    /**
     * 显提示信息的View
     */
    private View targetView;
    /**
     * 自定View
     */
    private View customGuideView;
    /**
     * 透明圆形画笔
     */
    private Paint mCirclePaint;
    /**
     * 目标图已测否
     */
    private boolean isMeasured;
    /**
     * 目标图圆心
     */
    private int[] center;
    /**
     * 绘图层叠模式
     */
    private PorterDuffXfermode porterDuffXfermode;
    /**
     * 绘制前景Bitmap
     */
    private Bitmap bitmap;
    /**
     * 背景色和透明度(格式#aarrggbb)
     */
    private int backgroundColor;
    /**
     * 画布(绘Bitmap)
     */
    private Canvas temp;
    /**
     * 相对目标图位(target方向)
     */
    private Direction direction;
    /**
     * 形状
     */
    private MyShape myShape;
    /**
     * 目标图左上角坐标
     */
    private int[] location;
    private boolean onClickExit;
    private OnClickCallback onclickListener;

    /**
     * constructor
     *
     * @param context 上下文
     */
    public GuideView(Context context) {
        super(context);
        this.context = context;
        init();
    }

    public void restoreState() {
        Timber.d("restoreState");
        xOffset = yOffset = 0;
        radius = 0;
        mCirclePaint = null;
        isMeasured = false;
        center = null;
        porterDuffXfermode = null;
        bitmap = null;
        needDraw = true;
        temp = null;
    }

    public int[] getLocation() {
        return location;
    }

    public void setLocation(int[] location) {
        this.location = location;
    }

    public int getRadius() {
        return radius;
    }

    public void setRadius(int radius) {
        this.radius = radius;
    }

    public void setxOffset(int xOffset) {
        this.xOffset = xOffset;
    }

    public void setyOffset(int yOffset) {
        this.yOffset = yOffset;
    }

    public void setDirection(Direction direction) {
        this.direction = direction;
    }

    public void setShape(MyShape shape) {
        this.myShape = shape;
    }

    /**
     * TextView引导图
     *
     * @return TextView
     */
    public static TextView guideTextView(Context context, int hintRes, int colorRes) {
        TextView textView = new TextView(context);
        textView.setText(hintRes);
        textView.setTextColor(colorRes);
        return textView;
    }

    public void setBgColor(int backgroundColor) {
        this.backgroundColor = backgroundColor;
    }

    public void setCustomGuideView(View customGuideView) {
        this.customGuideView = customGuideView;
        if (!firstShow) {
            restoreState();
        }
    }

    private void init() {

    }

    public void setTargetView(View targetView) {
        this.targetView = targetView;
    }

    public void showOnce() {
        if (targetView != null) {
            onlyOnce = true;
        }
    }

    private void saveShowState() {
        if (targetView != null) {
            context.getSharedPreferences(TAG, Context.MODE_PRIVATE).edit().putBoolean(generateUniqueId(targetView), true).apply();
        }
    }

    private String generateUniqueId(View v) {
        return SHOW_GUIDE_PREFIX + v.getId();
    }

    public int[] getCenter() {
        return center;
    }

    public void setCenter(int[] center) {
        this.center = center;
    }

    public void hide() {
        Timber.d("hide");
        if (customGuideView != null) {
            targetView.getViewTreeObserver().removeOnGlobalLayoutListener(this);
            this.removeAllViews();
            ((FrameLayout) ((Activity) context).getWindow().getDecorView()).removeView(this);
            restoreState();
        }
    }

    public boolean hasShown() {
        return targetView != null && context.getSharedPreferences(TAG, Context.MODE_PRIVATE).getBoolean(generateUniqueId(targetView), false);
    }

    /**
     * 目标图下添提示文本
     * <p>
     * 屏幕窗口添蒙层,蒙层绘总背景和透明圆形,圆形下绘说明文本。
     */
    private void createGuideView() {
        Timber.d("createGuideView");
        // Tips布局参数
        LayoutParams guideViewParams;
        guideViewParams = new LayoutParams(LayoutParams.WRAP_CONTENT, LayoutParams.WRAP_CONTENT);
        guideViewParams.setMargins(0, center[1] + radius + 10, 0, 0);
        if (customGuideView != null) {
            if (direction != null) {
                int width = this.getWidth();
                int height = this.getHeight();
                int left = center[0] - radius;
                int right = center[0] + radius;
                int top = center[1] - radius;
                int bottom = center[1] + radius;
                switch (direction) {
                    case TOP:
                        this.setGravity(Gravity.BOTTOM | Gravity.CENTER_HORIZONTAL);
                        guideViewParams.setMargins(xOffset, yOffset - height + top, -xOffset, height - top - yOffset);
                        break;
                    case LEFT:
                        this.setGravity(Gravity.END);
                        guideViewParams.setMargins(xOffset - width + left, top + yOffset, width - left - xOffset, -top - yOffset);
                        break;
                    case BOTTOM:
                        this.setGravity(Gravity.CENTER_HORIZONTAL);
                        guideViewParams.setMargins(xOffset, bottom + yOffset, -xOffset, -bottom - yOffset);
                        break;
                    case RIGHT:
                        guideViewParams.setMargins(right + xOffset, top + yOffset, -right - xOffset, -top - yOffset);
                        break;
                    case LEFT_TOP:
                        this.setGravity(Gravity.END | Gravity.BOTTOM);
                        guideViewParams.setMargins(xOffset - width + left, yOffset - height + top, width - left - xOffset, height - top - yOffset);
                        break;
                    case LEFT_BOTTOM:
                        this.setGravity(Gravity.END);
                        guideViewParams.setMargins(xOffset - width + left, bottom + yOffset, width - left - xOffset, -bottom - yOffset);
                        break;
                    case RIGHT_TOP:
                        this.setGravity(Gravity.BOTTOM);
                        guideViewParams.setMargins(right + xOffset, yOffset - height + top, -right - xOffset, height - top - yOffset);
                        break;
                    case RIGHT_BOTTOM:
                        guideViewParams.setMargins(right + xOffset, bottom + yOffset, -right - xOffset, -top - yOffset);
                        break;
                    default:
                        break;
                }
            } else {
                guideViewParams = new LayoutParams(ViewGroup.LayoutParams.WRAP_CONTENT, ViewGroup.LayoutParams.WRAP_CONTENT);
                guideViewParams.setMargins(xOffset, yOffset, -xOffset, -yOffset);
            }
            this.addView(customGuideView, guideViewParams);
        }
    }

    /**
     * 目标图宽高
     * <p>
     * 未测返-1、-1。
     *
     * @return 宽高
     */
    private int[] getTargetViewSize() {
        int[] location = {-1, -1};
        if (isMeasured) {
            location[0] = targetView.getWidth();
            location[1] = targetView.getHeight();
        }
        return location;
    }

    /**
     * 目标图半径
     *
     * @return 半径
     */
    private int getTargetViewRadius() {
        if (isMeasured) {
            int[] size = getTargetViewSize();
            int x = size[0];
            int y = size[1];

            return (int) (Math.sqrt(x * x + y * y) / 2);
        }
        return -1;
    }

    @Override
    protected void onDraw(Canvas canvas) {
        super.onDraw(canvas);
        Timber.d("onDraw");
        if (!isMeasured) {
            return;
        }
        if (targetView == null) {
            return;
        }
        drawBackground(canvas);
    }

    private void drawBackground(Canvas canvas) {
        Timber.d("drawBackground");
        needDraw = false;
        // 绘Bitmap后绘Bitmap至屏幕
        bitmap = Bitmap.createBitmap(canvas.getWidth(), canvas.getHeight(), Bitmap.Config.ARGB_8888);
        temp = new Canvas(bitmap);
        // 背景画笔
        Paint bgPaint = new Paint();
        if (backgroundColor != 0) {
            bgPaint.setColor(backgroundColor);
        } else {
            bgPaint.setColor(ContextCompat.getColor(context, R.color.blackCC));
        }
        // 绘屏幕背景
        temp.drawRect(0, 0, temp.getWidth(), temp.getHeight(), bgPaint);
        // 目标图透明圆形画笔
        if (mCirclePaint == null) {
            mCirclePaint = new Paint();
        }
        // 或CLEAR
        porterDuffXfermode = new PorterDuffXfermode(PorterDuff.Mode.SRC_OUT);
        mCirclePaint.setXfermode(porterDuffXfermode);
        mCirclePaint.setAntiAlias(true);
        if (myShape != null) {
            RectF oval = new RectF();
            switch (myShape) {
                /*
                  圆形
                 */
                case CIRCULAR:
                    temp.drawCircle(center[0], center[1], radius, mCirclePaint);
                    break;
                /*
                  椭圆
                 */
                case ELLIPSE:
                    // RectF对象
                    // 左、上、右、下
                    oval.left = center[0] - 150;
                    oval.top = center[1] - 50;
                    oval.right = center[0] + 150;
                    oval.bottom = center[1] + 50;
                    // 绘椭圆
                    temp.drawOval(oval, mCirclePaint);
                    break;
                /*
                  圆角矩形
                 */
                case RECTANGULAR:
                    // RectF对象
                    // 左、上、右、下
                    oval.left = center[0] - 150;
                    oval.top = center[1] - 50;
                    oval.right = center[0] + 150;
                    oval.bottom = center[1] + 50;
                    // 绘圆角矩形
                    temp.drawRoundRect(oval, radius, radius, mCirclePaint);
                    break;
                default:
                    break;
            }
        } else {
            // 绘圆形
            temp.drawCircle(center[0], center[1], radius, mCirclePaint);
        }
        // 绘至屏幕
        canvas.drawBitmap(bitmap, 0, 0, bgPaint);
        bitmap.recycle();
    }

    public void setOnClickExit(boolean onClickExit) {
        this.onClickExit = onClickExit;
    }

    public void setOnclickListener(OnClickCallback onclickListener) {
        this.onclickListener = onclickListener;
    }

    private void setClickInfo() {
        final boolean exit = onClickExit;
        setOnClickListener(v -> {
            if (onclickListener != null) {
                onclickListener.onClickedGuideView();
            }
            if (exit) {
                hide();
            }
        });
    }

    @Override
    public void onGlobalLayout() {
        if (isMeasured) {
            return;
        }
        if (targetView.getHeight() > 0 && targetView.getWidth() > 0) {
            isMeasured = true;
        }
        // 目标图中心坐标
        if (center == null) {
            // 右上角坐标
            location = new int[2];
            targetView.getLocationInWindow(location);
            center = new int[2];
            // 中心坐标
            center[0] = location[0] + targetView.getWidth() / 2;
            center[1] = location[1] + targetView.getHeight() / 2;
        }
        // 目标图外切圆半径
        if (radius == 0) {
            radius = getTargetViewRadius();
        }
        // 添GuideView
        createGuideView();
    }

    /**
     * 引导图相对目标图方位
     * <p>
     * 不设默目标图下方。
     */
    public enum Direction {
        // 左、上、右、下、左上、左下、右上、右下
        LEFT,
        TOP,
        RIGHT,
        BOTTOM,
        LEFT_TOP,
        LEFT_BOTTOM,
        RIGHT_TOP,
        RIGHT_BOTTOM
    }

    /**
     * 目标图状。
     * <p>
     * 圆形、椭圆、圆角矩形。
     * 可设圆角大小,不设默圆形。
     */
    public enum MyShape {
        // 圆形、椭圆、圆角矩形
        CIRCULAR,
        ELLIPSE,
        RECTANGULAR
    }

    /**
     * 引导图点回调
     */
    public interface OnClickCallback {
        /**
         * 引导图点
         */
        void onClickedGuideView();
    }

    /**
     * Builder
     * <p>
     * 建造者模式。
     */
    public static class Builder {
        GuideView guiderView;

        public Builder(Context context) {
            guiderView = new GuideView(context);
        }

        public Builder setTargetView(View target) {
            guiderView.setTargetView(target);
            return this;
        }

        public Builder setBgColor(int color) {
            guiderView.setBgColor(color);
            return this;
        }

        public Builder setDirection(Direction dir) {
            guiderView.setDirection(dir);
            return this;
        }

        public Builder setShape(MyShape shape) {
            guiderView.setShape(shape);
            return this;
        }

        public Builder setOffset(int x, int y) {
            guiderView.setxOffset(x);
            guiderView.setyOffset(y);
            return this;
        }

        public Builder setRadius(int radius) {
            guiderView.setRadius(radius);
            return this;
        }

        public Builder setCustomGuideView(View view) {
            guiderView.setCustomGuideView(view);
            return this;
        }

        public Builder setCenter(int x, int y) {
            guiderView.setCenter(new int[]{x, y});
            return this;
        }

        public Builder showOnce() {
            guiderView.showOnce();
            return this;
        }

        public GuideView build() {
            guiderView.setClickInfo();
            return guiderView;
        }

        public Builder setOnClickExit(boolean onclickExit) {
            guiderView.setOnClickExit(onclickExit);
            return this;
        }

        public Builder setOnClickListener(final OnClickCallback callback) {
            guiderView.setOnclickListener(callback);
            return this;
        }
    }

    public void show() {
        Timber.d("show");
        if (hasShown()) {
            return;
        }
        if (targetView != null) {
            targetView.getViewTreeObserver().addOnGlobalLayoutListener(this);
        }
        this.setBackgroundResource(R.color.transparent);
        ((FrameLayout) ((Activity) context).getWindow().getDecorView()).addView(this);
        firstShow = false;
        if (onlyOnce) {
            saveShowState();
        }
    }
}

colors.xml

<?xml version="1.0" encoding="utf-8"?>
<resources>   
     <color name="blackCC">#CC000000</color>
</resources>

主代码

/**
 * 引导视图
 */
private GuideView guideViewOne;
private GuideView guideViewTwo;
   
private void initConfiguration() {
    /*
      引导视图一
     */
    guideViewOne = new GuideView.Builder(this)
            // 目标图(必调)
            .setTargetView(guideOneActivityIvOne)
            // 引导图(ImageView或TextView)(必调)
            .setCustomGuideView(GuideView.guideTextView(this, R.string.picture, ContextCompat.getColor(this, R.color.background)))
            // 引导图状(圆形、椭圆、矩形,矩形可圆角矩形)
            .setShape(GuideView.MyShape.CIRCULAR)
            // 引导图相对目标图位(八种,不设默屏左上角)
            .setDirection(GuideView.Direction.RIGHT_TOP)
            // 圆形引导图透明区半径,矩形引导图圆角大小
            .setRadius(170)
            // 圆心(默目标图中心)
            .setCenter(300, 300)
            // 偏移(微调引导图位)
            .setOffset(200, 60)
            // 背景色(默透明)
            .setBgColor(ContextCompat.getColor(this, R.color.blackCC))
            // 点
            .setOnClickListener(() -> {
                guideViewOne.hide();
                guideViewTwo.show();
            })
            // 显一次
            .showOnce()
            // Builder模式(返引导图实例)(必调)
            .build();
    /*
      引导视图二
     */
    guideViewTwo = new GuideView.Builder(this)
            .setTargetView(guideOneActivityIvTwo)
            .setCustomGuideView(GuideView.guideTextView(this, R.string.animation, ContextCompat.getColor(this, R.color.background)))
            .setShape(GuideView.MyShape.CIRCULAR)
            .setDirection(GuideView.Direction.LEFT_TOP)
            .setRadius(170)
            .setBgColor(ContextCompat.getColor(this, R.color.blackCC))
            .setOnClickListener(() -> {
                guideViewTwo.hide();
                ToastUtils.shortShow(this, "引导结束");
            })
            .showOnce()
            .build();
}

private void startLogic() {
    if (guideViewOne.hasShown()) {
        ToastUtils.shortShow(this, "引导一仅显一次");
    } else {
        guideViewOne.show();
    }
}
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

snpmyn

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

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

余额充值