Pro Android学习笔记(一四二):触摸屏(1):MotionEvents

时间:2021-10-01 00:03:39

文章转载只能用于非商业性质,且不能带有虚拟货币、积分、注册等附加条件。转载须注明出处http://blog.csdn.net/flowingflying/以及作者@恺风Wei

MotionEvent对象用于告诉APP用户正在如何使用触摸屏幕。我们可以为view设置触摸回调函数,而view本身也带有触摸回调函数。

为view设置触摸回调函数onTouch()

public class MainActivity extends Activity implements OnTouchListener{
   @Override
    protected void onCreate(Bundle savedInstanceState) { 
        ……            
        RelativeLayout layout1 = (RelativeLayout)findViewById(R.id.layout1);
        layout1.setOnTouchListener(this);         
        Button trueButton1 = (Button)findViewById(R.id.trueButton1);
        trueButton1.setOnTouchListener(this);       
    }

    @Override //onTouchListener的回调函数onTouch()
    public boolean onTouch(View v, MotionEvent event)
{
        String tag = v.getTag().toString();  //在layout xml中设置android:tag属性,可通过getTag()获取
        Log.i(tag,"Got view " + v.getTag().toString() + " in touch");
        Log.i(tag,MainActivity.describeEvent(v, event));
        return super.onTouchEvent(event);

    }   
}

自定义view,重写触摸回调函数onTouchEvent()

public class MyButton extends Button{
    protected boolean myValue = false; 
    
    public BooleanButton(Context context, AttributeSet as){
        super(context,as);
    }

    @Override //对TextView的onTouchEvent()重写
    public boolean onTouchEvent(MotionEvent event) {
 
        String tag = this.getTag().toString(); 
        Log.d(tag,MainActivity.describeEvent(this, event)); 
        Log.d(tag,"Now return " + myValue);         
        return myValue;
    } 
   
}

我们可以在layout xml中使用自定义的MyButton,方式如下:

<cn.wei.flowingflying.testtouchscreen.MyButton android:id="@+id/trueButton1"
            android:text="Return true"
            android:layout_width="wrap_content"
            android:layout_height="wrap_content"
            android:tag="trueButtonTop" />

MotionEvent可获取的信息

当用户触摸屏幕是,会生成一个MotionEvent对象,含有触摸的信息,应用可以获得这个对象,例如自定义控件中的onTouchEvent()。MotionEvent可以获得很多信息,包括动作类型,如ACTION_DOWN等,触摸的位置,触摸相应的时间,触摸的压力,触摸的大小。前面的小例子中通过静态函数MainActivity.describeEvent(v, event)来显示详细信息,下面是相关代码:

protected static String describeEvent(View view, MotionEvent event) {
    String result = "Action: " + getActionName(event.getAction()) + "\n"
            + "Location: " + event.getX() + "," + event.getY() + "\n"; // X,Y是距离view左上角的值
    
    if(event.getX() < 0 || event.getX() > view.getWidth() || event.getY() < 0 || event.getY() > view.getHeight()){
        result += ">>> Touch has left the view <<<\n" ;
    }
    /*【1】getEdgeFlags()获取是否触摸到触屏的边框,但是这个可能永远返回0,例如模拟器和某些手机。在编程中,我们不能依赖这个值,可以自行通过位置计算,通过setEdgeFlage()来设定flags。
     *【2】压力和size的范围是0-1,每款设备的检测值有差异,每个绝对值并没有具体的衡量,很难说0.8以上的就是压力大,某款设备可能检测值不会有0.8以上,所有这些数值最好是自己和自己进行比较,获知轻重的变化。在某款手机上,size的值一直为0。*/ 
    result += "Edge Flag: " + event.getEdgeFlags() + "\n"
            + "Pressure : " + event.getPressure() + "\n"
            + "Size : " + event.getSize() + "\n"  
            + "Down time :" + event.getDownTime() + "ms\n"
            + "Event time :" + event.getEventTime() + "ms\n"
            + "Eclapsed: " + (event.getEventTime() - event.getDownTime()) + "ms\n";
   
    return result;
}

private static String getActionName(int code){     
    if(code == MotionEvent.ACTION_DOWN)
        return "ACTION_DOWN";
   
    if(code == MotionEvent.ACTION_MOVE)
        return "ACTION_MOVE";     
    ……    
    return ""+code;
}

当手指在某个view中按下,移动,离开,将会依次触发ACTION_DOWN,ACTION_MOVE(0~N个)和ACTION_UP。从DOWN开始,到UP结束,这是一组动作,event.getDownTime()给出的时间是一样的,我们可以通过当前时间event.getEventTime()进行比对,获得动作的持续时间。

除了这三个常见动作事件外,还有ACTION_CANCEL和ACTION_OUTSIDE。

触发的逻辑

Pro Android学习笔记(一四二):触摸屏(1):MotionEvents

用户的一个动作实际是可以分解为若干小动作处理,这一系列动作,从Down开始,在MotionEvent中具有getDownTime(),一般以Up结束。每个事件发生时,可能会有若干个回调函数,这些回调函数是有缺省的顺序,如果回调函数返回false,将触发事件传递到下一个回调函数进行处理,如果回调函数返回true,说明已经全部处理完,不在继续向下传递。这里比较特别的是,如果是最后一个回调函数,如果返回false,则下一个动作不会触发,因此,如果我们明确已经全部处理完,则返回true,否则返回super处理方法的值。

例子说明

以我们之前的代码为例,有一个自定义的button在RelativeLayout上,通过setTouchListener()为这个view都设置了触摸监听器,这个自定义的button,重写了onTouchEvent()。用户按button,触发的顺序如下:

Pro Android学习笔记(一四二):触摸屏(1):MotionEvents

通过setTouchListener()注册的监听器优先于view的onTouchEvent()。如果view在某个view group中,view的触摸触发优先于view group的触摸触发。

如果传递到RelativeLayout的onTouch(),我们看到Event的Y坐标有变化,因为Layout的左上角和Button的左上角位置不同。

如果我们在手指Button出按下,然后滑出Button,然后离开屏幕,这属于一个动作。我们会看到ACTION_DOWN,然后很多ACTION_MOVE,以及最后的ACTION_UP。在这个连续动作中,即使我们的手指已经滑出Button,我们仍可以在Button.onTouch()和Button.onTouchEvent()收到MotionEvent。通过检查具体的X、Y值,可判断是否离开view。无论手指滑到哪里,ACTION_DOWN顺序触发的各回调方法,是接下来这动作组的其他动作触发的顺序。例如我们有两个RelativeLayout,手指从其中要给滑到另一个,因为一开始ACTION_DOWN是在第一个RelativeLayout,所以只会触发到第一个RelativeLayout的回调函数,不会触发到第二个RelativeLayout的触屏回调函数。

一般而言,如果自定义view,我们返回super.onTouchEvent()即可。例如自定义Button,实际上Button接收到DOWN的时候,会改变Button的颜色,而UP的时候,再次变回来,因此需要提供Button缺省的处理。如果,我们确定不将其继续往下传递,可以在return true;之前增加super.onTouchEvent()的处理。如果我们在Button onTouch()就返回true,我们是不会看到Button的颜色有变化,当然我们可以添加view.onTouchEvent(event)来处理,或直接return view.onTouchEvent(event)。

安装会从AndroidManifest中获知信息,如果应用必须要求支持触摸,我们以加上说明。一些运行Android的机顶盒以TV作为屏幕,而TV是不支持触摸屏。

<uses-configuration android:reqTouchScreen="finger" />

android:onClick和触屏回调函数的关系

我们可以在layout.xml文件中对Button控件加入android:onClick=“doClick”,来跟踪按Button的情况。doClick()将在Button的ACTION_UP事件后触发,但是前提是:1、能收到ACTION_UP事件,而且范围在Button内(不滑出);2、能执行button.onTouchEvent(),如果我们重写该方法,要确保执行super.onTouchEvent();

相关小例子代码:Pro Android学习:touch screen小例子

 

相关链接:我的Android开发相关文章