android手势滑动的监听(层和层嵌套监听处理) 一

android手势滑动的监听(层跟层嵌套监听处理) 一
本人因为这块没有很好的静下来看过,所有以前在用的时候光拿别人的代码改来改去的很不理解这块,所有现在有空了想回过头来看看滑动这层跟层嵌套的滑动事件处理是怎么回事。

本文不是研究源码的,只是教大家怎么使用层跟层嵌套处理滑动事件。

建立一个新的空的android项目,我的ADT比较旧所以建出来的很多目录跟最新的不太一样,不过大家明白就可以了。
android手势滑动的监听(层和层嵌套监听处理) 一
然后建两个类,一个是RootLinearLayout.java一个是MyLinearLayout.java,然后修改main.xml文件如下:
<?xml version="1.0" encoding="utf-8"?>
<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
    android:layout_width="match_parent"
    android:layout_height="match_parent"
    android:orientation="vertical" android:background="@android:color/white">

    <com.iaiai.test.RootLinearLayout
	    android:layout_width="match_parent"
	    android:layout_height="match_parent"
	    android:orientation="vertical">
	    <com.iaiai.test.MyLinearLayout android:id="@+id/layout" android:background="#0000ff" android:clickable="true" android:layout_width="match_parent" android:layout_height="200dip" android:layout_margin="10dip">
	        
	    </com.iaiai.test.MyLinearLayout>
    </com.iaiai.test.RootLinearLayout>

</LinearLayout>


RootLinearLayout.java
package com.iaiai.test;

import android.content.Context;
import android.util.AttributeSet;
import android.util.Log;
import android.view.MotionEvent;
import android.widget.LinearLayout;

/** 
 *  
 * <br/> 
 * Title: RootLinearLayout.java<br/> 
 * E-Mail: 176291935@qq.com<br/> 
 * QQ: 176291935<br/> 
 * Http: iaiai.iteye.com<br/> 
 * Create time: 2014-10-28 上午9:59:24<br/> 
 * <br/> 
 * @author 丸子 
 * @version 0.0.1 
 */ 
public class RootLinearLayout extends LinearLayout {

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

	public RootLinearLayout(Context context) {
		super(context);
	}
	
	public boolean onTouchEvent(MotionEvent event) {
		Log.i("*****root","****手势滑动...");
		
		//如果这里返回true,则本类继续监听滑动事件,如果这里返回false,则本类不再监听滑动事件
        return true;  
    }

}

MyLinearLayout.java
package com.iaiai.test;

import android.content.Context;
import android.util.AttributeSet;
import android.util.Log;
import android.view.MotionEvent;
import android.widget.LinearLayout;

/** 
 *  
 * <br/> 
 * Title: RootLinearLayout.java<br/> 
 * E-Mail: 176291935@qq.com<br/> 
 * QQ: 176291935<br/> 
 * Http: iaiai.iteye.com<br/> 
 * Create time: 2014-10-28 上午9:59:24<br/> 
 * <br/> 
 * @author 丸子 
 * @version 0.0.1 
 */ 
public class MyLinearLayout extends LinearLayout {

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

	public MyLinearLayout(Context context) {
		super(context);
	}
	
	public boolean onTouchEvent(MotionEvent event) {
		Log.i("*****","****手势滑动...");

		//如果这里返回true,则本类继续监听滑动事件,如果这里返回false,则本类不再监听滑动事件
        return true;  
    }

}

然后运行代码:
android手势滑动的监听(层和层嵌套监听处理) 一
这时显示的白色布局中显示了一个蓝色布局,白色的位置为RootLinearLayout.java,蓝色的布局为MyLinearLayout.java

在白的的位置做滑动,显示为:
android手势滑动的监听(层和层嵌套监听处理) 一
在蓝色的布局位置滑动,显示为:
android手势滑动的监听(层和层嵌套监听处理) 一

在两个类中的onTouchEvent都是返回true,所以说在哪个层上滑动就处理哪个层的滑动事件。

把上面的MyLinearLayout.java代码改一下,改成onTouchEvent返回false,然后运行再在蓝色区域滑动如下:
android手势滑动的监听(层和层嵌套监听处理) 一
大家会看到,MyLinearLayout里只执行了一次,然后会把滑动事件给上层布局,所以大家会看到后面执行的RootLinearLayout中的输出。

如果再把RootLinearLayout中的onTouchEvent也改成false,那这两个布局都只会执行一次输出后就给上层处理。

再来计算蓝色层的横向和竖向滑动,
MyLinearLayout.java
package com.iaiai.test;

import android.content.Context;
import android.util.AttributeSet;
import android.util.Log;
import android.view.MotionEvent;
import android.widget.LinearLayout;

/** 
 *  
 * <br/> 
 * Title: RootLinearLayout.java<br/> 
 * E-Mail: 176291935@qq.com<br/> 
 * QQ: 176291935<br/> 
 * Http: iaiai.iteye.com<br/> 
 * Create time: 2014-10-28 上午9:59:24<br/> 
 * <br/> 
 * @author 丸子 
 * @version 0.0.1 
 */ 
public class MyLinearLayout extends LinearLayout {
	
	private float x=-1,mx=-1;
	private float y=-1,my=-1;
	
	private final static int moveDistance = 50;	//移动距离(按像素来算)
	
	/**
	 * 状态用于检测滑动是否符合规定距离,如果符合状态则为规定状态的滑动,方向相当于锁死,不再计算是横向滑动还是竖向滑动
	 */
	private boolean status = false;
	
	/**
	 * 方向,用于存储方向,false为横,true为竖
	 */
	private Direction touchDirection;
	
	private enum Direction{
		横向,竖向;
	}
	
	public MyLinearLayout(Context context, AttributeSet attrs) {
		super(context, attrs);
	}

	public MyLinearLayout(Context context) {
		super(context);
	}
	
	public boolean onTouchEvent(MotionEvent event) {
		switch(event.getAction()){
			case MotionEvent.ACTION_DOWN:
				actionDown(event);
				break;
			case MotionEvent.ACTION_MOVE:
				actionMove(event);
				break;
			case MotionEvent.ACTION_UP:
				actionUp(event);
				break;
		}
		
		if(touchDirection!=null&&touchDirection==Direction.竖向){
			return false;
		}
		
		//如果这里返回true,则本类继续监听滑动事件,如果这里返回false,则本类不再监听滑动事件
        return true;  
    }
	
	private void actionDown(MotionEvent event){
		x = event.getX();
		y = event.getY();
	}
	
	private void actionMove(MotionEvent event){
		mx = event.getX();
		my = event.getY();
		
		//先判断是否锁死方向
		if(!status){
			//先判断横向滑动
			if(Math.abs(x-mx)>moveDistance){
				status = true;
				touchDirection = Direction.横向;
			}
			
			//处理竖向滑动
			if(Math.abs(y-my)>moveDistance){
				if(!status){
					status = true;
					touchDirection = Direction.竖向;
				}
			}
		}else{
			if(touchDirection==Direction.横向){
				Log.i("****","****横向滑动...");
			}else if(touchDirection==Direction.竖向){
				Log.i("****","****竖向滑动...");
			}
		}
	}
	
	private void actionUp(MotionEvent event){
		//计算完之后把所有值初始化
		touchDirection = null;
		status = false;
		x = y = mx = my = -1;
	}

}

这里大家会注意到,如果说滑动某一个方向50个像素之后就会计算出是横向还是竖向,后面所有操作都是向这个方向操作,本来以为想得把竖向滑动操作扔给父层的RootLinearLayout去处理,但是运行结果缺不是我想像的那样,结果是竖向滑动50个像素之后即使onTouchEvent里返回了false也没有把操作扔给父类,为什么会这样?
查找资料得知道:
// true: 1.告诉Android,MotionEvent对象已被使用,不能再提供给其他方法。
// 2.还告诉Android,继续将此触摸序列的触摸事件(move,up)发送到此方法。
// false:1.告诉Android,onTouch()方法未使用该事件,所以Android寻找要调用的下一个方法。
// 2.告诉Android。不再将此触摸序列的触摸事件(move,up)发送到此方法。
public boolean onTouch(View v, MotionEvent event) {
...
}


看到这个“不能再提供给其他方法”,才知道,原来onTouchEvent方法第一次如果返回true即使之后再返回false也不管用,因为处理事件不会再给其它方法,看来这里原来隐藏着这么一个问题,最主要的是要注意第一次返回的是什么。

onTouchListener的onTouch方法优先级比onTouchEvent高,会先触发。