MotionEvent(一) 单点触摸和事件传递

当触摸android设备的屏幕时,android系统将创建一个MotionEvent对象。

MotionEvent对象是描述了一组和用户触摸相关的事件序列,它包含了触摸的位置,时间,动作等信息。

发生触摸时,这个MotionEvent对象会被传递到合适的方法中作为方法参数(通常是回调方法)。

getAction()方法描述了当前的动作:

ACTION_DOWN:手指按下     值:0
ACTION_MOVE:手指移动   值:2
ACTION_UP:手指弹起          值:1

像OnTouchListener的onTouch()回调方法就接受一个系统产生的MotionEvent,这类回调方法返回值为boolean,如果返回false,那么实现OnTouchListener的View会告诉android:我对此触摸的事件序列以及以后会发生的触摸事件序列都不感兴趣,请你寻找下一个感兴趣的View。android系统下次收到触摸事件不会将MotionEvent的实例发送给这个View,而是寻找下一个接受MotionEvent的View。前后2个MotionEvent是同一个对象,而不是新产生的(下面的例子会验证这个说法)

下面是Demo:

布局文件: activity_main.xml

<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
    xmlns:tools="http://schemas.android.com/tools"
    android:layout_width="match_parent"
    android:layout_height="match_parent"
    android:orientation="vertical"
    tools:context=".MainActivity" >
    
    <RelativeLayout 
        android:id="@+id/truelayout"
        android:layout_width="match_parent"
        android:layout_height="wrap_content"
        android:background="@android:color/black"
        android:layout_weight="1"
        android:tag="truelayout"
        >
        <com.example.mytouchdemo.TrueButton 
            android:id="@+id/truebutton"
            android:layout_width="wrap_content"
            android:layout_height="wrap_content"
            android:textColor="@android:color/white"
            android:tag="truebutton"
            android:text="true"
            />
        
        <com.example.mytouchdemo.FalseButton
            android:id="@+id/falsebutton"
            android:layout_below="@+id/truebutton"
            android:layout_width="wrap_content"
            android:layout_height="wrap_content"
            android:textColor="@android:color/white"
            android:tag="falsebutton"
            android:text="false"
            />
    </RelativeLayout>
    <RelativeLayout 
        android:id="@+id/falselayout"
        android:layout_width="match_parent"
        android:layout_height="wrap_content"
        android:layout_weight="1"
        android:tag="falselayout"
        android:background="@android:color/darker_gray"
        >
        <com.example.mytouchdemo.TrueButton
            android:id="@+id/falsebutton2"
            android:layout_width="wrap_content"
            android:layout_height="wrap_content"
            android:textColor="@android:color/white"
            android:tag="falsebutton2"
            android:text="false"
            />
    </RelativeLayout>

</LinearLayout>


TrueButton和FalseButton都是自定义控件,继承了BooleanButton,BooleanButton继承了Button

BooleanButton的onTouchEvent回调在接收了MotionEvent对象,返回值取决于是trueButton.还是falseButton..

public abstract class BooleanButton extends Button {
    protected boolean myValue() {
    	return false;
    }

	public BooleanButton(Context context, AttributeSet attrs) {
		super(context, attrs);
	}
	
	@Override
	public boolean onTouchEvent(MotionEvent event) {
		Log.v(tag, "------"+ getTag() +"--------------");
        Log.v(tag, "action:" + event.getAction());
        Log.v(tag, "and I'm returning " + myValue());
        Log.v(tag, "MotionEvent's hashcode="+event.hashCode()+"");
        return(myValue());
	}
	
	private String tag = "test";
}

public class TrueButton extends BooleanButton {
    protected boolean myValue() {
    	return true;
    }
    
	public TrueButton(Context context, AttributeSet attrs) {
		super(context, attrs);
	}
}

public class FalseButton extends BooleanButton {

	public FalseButton(Context context, AttributeSet attrs) {
		super(context, attrs);
	}
}

MainActivity.java

public class MainActivity extends Activity implements OnTouchListener{

	@Override
	protected void onCreate(Bundle savedInstanceState) {
		super.onCreate(savedInstanceState);
		setContentView(R.layout.activity_main);
		//上半部分布局,ontouch方法返回true
		RelativeLayout trueLayout = (RelativeLayout)findViewById(R.id.truelayout);
		//true按钮,ontouch方法和自身的onTouchEvent方法返回true
		Button trueButton = (Button)findViewById(R.id.truebutton);
		//false按钮,ontouch方法和自身的onTouchEvent方法返回false
		Button falseButton = (Button)findViewById(R.id.falsebutton);
		Button falseButton2 = (Button)findViewById(R.id.falsebutton2);
		//下半部分布局,ontouch方法返回false
		RelativeLayout falseLayout = (RelativeLayout)findViewById(R.id.falselayout);
		
		//注册ontouch事件
		trueLayout.setOnTouchListener(this);
		falseLayout.setOnTouchListener(this);
		trueButton.setOnTouchListener(this);
		falseButton.setOnTouchListener(this);
		falseButton2.setOnTouchListener(this);
	}

	@Override
	public boolean onTouch(View v, MotionEvent event) {
		String tagStr = v.getTag().toString().substring(0, 4);
		//tag前4位数为true的控件,onTouch返回true,否则返回false
		if ("true".equals(tagStr)) {
			Log.v(tag, "------"+ v.getTag() +"--------------");
	        Log.v(tag, "action:" + event.getAction());
	        Log.v(tag, "and I'm returning " + tagStr);
	        Log.v(tag, "MotionEvent's hashcode="+event.hashCode()+"");
			return true;
		} else {
			return false;
		}
	}
	
	String tag = "test";
	
}

现在启动程序,点击上半布局的TrueButton,检查logcat输出:

06-17 22:26:31.662: V/test(5766): ------truebutton--------------
06-17 22:26:31.662: V/test(5766): action:0
06-17 22:26:31.662: V/test(5766): and I'm returning true
06-17 22:26:31.662: V/test(5766): MotionEvent's hashcode=1097894832
06-17 22:26:31.682: V/test(5766): ------truebutton--------------
06-17 22:26:31.682: V/test(5766): action:2
06-17 22:26:31.682: V/test(5766): and I'm returning true
06-17 22:26:31.682: V/test(5766): MotionEvent's hashcode=1097894832
06-17 22:26:31.702: V/test(5766): ------truebutton--------------
06-17 22:26:31.702: V/test(5766): action:2
06-17 22:26:31.702: V/test(5766): and I'm returning true
06-17 22:26:31.702: V/test(5766): MotionEvent's hashcode=1097894832
06-17 22:26:31.712: V/test(5766): ------truebutton--------------
06-17 22:26:31.712: V/test(5766): action:1
06-17 22:26:31.712: V/test(5766): and I'm returning true
06-17 22:26:31.712: V/test(5766): MotionEvent's hashcode=1097894832

可以看到所有的事件都在truebutton中被接收了,包括按下action:0,移动action:2,弹起action:1。这是因为onTouch方法中返回了true。

现在点击上半布局的FalseButton,检查logcat输出:

06-17 22:29:06.892: V/test(5766): ------falsebutton--------------
06-17 22:29:06.892: V/test(5766): action:0
06-17 22:29:06.892: V/test(5766): and I'm returning false
06-17 22:29:06.892: V/test(5766): MotionEvent's hashcode=1097894832
06-17 22:29:06.892: V/test(5766): ------truelayout--------------
06-17 22:29:06.892: V/test(5766): action:0
06-17 22:29:06.902: V/test(5766): and I'm returning true
06-17 22:29:06.902: V/test(5766): MotionEvent's hashcode=1097894832
06-17 22:29:06.912: V/test(5766): ------truelayout--------------
06-17 22:29:06.912: V/test(5766): action:2
06-17 22:29:06.912: V/test(5766): and I'm returning true
06-17 22:29:06.912: V/test(5766): MotionEvent's hashcode=1097894832
06-17 22:29:06.932: V/test(5766): ------truelayout--------------
06-17 22:29:06.932: V/test(5766): action:2
06-17 22:29:06.932: V/test(5766): and I'm returning true
06-17 22:29:06.932: V/test(5766): MotionEvent's hashcode=1097894832
06-17 22:29:06.952: V/test(5766): ------truelayout--------------
06-17 22:29:06.952: V/test(5766): action:2
06-17 22:29:06.952: V/test(5766): and I'm returning true
06-17 22:29:06.952: V/test(5766): MotionEvent's hashcode=1097894832
06-17 22:29:06.972: V/test(5766): ------truelayout--------------
06-17 22:29:06.972: V/test(5766): action:1
06-17 22:29:06.972: V/test(5766): and I'm returning true
06-17 22:29:06.972: V/test(5766): MotionEvent's hashcode=1097894832

看到falseButton和包含它的布局truelayout都接收了按下事件,但是之后的移动,弹起都只有truelayout接收。因为falseButton在onTouch中返回false,这表明它不再接收之后的触摸时间,交由其他View处理,所以android系统找到了truelayout,因为truelayout的onTouch返回true,所以它处理所有的触摸事件序列。并且注意到,2次接受的motionEvent的hashcode都是一样的,说明他们是一个motionevent

下面点击下半个布局的falsebutton,logcat输出:

06-17 22:36:57.942: V/test(7164): ------falsebutton2--------------
06-17 22:36:57.942: V/test(7164): action:0
06-17 22:36:57.942: V/test(7164): and I'm returning false
06-17 22:36:57.942: V/test(7164): MotionEvent's hashcode=1098095696

可以看到非常悲剧,因为包含它的布局falselayout返回的也是false,现在没有view可以接收motionevent对象,所以不能监听到移动和弹起的事件


下面看一个特殊的情况:按住上半部分的trueButton并且移动手指一直到移出button外,移到relativelayout上。看log:

06-17 22:39:41.442: V/test(7164): ------truebutton--------------
06-17 22:39:41.442: V/test(7164): action:0
06-17 22:39:41.442: V/test(7164): and I'm returning true
06-17 22:39:41.442: V/test(7164): MotionEvent's hashcode=1098095696
06-17 22:39:41.462: V/test(7164): ------truebutton--------------
06-17 22:39:41.462: V/test(7164): action:2
06-17 22:39:41.462: V/test(7164): and I'm returning true
06-17 22:39:41.462: V/test(7164): MotionEvent's hashcode=1098095696

………………………………………………

你会发现就算出了button的范围它依然监听触摸事件,这是因为它返回true,代表这一系列触摸动作都由我来处理,这也是“触摸序列”的含义

补充:注意如果MainActivity中onTouch方法返回true,则layout或者Button本身的onTouchEvent都不会被调用,因为触摸事件已经被onTouch处理。

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 1
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值