android自定义控件系列教程-----touch事件的传递

前沿:

很久没有写过博客了,因为工作的原因很少有时间写东西了,最近想写一个UI系列的博客,因为我发现这一系列的都很少,而且没有那么系统,这里我想以我自己的观点来阐述一下如何自定义android 控件系列。

自定义控件阐述:

在我的理解里面自定义控件,需要了解到touch事件的传递、分发、拦截机制,Scroller类的运用,andorid 视图的理解,ViewGroup的熟悉,因为我们绝大多的控件都是继承自ViewGroup,还有就是要学会布局测量等。

Touch事件的传递

首先我们要了解在android系统里面有几个地方会走touch事件,这个是老生常谈的问题了,但是我还是希望写一下这个问题,因为温故而知新嘛,我们首先得知道VIew类这种不能作为容器的类只会有这两个函数:
	@Override
	public boolean dispatchTouchEvent(MotionEvent event) {
		// TODO Auto-generated method stub
		return super.dispatchTouchEvent(event);
	}

	@Override
	public boolean onTouchEvent(MotionEvent event) {
		// TODO Auto-generated method stub
		return super.onTouchEvent(event);
	}
而能做为容器的类如ViewGroup以及继承它的类会有这几个函数:
@Override
	public boolean onInterceptTouchEvent(MotionEvent ev) {
		// TODO Auto-generated method stub
		return super.onInterceptTouchEvent(ev);
	}

	@Override
	public boolean onTouchEvent(MotionEvent event) {
		// TODO Auto-generated method stub
		return super.onTouchEvent(event);
	}

	@Override
	public boolean dispatchTouchEvent(MotionEvent ev) {
		// TODO Auto-generated method stub
		return super.dispatchTouchEvent(ev);
	}
现在我们就分别来重写这几个函数分别走一遍流程,让我们更能清楚的看到android的touch事件是怎么传递的。我们来重写一下这两类的方法。重写一个TextView
public class MyTextView extends TextView{
	private final String TAG = MyTextView.class.getSimpleName();

	public MyTextView(Context context) {
		super(context);
	}

	@Override
	public boolean dispatchTouchEvent(MotionEvent event) {
		switch (event.getAction()) {
		case MotionEvent.ACTION_DOWN:
			Log.d(TAG, TAG + "dispatchTouchEvent+ACTION_DOWN");
			break;
		case MotionEvent.ACTION_POINTER_DOWN:
			Log.d(TAG, TAG + "dispatchTouchEvent+ACTION_POINTER_DOWN");
			break;
		case MotionEvent.ACTION_POINTER_UP:
			Log.d(TAG, TAG + "dispatchTouchEvent+ACTION_POINTER_UP");
			break;
		case MotionEvent.ACTION_MOVE:
			Log.d(TAG, TAG + "dispatchTouchEvent+ACTION_MOVE");
			break;
		case MotionEvent.ACTION_UP:
			Log.d(TAG, TAG + "dispatchTouchEvent+ACTION_UP");
			break;
		}
		return super.dispatchTouchEvent(event);
	}

	@Override
	public boolean onTouchEvent(MotionEvent event) {
		switch (event.getAction()) {
		case MotionEvent.ACTION_DOWN:
			Log.d(TAG, TAG + "onTouchEvent+ACTION_DOWN");
			break;
		case MotionEvent.ACTION_POINTER_DOWN:
			Log.d(TAG, TAG + "onTouchEvent+ACTION_POINTER_DOWN");
			break;
		case MotionEvent.ACTION_POINTER_UP:
			Log.d(TAG, TAG + "onTouchEvent+ACTION_POINTER_UP");
			break;
		case MotionEvent.ACTION_MOVE:
			Log.d(TAG, TAG + "onTouchEvent+ACTION_MOVE");
			break;
		case MotionEvent.ACTION_UP:
			Log.d(TAG, TAG + "onTouchEvent+ACTION_UP");
			break;
		}
		return super.onTouchEvent(event);
	}
}
然后我们再重写一个LinearLayout
public class MyLinearLayout extends LinearLayout {
	private final String TAG = MyLinearLayout.class.getSimpleName();

	public MyLinearLayout(Context context) {
		super(context);
	}

	@Override
	public boolean onInterceptTouchEvent(MotionEvent ev) {
		switch (ev.getAction()) {
		case MotionEvent.ACTION_DOWN:
			Log.d(TAG, TAG + "onInterceptTouchEvent+ACTION_DOWN");
			break;
		case MotionEvent.ACTION_POINTER_DOWN:
			Log.d(TAG, TAG + "onInterceptTouchEvent+ACTION_POINTER_DOWN");
			break;
		case MotionEvent.ACTION_POINTER_UP:
			Log.d(TAG, TAG + "onInterceptTouchEvent+ACTION_POINTER_UP");
			break;
		case MotionEvent.ACTION_MOVE:
			Log.d(TAG, TAG + "onInterceptTouchEvent+ACTION_MOVE");
			break;
		case MotionEvent.ACTION_UP:
			Log.d(TAG, TAG + "onInterceptTouchEvent+ACTION_UP");
			break;
		}
		return super.onInterceptTouchEvent(ev);
	}

	@Override
	public boolean onTouchEvent(MotionEvent event) {
		switch (event.getAction()) {
		case MotionEvent.ACTION_DOWN:
			Log.d(TAG, TAG + "onTouchEvent+ACTION_DOWN");
			break;
		case MotionEvent.ACTION_POINTER_DOWN:
			Log.d(TAG, TAG + "onTouchEvent+ACTION_POINTER_DOWN");
			break;
		case MotionEvent.ACTION_POINTER_UP:
			Log.d(TAG, TAG + "onTouchEvent+ACTION_POINTER_UP");
			break;
		case MotionEvent.ACTION_MOVE:
			Log.d(TAG, TAG + "onTouchEvent+ACTION_MOVE");
			break;
		case MotionEvent.ACTION_UP:
			Log.d(TAG, TAG + "onTouchEvent+ACTION_UP");
			break;
		}
		return super.onTouchEvent(event);
	}

	@Override
	public boolean dispatchTouchEvent(MotionEvent ev) {
		switch (ev.getAction()) {
		case MotionEvent.ACTION_DOWN:
			Log.d(TAG, TAG + "dispatchTouchEvent+ACTION_DOWN");
			break;
		case MotionEvent.ACTION_POINTER_DOWN:
			Log.d(TAG, TAG + "dispatchTouchEvent+ACTION_POINTER_DOWN");
			break;
		case MotionEvent.ACTION_POINTER_UP:
			Log.d(TAG, TAG + "dispatchTouchEvent+ACTION_POINTER_UP");
			break;
		case MotionEvent.ACTION_MOVE:
			Log.d(TAG, TAG + "dispatchTouchEvent+ACTION_MOVE");
			break;
		case MotionEvent.ACTION_UP:
			Log.d(TAG, TAG + "dispatchTouchEvent+ACTION_UP");
			break;
		}
		return super.dispatchTouchEvent(ev);
	}
}


public class TestActivity extends Activity {
@Override
protected void onCreate(Bundle savedInstanceState) {
	super.onCreate(savedInstanceState);
	MyLinearLayout layout = new MyLinearLayout(this);
	
	
	MyTextView myTextView = new MyTextView(this);
	myTextView.setText("touch event");
	
	layout.addView(myTextView, new LayoutParams(LayoutParams.MATCH_PARENT, LayoutParams.MATCH_PARENT));
	
	setContentView(layout);
}
}
我们来看看log
然后我们在把TextView改成Button来看一下然后在看一下log
分析上面的log我们看到,touch事件首先走到的是父亲的dispatchTouchEvent方法中,然后是onInterceptTouchEvent过后才是MyTextView的dispatchTouchEvent和TouchEent中。所以我们知道了一件事:
那就是所有的touch事件一定是先传递给父亲然后在传递给孩子,然后在分析函数执行的顺序最开始执行的是dispatchTouchEvent方法,后面执行onInterceptTouchEvent最后才走到onTouchEvent事件当中。同时我们也要注意这几个函数的的返回值是boolean变量。然后我们再把MyLinearLayout的dispatchTouchEvent方法的返回值返回true,跑一下。

你会看到事件始终都只在MyLinearLayout里面了,根本就没有传递到孩子里面去了同样我们把它返回false,试试


这时候看到只有down事件了,这说明了什么呢?当dispatchTouchEvent返回true的时候会继续传递事件给自身控件dispatchTouchEvent处理,就像刚才的MylinearLayout一样仍然可以执行,DOWN和UP事件,当我们返回了false就连自身控件都不能接收到事件了。同样我们把dispatchTouchEvent还原,然后在onInterceptTouchEvent里面返回true;


可以看到事件也只分发到自身控件并进入到自身的OntouchEvent中,然后我们在把onInterceptTouchEvent的返回值改为false,看看结果


可以看到我们的事件没有收到任何影响所有的都照常执行,同样把MylinearLayout中的onInterceptTouchEvent复原,现在注意了我们要修改的是MyTextView中的onTouchEvent的返回值,我们把它修改为true


可以看到也没有任何变化,我们在试试把它改为false。


可以看到MytextView只接收到DOWN事件就再也不会接收到其他事件了。

结论:

androidtouch事件的默认传递顺序的父亲控件到子控件,而函数的调用顺序大致是这样的dispatchTouchEvent---->消息分发--->onInterceptTouchEvent--->事件打断-->onTouchEvent-->>事件处理。
关于返回值的问题我来简单的总结一下刚才上面的实验和结论:
dispatchTouchEvent返回的是true的时候事件是不会继续往下面传递了,一直都会走本身的dispatchTouchEvent这个方法,当返回为false的时候只会走一次dispatchTouchEvent这个方法的Down事件:
onInterceptTouchEvent返回的是true的时候是把事件拦截了让他走自身的onTouchEvent方法,当他返回为false的方法的时候不进行拦截正常进行。
 onTouchEvent这个方法的返回值是true的时候,继续分发事件到自身的touchEvent中,当为false的时候只会分发第一次down事件,还有各种返回值的组合我就不一一举例了,要想写好自定义的UI和控件touch事件的分发是必须得学好的,剩下的就慢慢的自己体会把。

郑重声明:本站内容如果来自互联网及其他传播媒体,其版权均属原媒体及文章作者所有。转载目的在于传递更多信息及用于网络分享,并不代表本站赞同其观点和对其真实性负责,也不构成任何其他建议。