android自定义高亮引导

原理就是用一个蒙层覆盖在界面之上,这里所说的界面从下面获取:

activity.getWindow().getDecorView()

高亮部分原理使用Xfermode图像混合模式,参考Android进阶——Xfermode图像混合模式
工具类:

/**
 * 高亮引导工具类
 *
 */
public class HighlightUtil {

    /**
     *
     * @param activity
     * @param guideViews 要高亮提示的view数组 (TODO 目前只至此一个view,待扩充)
     */
    public static void setHighlight(Activity activity,  View[] guideViews) {
        if (activity == null || guideViews == null || guideViews.length <= 0) {
            return;
        }
        View highlightView = new HighlightView(activity, guideViews);
        View rootView = activity.getWindow().getDecorView();
        if (rootView instanceof FrameLayout) {
            ViewGroup.LayoutParams lp = new ViewGroup.LayoutParams(ViewGroup.LayoutParams.MATCH_PARENT,
                    ViewGroup.LayoutParams.MATCH_PARENT);
            ((FrameLayout) rootView).addView(highlightView,
                    ((FrameLayout) rootView).getChildCount(), lp);
        } else {
            FrameLayout frameLayout = new FrameLayout(activity);
            ViewGroup parent = (ViewGroup) rootView.getParent();
            parent.removeView(rootView);
            parent.addView(frameLayout, rootView.getLayoutParams());
            ViewGroup.LayoutParams lp = new ViewGroup.LayoutParams(
                    ViewGroup.LayoutParams.MATCH_PARENT, ViewGroup.LayoutParams.MATCH_PARENT);
            frameLayout.addView(rootView, lp);
            lp = new ViewGroup.LayoutParams(
                    ViewGroup.LayoutParams.MATCH_PARENT, ViewGroup.LayoutParams.MATCH_PARENT);
            frameLayout.addView(highlightView, lp);
        }
    }
}

自定义蒙层:

/**
 * 自定义高亮引导view
 *
 */
public class HighlightView extends View {

    private View[] guideViews;
    private int padding = 20;
    private int[] guideLocation; // left right top bottom
    private Bitmap arrow;
    private Bitmap dst;
    private Bitmap src;

    public HighlightView(Context context, View[] guideViews) {
        super(context);
        this.guideViews = guideViews;

        // 绘制完成后初始化
        getViewTreeObserver().addOnGlobalLayoutListener(mListener);
    }

    private ViewTreeObserver.OnGlobalLayoutListener mListener = new ViewTreeObserver.OnGlobalLayoutListener() {
        @Override
        public void onGlobalLayout() {
            // 移除监听,只初始化一次
            getViewTreeObserver().removeOnGlobalLayoutListener(mListener);
            arrow = BitmapFactory.decodeResource(getResources(), R.drawable.highlight_arrow);
            dst = createDstBitmap(guideViews[0]);
            src = createSrcBitmap();
        }
    };

    @Override
    protected void onDraw(Canvas canvas) {
        if (guideViews == null || guideViews.length <= 0) {
            return;
        }
        //禁用硬件加速
        setLayerType(View.LAYER_TYPE_SOFTWARE, null);
        //使用离屏绘制
        int layerID = canvas.saveLayer(0, 0, getWidth(), getHeight(), null, Canvas.ALL_SAVE_FLAG);

        Paint paint = new Paint();
        canvas.drawBitmap(dst, 0, 0 ,paint);
        paint.setXfermode(new PorterDuffXfermode(PorterDuff.Mode.SRC_OUT));
        canvas.drawBitmap(src, 0, 0 ,paint);
        paint.setXfermode(null);
        canvas.drawBitmap(arrow, guideLocation[0] - arrow.getWidth(), guideLocation[3], paint);

        paint.setColor(0xFFFFFFFF);
        paint.setTextAlign(Paint.Align.CENTER);
        paint.setTextSize(40);
        canvas.drawText("点击扫描登录", guideLocation[0] - arrow.getWidth(),
                guideLocation[3] + arrow.getHeight() + 3 * padding, paint);

        canvas.restoreToCount(layerID);
    }

    @Override
    public boolean onTouchEvent(MotionEvent event) {
        if (event.getAction() == MotionEvent.ACTION_UP) {
            // 关闭高亮指引
            setVisibility(GONE);
        }
        return true;
    }

    public Bitmap createDstBitmap(View view) {
        Bitmap bitmap = Bitmap.createBitmap(getWidth(), getHeight(), Bitmap.Config.ARGB_8888);
        Canvas canvas = new Canvas(bitmap);
        Paint scrPaint = new Paint(Paint.ANTI_ALIAS_FLAG);
        scrPaint.setColor(0xFFFFFFFF);
        int[] location = new int[2];
        view.getLocationInWindow(location);
        int[] location2 = new int[2];
        getLocationInWindow(location2); // 用于计算状态栏的高度
        guideLocation = new int[4];
        guideLocation[0] = location[0] - padding;
        guideLocation[1] = location[1] - location2[1] - padding;
        guideLocation[2] = location[0] + view.getWidth() + padding;
        guideLocation[3] = location[1] - location2[1] + view.getHeight() + padding;
        canvas.drawOval(new RectF(guideLocation[0], guideLocation[1], guideLocation[2],
                guideLocation[3]),scrPaint);

        return bitmap;
    }

    public Bitmap createSrcBitmap() {
        Bitmap bitmap = Bitmap.createBitmap(getWidth(), getHeight(), Bitmap.Config.ARGB_8888);
        Canvas canvas = new Canvas(bitmap);
        Paint dstPaint = new Paint(Paint.ANTI_ALIAS_FLAG);
        dstPaint.setColor(0x99000000);
        canvas.drawRect(new Rect(0, 0, getWidth(), getHeight()), dstPaint);
        return bitmap;
    }
}

效果如下:
在这里插入图片描述

后期需要补充功能:

  • 版本号控制,只在某个版本号显示
  • 只显示一次
  • 高亮提示的view数组

参考:
Android高亮引导页的简单实现

发布了216 篇原创文章 · 获赞 91 · 访问量 25万+

猜你喜欢

转载自blog.csdn.net/yu75567218/article/details/88851508