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

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/xiaofeifei3/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/xiaofeifei3/queen

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值