自定义高亮区域 作用户引导的思路

先显示简易的效果

网上看了一些引导 发现自定义文本 和箭头指向都不够方便,就写了比较符合自己习惯的

//内容更新
视觉优化 将下面代码分别添加到两个addView之后

  ObjectAnimator.ofFloat(高亮的view, "scaleY", 0.5f, 1.0f).setDuration(800).start();

  ObjectAnimator.ofFloat(显示文本, "scaleX", 0.5f, 1.0f).setDuration(800).start();
  ObjectAnimator.ofFloat(显示文本, "scaleY", 0.5f, 1.0f).setDuration(800).start();

效果如下
这里写图片描述

相关Module下载

1.已经实现的有3种简易功能

一种是连接两个高亮的view 提示语句在线(View)的中间
二是指向某一条文件
三是多个高亮区域的连接

核心思路就是遮盖一层。我选择直接放在根布局下面。当然你想 直接动态添加的方式也行。目前只是探讨一下思路。
通过传入的view` Region region = new Region(new Region(0, 0, getWidth(), getHeight()));

    for (int i = 0; i < rects.size(); i++) {
        Region region0 = new Region(rects.get(i));
        region.op(region0, Region.Op.DIFFERENCE);
    }

    RegionIterator iterator = new RegionIterator(region);
    Rect rectNew = new Rect();
    while (iterator.next(rectNew)) {
        canvas.drawRect(rectNew, paint);
    }`

扣出高亮区域然后连线添加文本 剩下的就是具体坐标和角度的计算。


    @Override
    protected void dispatchDraw(Canvas canvas) {
        //核心顺序
        if (rects.size() != 0) {
            drawRect(canvas);//绘制高亮区域
            super.dispatchDraw(canvas);//绘制子view -->如提示文字 箭头
        }
    }

为了大家方便自定义自己的 指向箭头。我没有画这条线 而是留了一个简易的view。可以用自己ui设计的箭头去替换 那么你可能就需要加上 图片宽度带来的影响

使用方法简易示例

  //盖在根布局最上层 可指定其他任意层级的view高亮 链接 提示
        gcv = (GuideCoverView) findViewById(R.id.gcv);
        //后期可改进为 动态添加


//        gcv.doubleViewContact(view, view2, 0, "这是一个提示??", false);


        gcv.setOnClickListener(new View.OnClickListener() {
            @Override
            public void onClick(View v) {
                Log.i("rex", "v");

                int c = index++ % 5;
                //第三处参数为 链接线的 自定义view 此处仅传图片资源id 传0即默认黄线 如需要其他自定义或者网络图片 可以去内部自定义addArr
                if (c == 0) {
                    gcv.doubleViewContact(view, view2, 0, "这是一个提示~~", false);
                } else if (c == 1) {
                    gcv.doubleViewContact(view, view3, 0, "这又是一个提示??", false);
                } else if (c == 2) {
                    gcv.doubleViewContact(view2, view3, 0, "这又他妈是一个提示??", false);
                } else if (c == 3) {
                    gcv.doubleViewContact(view, view4, 0, "熄火区域", true);
                } else {
                    //第二种多连接示例
                    gcv.manyViewContact(
                            asList(view, view2, view2, view3, view3, view4, view4,view),
                            asList("多连接示例1", "多连接示例2", "多连接示例3", "多连接示例4"),
                            asList(false, false, false, true)
                    );
                }
            }
        });

核心自定义遮盖引导层

package com.rex;

import android.animation.ObjectAnimator;
import android.animation.PropertyValuesHolder;
import android.content.Context;
import android.graphics.Canvas;
import android.graphics.Color;
import android.graphics.Paint;
import android.graphics.Rect;
import android.graphics.Region;
import android.graphics.RegionIterator;
import android.os.Build;
import android.util.AttributeSet;
import android.util.Log;
import android.view.Gravity;
import android.view.View;
import android.view.ViewTreeObserver;
import android.widget.FrameLayout;
import android.widget.ImageView;
import android.widget.TextView;

import org.w3c.dom.Text;

import java.util.ArrayList;
import java.util.List;

/**
 * Created by  Rex on 2017/1/4.
 * 比较开放性便于自定义文字 联系线的高亮引导容器
 */

public class GuideCoverView extends FrameLayout {

    private List<Rect> rects = new ArrayList<>();
    private Paint paint;
    private int padding = 8;
    private int index;
    private View lastView;
    private int statusBarHeight;
    private boolean isOnlyOneViewToText;
    private int mResourId = 0;
    private Canvas mCanvas;

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

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

    public GuideCoverView(Context context, AttributeSet attrs, int defStyleAttr) {
        super(context, attrs, defStyleAttr);
        init();
    }


    private void init() {

        statusBarHeight = getStatusBarHeight();
        paint = new Paint();
        paint.setColor(Color.parseColor("#8c000000"));

    }

    public View addArr(int w, int h) {
        FrameLayout.LayoutParams lp = new FrameLayout.LayoutParams(w, h);
        View view = new View(getContext());
        if (mResourId > 0)
            view.setBackgroundResource(mResourId);
        else
            view.setBackgroundColor(Color.GREEN);

        addView(view, lp);
        return view;
    }

    /**
     * @param viewC1
     * @param viewC2
     * @param id     这里示例为 两个view高亮  自定义图片作为连接示意   线中心作为 提示语
     * @param isText 是否为单高亮 末端指向文字   此时另一个view决定了文本显示的位置
     */
    public void doubleViewContact(final View viewC1, final View viewC2, final int id, final String msg, final boolean isText) {
        clear();
        HighlightView(viewC1, msg, isText);
        HighlightView(viewC2, msg, isText);
        invalidate();


    }

    public void manyViewContact(ArrayList<View> views, ArrayList<String> msg, ArrayList<Boolean> isText) {
        clear();


        for (int i = 0; i < views.size(); i++) {
            if (i + 1 >= views.size())
                return;
            int j = i / 2;
            Log.i("rex", "j-->" + j + "msg-->" + msg.get(j) + "boolean--->" + isText.get(j));
            HighlightView(views.get(i), msg.get(j), isText.get(j));
            ++i;
            HighlightView(views.get(i), msg.get(j), isText.get(j));
            invalidate();
        }

    }


    @Override
    protected void dispatchDraw(Canvas canvas) {
        //核心顺序
        if (rects.size() != 0) {
            drawRect(canvas);//绘制高亮区域
            super.dispatchDraw(canvas);//绘制子view -->如提示文字 箭头
        }
    }

    @Override
    protected void onDraw(Canvas canvas) {
        super.onDraw(canvas);

    }

    public void clear() {
        rects.clear();
        lastView = null;
        removeAllViews();
        invalidate();
    }

    private void drawRect(Canvas canvas) {

        Region region = new Region(new Region(0, 0, getWidth(), getHeight()));


        for (int i = 0; i < rects.size(); i++) {
            Region region0 = new Region(rects.get(i));
            region.op(region0, Region.Op.DIFFERENCE);
        }

        RegionIterator iterator = new RegionIterator(region);
        Rect rectNew = new Rect();
        while (iterator.next(rectNew)) {
            canvas.drawRect(rectNew, paint);
        }


    }

    public void HighlightView(final View viewH, final String msg, final boolean isText) {
        ViewTreeObserver vto = viewH.getViewTreeObserver();
        vto.addOnGlobalLayoutListener(new ViewTreeObserver.OnGlobalLayoutListener() {
            @Override
            public void onGlobalLayout() {
                //监听一次马上结束
                if (Build.VERSION.SDK_INT < 16) {
                    viewH.getViewTreeObserver().removeGlobalOnLayoutListener(this);
                } else {
                    viewH.getViewTreeObserver().removeOnGlobalLayoutListener(this);
                }
                int[] locationXy = getLocationXy(viewH);

                Rect rect = new Rect(locationXy[0] - padding,
                        locationXy[1] - statusBarHeight - padding,
                        locationXy[0] + viewH.getMeasuredWidth() + padding,
                        locationXy[1] + viewH.getMeasuredHeight() - statusBarHeight + padding);


                if (lastView == null) {
                    rects.add(rect);
                    lastView = viewH;

                } else {
                    if (!isText) {
                        rects.add(rect);//是否高亮
                    }
                    ContactDouble(lastView, viewH, msg, isText);
                    lastView = null;
                }
            }
        });
    }

    /**
     * 高亮view指向文本
     *
     * @param view
     */
    private void ContactText(View view) {

    }

    /**
     * 用自定义的view连接两个
     *
     * @param viewC1
     * @param viewC2
     * @param isText
     */
    private void ContactDouble(View viewC1, View viewC2, final String msg, final boolean isText) {
        int[] locationXy = getLocationXy(viewC1);
        int[] locationXy2 = getLocationXy(viewC2);
        final int x1 = locationXy[0] + viewC1.getWidth() / 2 - 2;
        final int x2 = locationXy2[0] + viewC2.getWidth() / 2;
        final int y1 = locationXy[1];
        final int y2 = locationXy2[1];
        final int lenth = (int) Math.sqrt((x1 - x2) * (x1 - x2) + (y1 - y2) * (y1 - y2)) - 20 * padding;


        final View view = addArr(3, lenth);


        float tan = (x1 - x2) * 1.0f / (y1 - y2);
        final float du = -(float) Math.toDegrees(Math.atan(tan));

        ViewTreeObserver vto = view.getViewTreeObserver();
        vto.addOnGlobalLayoutListener(new ViewTreeObserver.OnGlobalLayoutListener() {
            @Override
            public void onGlobalLayout() {
                //监听一次马上结束
                if (Build.VERSION.SDK_INT < 16) {
                    view.getViewTreeObserver().removeGlobalOnLayoutListener(this);
                } else {
                    view.getViewTreeObserver().removeOnGlobalLayoutListener(this);
                }

                final int centerX = (x1 + x2) / 2 - 4 * padding;
                final int centerY = (y1 + y2) / 2 - view.getHeight() / 2 - 4 * padding;
                view.setX(centerX);
                view.setY(centerY);
                view.setRotation(du);


                FrameLayout.LayoutParams params = new FrameLayout.LayoutParams(LayoutParams.WRAP_CONTENT, LayoutParams.WRAP_CONTENT);
                final TextView tv = new TextView(getContext());
                tv.setText(msg);
                tv.setGravity(Gravity.CENTER);
                if (isText) {
                    //线顶端
                    MeasureWH(tv, new measureOk() {
                        @Override
                        public void ok(View view, int w, int h) {
                            view.setX(centerX + w / 2);
                            view.setY(centerY);

                        }
                    });


                } else {
                    //线中间
                    tv.setX((x1 + x2) / 2 + 2 * padding);
                    tv.setY((y1 + y2) / 2 + 2 * padding);
                }
                tv.setTextColor(Color.WHITE);
                addView(tv, params);


            }
        });


    }

    /**
     * 获取屏幕的坐标(包括状态栏等)
     */
    public int[] getLocationXy(View viewL) {
        int[] lXy = new int[2];
        viewL.getLocationOnScreen(lXy);
        LogI("getLocationXy", lXy.toString());
        return lXy;
    }


    /**
     * 监听绘制完成后测量宽高
     */
    public interface measureOk {
        void ok(View view, int w, int h);
    }

    public static void MeasureWH(final View viewL, final measureOk impl) {
        ViewTreeObserver vto = viewL.getViewTreeObserver();
        vto.addOnGlobalLayoutListener(new ViewTreeObserver.OnGlobalLayoutListener() {
            @Override
            public void onGlobalLayout() {
                //监听一次马上结束
                if (Build.VERSION.SDK_INT < 16) {
                    viewL.getViewTreeObserver().removeGlobalOnLayoutListener(this);
                } else {
                    viewL.getViewTreeObserver().removeOnGlobalLayoutListener(this);
                }
                if (impl != null) {
                    impl.ok(viewL, viewL.getWidth(), viewL.getHeight());
                }

            }
        });


    }

    private void LogI(String msg1, String msg2) {
        LogI(msg1 + " ---- >" + msg2);
    }

    private void LogI(String msg) {
        Log.i("rex", msg);
    }

    public int getStatusBarHeight() {
        int result = 0;
        int resourceId = getResources().getIdentifier("status_bar_height", "dimen", "android");
        if (resourceId > 0) {
            result = getResources().getDimensionPixelSize(resourceId);
        }
        return result;
    }
}
  • 1
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

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

余额充值