美文网首页
指示View

指示View

作者: 叶落清秋 | 来源:发表于2018-12-26 11:02 被阅读11次

    要实现的东西也比较简单。先来看看效果:

    一个简单的view

    效果看完了,看看是怎么实现的吧。
    首先,使用方面,只要一句话,是不是比较简单。

    GuideFun.makeFun(findViewById(R.id.tv),R.layout.view_guide).show();
    

    这种使用方式是不是很熟悉,对的 , 和SnackBar的使用方式相同,一起来看看SnackBar的简单使用:

     Snackbar.make(view, "已删除一个会话", Snackbar.LENGTH_SHORT).show();
    

    一起来看看GuideFun做了什么吧
    当调用makeFun方法时,会创建GuideFun对象,将值传给构造函数

    public GuideFun(View anchorView , int resId) {
        //1.渲染布局进来
        view = LayoutInflater.from(anchorView.getContext()).inflate(resId, null, false);
        GuideView guideView = (GuideView)view.findViewById(R.id.guideView);
        //2.找到FrameLayout id 为content
        parent = findSuitableParent(anchorView);
        guideView.bindViewGroup(parent);
    }
    

    构造函数中,最主要的是找个了一个id 为content的FrameLayout,来看看是怎么找到的:

    private static ViewGroup findSuitableParent(View view) {
        do {
            if (view instanceof FrameLayout) {
                //找到id为content的FrameLayout
                if (view.getId() == android.R.id.content) {
                    return (ViewGroup) view;
                }
            }
    
            if (view != null) {
                final ViewParent parent = view.getParent();
                view = parent instanceof View ? (View) parent : null;
            }
        } while (view != null);
    
        return null;
    }
    

    是的,这个方法是我从SnackBar源码中抽出来的方法,使用起来很方便。同时我们来看看为什么要找到这个id为content的上层FrameLayout。

    网上剪切来的图

    android的顶层view是DecorView,而我们在Activity中setContentView的布局都将加载到id为content的FrameLayout。所以,当我们在将guideView加到content中,就位于Activity的布局之上。这样,就没用什么可以阻挡我们了。

    我们要怎么来找到点击的位于guideView之下的那些控件呢?
    我们只需要根据触摸的x、y值,通过父类逐级向下遍历,如果子孩子不为ViewGroup,那么判断触摸点是否在他上面就可以了。

    //遍历viewGroup
    //view ,bindViewGroup传入的parent
    //rawX,rawY触摸点的x、y
    private View getPointViewInfo(View view,int rawX, int rawY){
        if(null == view) {
            return null;
        }
        if(view instanceof ViewGroup) {
            ViewGroup viewGroup = (ViewGroup) view;
            LinkedList<ViewGroup> queue = new LinkedList<>();
            queue.add(viewGroup);
            while(!queue.isEmpty()) {
                //取出并删除节点
                ViewGroup current = queue.removeFirst();
                for(int i = 0; i < current.getChildCount(); i ++) {
                    View child = current.getChildAt(i);
                    if(child instanceof ViewGroup) {
                        //加入链表末尾
                        queue.addLast((ViewGroup)child);
                        continue;
                    }
                    // View view;
                    if((child instanceof GuideView)){
                        //不能是自身
                        continue;
                    }
                    rect=calcViewScreenLocation(child);
                    if(rect.contains(rawX, rawY)){
                        //找到view,返回
                        return child;
                    }
                }
            }
        }
        return null;
    }
    

    返回view的边框矩形

    /**
    * 计算指定的 View 在屏幕中的坐标。
    */
    public static RectF calcViewScreenLocation(View view) {
        int[] location = new int[2];
        // 获取控件在屏幕中的位置,返回的数组分别为控件左顶点的 x、y 的值
        view.getLocationOnScreen(location);
        return new RectF(location[0], location[1], location[0] + view.getWidth(),location[1] + view.getHeight());
    }
    

    剩下的就是绘制和动画了,比较简单,有兴趣的可以看看[源码]: https://github.com/leaf-fade/GuideView,在这就不多提了。

    相关文章

      网友评论

          本文标题:指示View

          本文链接:https://www.haomeiwen.com/subject/hyajyxtx.html