寻找屏幕上的点击位置--Queen核心技术分享

来源:互联网 发布:大数据云计算培训 编辑:程序博客网 时间:2024/05/22 17:07

APP发布之后,发布者最关心的事情自然是用户使用APP的情况。换言之,他们希望回答一个问题:我的用户是怎么使用我的APP的?为了回答这个问题,最重要的步骤就是用户数据收集,因为用户不可能来到你的身边去展示他对APP的使用方法。那么只能让数据来告诉你答案。

Queen的工作便是跟踪用户对APP的使用过程,其中,识别用户的操作是Queen的核心内容。用户对APP的动作包括点击,滑动等等。识别动作的思路自然可以是从控件上下功夫,因为用户的操作的直接对象便是控件。那么对于一个点击,自然可以通过以下方法去采集数据。


button.setOnClickListener(new View.OnClickListener(){        onClick(View v){        //插入数据采集接口    }})

但是,一个APP可能有成百个控件,甚至一个页面就有好几个。统统这样插入接口工作量巨大,所以并不是一个最优的方法。Queen的采集过程来自Android点击信号传递方式。总所周知,在Android中最先感知点击事件的是rootView,之后通过onInterceptTouchEvent()接口向子View传递该信息,直到消费该点击事件的view为止。之后onTouchEvent将该事件从子View向父View传递。

这个工作原理为我们抓取点击事件提供了线索,这里由于rootView包含了整个View上的所有控件,那么对于一个点击事件,我们只需要知道点击位置,然后依据点击位置,从rootView开始一层层去查最顶上的,可点击的时间就可以了。


详细代码请参考 https://github.com/chenqihong3/queen。


Queen的解决方案是这样子:


/** * 识别在View上所进行的动作 * Get the operation on the view. * * @param ev 动作; Operation; * @param myView 执行动作的View; View on the screen; * @param context */public void recognizeViewEvent(MotionEvent ev, View myView, Context context){switch(ev.getAction()){    case MotionEvent.ACTION_DOWN:{    try{//... ...    final float pressX = ev.getRawX(); //点击位置X    final float pressY = ev.getRawY(); //点击位置Y    findViewAtPosition(myView, (int)pressX, (int)pressY);//找到点击的控件    if(mViewStack.isEmpty()){    return;    }    }catch(Exception e){Log.e(TAG, "recognizeViewEvent: unknown error");    }        break;    }    case MotionEvent.ACTION_UP:{    //... ...    final float x = ev.getRawX();    final float y = ev.getRawY();    findViewAtPosition(myView, (int)x, (int)y); //找到点击控件    try{if(view instanceof CheckBox){//收集控件信息}else if(view instanceof Button){    //收集控件信息    }else if(view instanceof ImageView){    //收集控件信息    }else if(view instanceof TextView){    //收集控件信息    }else{        }    }catch(Exception e){Log.e(TAG, "recognizeViewEvent: unknown error");    }    }    break;    }}

Queen第一步便是识别点击的动作ACTION_DOWN和ACTION_UP,只有两个动作所消费的对象是同一个控件时,此点击才能算是一次完整的点击,然后获取点击位置的x, y。第二步便是根据位置x,y查找相应的View,即findViewAtPosition(View, int, int)方法的调用,找到相应的View以后便进行View的控件类型判断,输出结果。接下来我们看看findViewAtPosition方法都做了一些什么:


/** * 通过用户动作的范围查找相应的View * find view that the user interacts. * * @param parent 最上层View * @param x 动作触摸点x坐标 * @param y 动作触摸点y坐标 */    private void findViewAtPosition(View parent, int x, int y) {    int length = 1;    Rect rect = new Rect();   parent.getGlobalVisibleRect(rect);    if(parent instanceof ViewGroup){    length = ((ViewGroup)parent).getChildCount();    }         for (int i = 0; i < length; i++) {              if(parent instanceof ViewGroup){                            if(View.VISIBLE == parent.getVisibility()){              View child = ((ViewGroup) parent).getChildAt(i);              findViewAtPosition(child, x, y);              }               } else {               if (rect.contains(x, y)) {                 if(View.VISIBLE == parent.getVisibility() && parent.isClickable()){               mViewStack.push(parent);               }               }             }                         }                  if(parent.isClickable()          && rect.contains(x, y)          && View.VISIBLE == parent.getVisibility()){         mViewStack.push(parent);         }    }

在这个方法中,从根View开始,我们一步步搜索被点击的View,首先确定点击范围rect,然后判断该View是不是ViewGroup,如果是,获取其中包含的子View并对每个子View进行迭代搜索,对于每个子View,我们确定该View是不是点击,同时确定该view是不是在rect点击范围,以及是否可见,最终将符合标准的View放入ViewStack。


通过以上两个方法,Queen一步一步找到用户点击的控件,并记录一条动作信息。这就是Queen寻找屏幕上被点击控件的方法。


详细代码请参考:https://github.com/chenqihong3/queen

1 0
原创粉丝点击