当前位置: 代码迷 >> Android >> android自定义控件系列课程-touch事件的传递
  详细解决方案

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

热度:55   发布时间:2016-04-28 03:21:37.0
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 {@Overrideprotected 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事件的分发是必须得学好的,剩下的就慢慢的自己体会把。

  相关解决方案