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

效果看完了,看看是怎么实现的吧。
首先,使用方面,只要一句话,是不是比较简单。
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,在这就不多提了。
网友评论