开发的过程中,会遇到很多很多的滑动冲突,当然,我们解决滑动冲突就会用到事件分发,由于网上的例子较多,你可能直接找一个工具类而不去考虑是怎么解决的,所以面试的时候一脸蒙蔽,在这里我也为大家讲解一下。
在这里给大家展示一个流程图,我在网上看到的,感觉画的不错。
- 可以看出事件分发是有三层事件处理,分别为Activity 、ViewGroup 、 View
- 事件处理的标志有 super、true、false(super是调用父类的实现)
- 由Activity的diapatchTouchEvent做分发,不管调用什么,都由ViewGrop层的dispatchTouchEvent来处理。
dispatchTouchEvent和 onTouchEvent的框里有个【true—->消费】的字,表示的意思是如果方法返回true,那么代表事件就此消费,不会继续往别的地方传了,事件终止。
给大家分享一张U型图,让大家更加的了解事件分发。
在下面总结一下我的理解。
- 如果我们没有对控件里面的方法进行重写或更改返回值,而直接用super调用父类的默认实现,那么整个事件流向应该是从Activity—->ViewGroup—>View 从上往下调用dispatchTouchEvent方法,一直到叶子节点(View)的时候,再由View—>ViewGroup—>Activity从下往上调用onTouchEvent方法。
- dispatchTouchEvent 和 onTouchEvent 一旦return true,事件就停止传递了(到达终点)(没有谁能再收到这个事件)。
- dispatchTouchEvent 和 onTouchEvent return false的时候事件都回传给父控件的onTouchEvent处理。
上面讲解的都是针对ACTION_DOWN的事件传递,ACTION_MOVE和ACTION_UP在传递的过程中并不是和ACTION_DOWN 一样,你在执行ACTION_DOWN的时候返回了false,后面一系列其它的action就不会再得到执行了。简单的说,就是当dispatchTouchEvent在进行事件分发的时候,只有前一个事件(如ACTION_DOWN)返回true,才会收到ACTION_MOVE和ACTION_UP的事件。
下面我写了一个小demo,你们可以在闲暇的时候,看看是怎么触发每一个方法的。
import android.content.Context;
import android.graphics.PointF;
import android.util.AttributeSet;
import android.util.Log;
import android.view.MotionEvent;
import android.widget.LinearLayout;
public class TouchView extends LinearLayout{
private PointF pf=new PointF();
private int disX=0;
private int disY=0;
public TouchView(Context context, AttributeSet attrs) {
super(context, attrs);
// TODO Auto-generated constructor stub
}
@Override
public boolean dispatchTouchEvent(MotionEvent ev) {
if(ev.getAction()==MotionEvent.ACTION_DOWN){
Log.e("", "手指按下");
pf.x=ev.getX();
pf.y=ev.getY();
Log.e("", "X:"+pf.x+"\tY:"+pf.y);
}else if(ev.getAction()==MotionEvent.ACTION_POINTER_2_DOWN){
Log.e("", "第二根手指按下");
int x=(int) ev.getX();
int y=(int) ev.getY();
Log.e("", "第二根手指的坐标:X:"+x+"第二根手指的坐标:Y:"+y);
disX=(int) Math.sqrt(Math.pow(ev.getX(0)-ev.getX(1), 2)+
Math.pow(ev.getY(0)-ev.getY(1), 2)
);
Log.e("", "两手指间的距离"+disX);
}else if(ev.getAction()==MotionEvent.ACTION_MOVE){
Log.e("", "手指滑动");
int x1=(int) ev.getX();
int y1=(int) ev.getY();
Log.e("", "手指滑动时当前的坐标:"+x1+" "+y1);
}else if(ev.getAction()==MotionEvent.ACTION_UP){
Log.e("","手指抬起");
}
return super.dispatchTouchEvent(ev);
}
}
MainActivity
import android.os.Bundle;
import android.app.Activity;
import android.util.Log;
import android.view.Menu;
import android.view.View;
import android.view.View.OnClickListener;
import android.widget.Button;
import android.widget.LinearLayout;
public class MainActivity extends Activity {
private LinearLayout mLinear;
private Button mBtn;
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_main);
initView();
}
private void initView() {
mLinear=(LinearLayout) findViewById(R.id.mLinear);
mBtn=new Button(this);
LinearLayout.LayoutParams lp=new LinearLayout.LayoutParams(LinearLayout.LayoutParams.MATCH_PARENT, LinearLayout.LayoutParams.MATCH_PARENT);
mBtn.setLayoutParams(lp);
mLinear.addView(mBtn);
mBtn.setOnClickListener(new OnClickListener() {
@Override
public void onClick(View arg0) {
Log.e("","这是button按钮的点击事件" );
}
});
}
}
最下面是布局文件:
<RelativeLayout 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"
tools:context=".MainActivity" >
<touchdemo.TouchView
android:id="@+id/mLinear"
android:layout_width="match_parent"
android:layout_height="match_parent"
/>
</RelativeLayout>