onTouch事件传递

来源:互联网 发布:长寿行知计算机老师 编辑:程序博客网 时间:2024/06/04 20:09

前言


在平常开发中,我们经常会遇到点击事件冲突的情况,或者在面试中被问及,那么下面我们来看下onTouch事件到底是怎么回事。


1.代码

先上代码,三个java类和一个简单的xml布局:

父View:

public class ParentView extends LinearLayout {    public ParentView(Context context) {        super(context);    }    public ParentView(Context context, @Nullable AttributeSet attrs) {        super(context, attrs);    }    public ParentView(Context context, @Nullable AttributeSet attrs, int defStyleAttr) {        super(context, attrs, defStyleAttr);    }    @Override    public boolean onInterceptTouchEvent(MotionEvent ev) {        showLog("--onInterceptTouchEvent--", "--ACTION_DOWN" + "-- result = " + false);        return false;    }    @Override    public boolean dispatchTouchEvent(MotionEvent event) {        String tag = "";        switch(event.getAction()) {            case MotionEvent.ACTION_DOWN:                tag = "--ACTION_DOWN()";                break;            case MotionEvent.ACTION_MOVE:                tag = "--ACTION_MOVE()";                break;            case MotionEvent.ACTION_UP:                tag = "--ACTION_UP()";                break;            default:                tag = "--" + event.getAction();                break;        }        showLog("--dispatchTouchEvent--", tag);        boolean result = super.dispatchTouchEvent(event);        return result;    }    @Override    public boolean onTouchEvent(MotionEvent event) {        String tag = "";        switch(event.getAction()) {            case MotionEvent.ACTION_DOWN:                tag = "--ACTION_DOWN()";                break;            case MotionEvent.ACTION_MOVE:                tag = "--ACTION_MOVE()";                break;            case MotionEvent.ACTION_UP:                tag = "--ACTION_UP()";                break;            default:                tag = "--" + event.getAction();                break;        }        boolean result = super.onTouchEvent(event);        showLog("--onTouchEvent--", tag + "-- result = " +  result);        return result;    }    private void showLog(String method, String msg) {        Log.e("on-touch", "ParentView:" + method + "is called, process:" + msg);    }}

子View:

public class SubView extends View {    public SubView(Context context) {        super(context);    }    public SubView(Context context, @Nullable AttributeSet attrs) {        super(context, attrs);    }    public SubView(Context context, @Nullable AttributeSet attrs, int defStyleAttr) {        super(context, attrs, defStyleAttr);    }    @Override    public boolean dispatchTouchEvent(MotionEvent event) {        String tag = "";        switch(event.getAction()) {            case MotionEvent.ACTION_DOWN:                tag = "--ACTION_DOWN()";                break;            case MotionEvent.ACTION_MOVE:                tag = "--ACTION_MOVE()";                break;            case MotionEvent.ACTION_UP:                tag = "---ACTION_UP()";                break;            default:                tag = "" + event.getAction();                break;        }        showLog("--dispatchTouchEvent--", "event.getAction() = " + event.getAction());        boolean result = super.dispatchTouchEvent(event);        return result;    }    @Override    public boolean onTouchEvent(MotionEvent event) {        String tag = "";        switch(event.getAction()) {            case MotionEvent.ACTION_DOWN:                tag = "--ACTION_DOWN()";                break;            case MotionEvent.ACTION_MOVE:                tag = "--ACTION_MOVE()";                break;            case MotionEvent.ACTION_UP:                tag = "--ACTION_UP()";                break;            default:                tag = "--" + event.getAction();                break;        }        boolean result = super.onTouchEvent(event);        showLog("--onTouchEvent--", tag + "-- result = " + result);        return true;    }    private void showLog(String method, String msg) {        Log.e("on-touch", "SubView:" + method + "is called, process:" + msg);    }}

MainActivity:

public class MainActivity extends AppCompatActivity {    @Override    protected void onCreate(Bundle savedInstanceState) {        super.onCreate(savedInstanceState);        setContentView(R.layout.activity_main);        findViewById(R.id.parent).setOnClickListener(new View.OnClickListener() {            @Override            public void onClick(View v) {                Toast.makeText(MainActivity.this, "父类响应了", Toast.LENGTH_LONG).show();            }        });        findViewById(R.id.sub).setOnClickListener(new View.OnClickListener() {            @Override            public void onClick(View v) {                Toast.makeText(MainActivity.this, "子类响应了", Toast.LENGTH_LONG).show();            }        });        findViewById(R.id.sub).setOnTouchListener(new View.OnTouchListener() {            @Override            public boolean onTouch(View v, MotionEvent event) {                String tag = "";                switch(event.getAction()) {                    case MotionEvent.ACTION_DOWN:                        tag = "--ACTION_DOWN()";                        break;                    case MotionEvent.ACTION_MOVE:                        tag = "--ACTION_MOVE()";                        break;                    case MotionEvent.ACTION_UP:                        tag = "--ACTION_UP()";                        break;                    default:                        tag = "--" + event.getAction();                        break;                }                showLog("--onTouch--", tag);                return false;            }        });    }    @Override    public boolean dispatchTouchEvent(MotionEvent event) {        String tag = "";        switch(event.getAction()) {            case MotionEvent.ACTION_DOWN:                tag = "--ACTION_DOWN()";                break;            case MotionEvent.ACTION_MOVE:                tag = "--ACTION_MOVE()";                break;            case MotionEvent.ACTION_UP:                tag = "--ACTION_UP()";                break;            default:                tag = "--" + event.getAction();                break;        }        showLog("--dispatchTouchEvent--", tag);        return super.dispatchTouchEvent(event);    }    @Override    public boolean onTouchEvent(MotionEvent event) {        String tag = "";        switch(event.getAction()) {            case MotionEvent.ACTION_DOWN:                tag = "--ACTION_DOWN()";                break;            case MotionEvent.ACTION_MOVE:                tag = "--ACTION_MOVE()";                break;            case MotionEvent.ACTION_UP:                tag = "--ACTION_UP()";                break;            default:                tag = "--" + event.getAction();                break;        }        showLog("--onTouchEvent--", tag);        return super.onTouchEvent(event);    }    private void showLog(String method, String msg) {        Log.e("on-touch", "MainActivity:" + method + "is called, process:" + msg);    }}


都比较简单,只是简单的进行打印事件信息,来确定事件的传递方向。

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:gravity="center"    android:orientation="vertical"    >    <com.victor.ontouchdemo.ParentView        android:id="@+id/parent"        android:layout_width="300dp"        android:layout_height="300dp"        android:background="#ff0088"        android:gravity="center"        android:orientation="vertical">        <com.victor.ontouchdemo.SubView            android:id="@+id/sub"            android:layout_width="150dp"            android:layout_height="150dp"            android:background="#000088"/>    </com.victor.ontouchdemo.ParentView></LinearLayout>
也很简单:


2.正常流程分析


如果按照正常流程,不对视图点击事件做任何处理,我们对子View进行点击,那么只有子View会进行响应,而父View不会触发点击事件。

打印信息:

MainActivity:--dispatchTouchEvent is called, process:--ACTION_DOWN()

ParentView:--dispatchTouchEvent is called, process:--ACTION_DOWN()

ParentView:--onInterceptTouchEvent is called, process:--ACTION_DOWN-- result = false

SubView:--dispatchTouchEvent is called, process:event.getAction() = 0

MainActivity:--onTouch is called, process:--ACTION_DOWN()

SubView:--onTouchEvent is called, process:--ACTION_DOWN()-- result = true

MainActivity:--dispatchTouchEvent is called, process:--ACTION_MOVE()

ParentView:--dispatchTouchEvent is called, process:--ACTION_MOVE()

ParentView:--onInterceptTouchEvent is called, process:--ACTION_DOWN-- result = false

SubView:--dispatchTouchEvent is called, process:event.getAction() = 2

MainActivity:--onTouch is called, process:--ACTION_MOVE()

SubView:--onTouchEvent is called, process:--ACTION_MOVE()-- result = true

MainActivity:--dispatchTouchEventiscalled, process:--ACTION_UP()

ParentView:--dispatchTouchEventiscalled, process:--ACTION_UP()

ParentView:--onInterceptTouchEvent()is called, process:--ACTION_DOWN-- result = false

SubView:--dispatchTouchEventiscalled, process:event.getAction() = 1

MainActivity:--onTouchiscalled, process:--ACTION_UP()

SubView:--onTouchEventiscalled, process:--ACTION_UP()-- result = true


通过打印的信息,我们发现,事件的传递是由Activity-->父View-->子View的,具体事件的传递是:dispatchTouchEventis-->(onInterceptTouchEvent只有继承自ViewGroup的类才有,如LinearLayout)-->onTouch-->onTouchEvent的。

onInterceptTouchEvent方法默认是返回false的,也就是父View默认不对子View进行拦截。

默认子View的onTouchEvent会返回true,也就是把点击事件消费了,所以就不会再向父View传递。

而当对子View设置了onTouch事件时,如果没有消费事件,应该返回false,让事件继续传递,此时onTouchEvent会被触发;如果onTouch返回true,则事件被消费,不会再传递给onTouchEvent和父View了。


3.拦截子View事件

如果父View需要拦截子View点击事件,只需要让父View的onInterceptTouchEvent()方法返回true即可。

打印信息:

MainActivity:--dispatchTouchEvent--is called, process:--ACTION_DOWN()

ParentView:--dispatchTouchEvent--is called, process:--ACTION_DOWN()

ParentView:--onInterceptTouchEvent--is called, process:--ACTION_DOWN--result = true

ParentView:--onTouchEvent--is called, process:--ACTION_DOWN()-- result = true

MainActivity:--dispatchTouchEvent--is called, process:--ACTION_MOVE()

ParentView:--dispatchTouchEvent--is called, process:--ACTION_MOVE()

ParentView:--onTouchEvent--is called, process:--ACTION_MOVE()-- result = true

MainActivity:--dispatchTouchEvent--is called, process:--ACTION_MOVE()

ParentView:--dispatchTouchEvent--is called, process:--ACTION_MOVE()

ParentView:--onTouchEvent--is called, process:--ACTION_MOVE()-- result = true

MainActivity:--dispatchTouchEvent--is called, process:--ACTION_UP()

ParentView:--dispatchTouchEvent--is called, process:--ACTION_UP()

ParentView:--onTouchEvent--is called, process:--ACTION_UP()-- result = true

从打印信息我们发现,如果子View的onTouch事件被拦截后,子View的dispatchTouchEvent、onTouchonTouchEvent都不会再响应了。


4.子View的onTouchEvent或onTouch事件返回false


4.1当onTouch事件返回false,onTouchEvent返回true时,子View能够正常响应点击事件。

MainActivity:--dispatchTouchEvent--is called, process:--ACTION_DOWN()

ParentView:--dispatchTouchEvent--is called, process:--ACTION_DOWN()

ParentView:--onInterceptTouchEvent--is called, process:--ACTION_DOWN-- result = false

SubView:--dispatchTouchEvent--is called, process:event.getAction() = 0

MainActivity:--onTouch--is called, process:--ACTION_DOWN()

SubView:--onTouchEvent--is called, process:--ACTION_DOWN()-- result = true

MainActivity:--dispatchTouchEvent--is called, process:--ACTION_MOVE()

ParentView:--dispatchTouchEvent--is called, process:--ACTION_MOVE()

ParentView:--onInterceptTouchEvent--is called, process:--ACTION_DOWN-- result = false

SubView:--dispatchTouchEvent--is called, process:event.getAction() = 2

MainActivity:--onTouch--is called, process:--ACTION_MOVE()

SubView:--onTouchEvent--is called, process:--ACTION_MOVE()-- result = true


MainActivity:--dispatchTouchEvent--is called, process:--ACTION_UP()

ParentView:--dispatchTouchEvent--is called, process:--ACTION_UP()

ParentView:--onInterceptTouchEvent--is called, process:--ACTION_DOWN-- result = false

SubView:--dispatchTouchEvent--is called, process:event.getAction() = 1

MainActivity:--onTouch--is called, process:--ACTION_UP()

SubView:--onTouchEvent--is called, process:--ACTION_UP()-- result = true

由打印信息我们可以看到,当onTouch返回false,onTouchEvent返回true照样把事件消费了,不再传递给父View,也就是说,设置onTouch事件,也就是在onTouchEvent事件前,多设置了一层拦截事件而已。

4.2 onTouch事件返回true,onTouchEvent返回false时,子View的onTouchEvent事件不能够正常响应点击事件。因为onTouch事件已经消费了该事件。

MainActivity:--dispatchTouchEvent--is called, process:--ACTION_DOWN()

ParentView:--dispatchTouchEvent--is called, process:--ACTION_DOWN()

ParentView:--onInterceptTouchEvent--is called, process:--ACTION_DOWN-- result = false

SubView:--dispatchTouchEvent--is called, process:event.getAction() = 0

MainActivity:--onTouch--is called, process:--ACTION_DOWN()
MainActivity:--dispatchTouchEvent--is called, process:--ACTION_MOVE()

ParentView:--dispatchTouchEvent--is called, process:--ACTION_MOVE()

ParentView:--onInterceptTouchEvent--is called, process:--ACTION_DOWN-- result = false

SubView:--dispatchTouchEvent--is called, process:event.getAction() = 2

MainActivity:--onTouch--is called, process:--ACTION_MOVE()
MainActivity:--dispatchTouchEvent--is called, process:--ACTION_UP()

ParentView:--dispatchTouchEvent--is called, process:--ACTION_UP()

ParentView:--onInterceptTouchEvent--is called, process:--ACTION_DOWN-- result = false

SubView:--dispatchTouchEvent--is called, process:event.getAction() = 1

MainActivity:--onTouch--is called, process:--ACTION_UP()

我们看到,当事件传递到onTouch返回true后,onTouchEvent事件并没有执行,因为事件已经被消费了。

4.3 onTouch事件返回false,onTouchEvent返回false

MainActivity:--dispatchTouchEvent--is called, process:--ACTION_DOWN()

ParentView:--dispatchTouchEvent--is called, process:--ACTION_DOWN()

ParentView:--onInterceptTouchEvent--is called, process:--ACTION_DOWN-- result = false

SubView:--dispatchTouchEvent--is called, process:event.getAction() = 0

MainActivity:--onTouch--is called, process:--ACTION_DOWN()

SubView:--onTouchEvent--is called, process:--ACTION_DOWN()-- result = true

ParentView:--onTouchEvent--is called, process:--ACTION_DOWN()-- result = true


MainActivity:--dispatchTouchEvent--is called, process:--ACTION_MOVE()

ParentView:--dispatchTouchEvent--is called, process:--ACTION_MOVE()

ParentView:--onTouchEvent--is called, process:--ACTION_MOVE()-- result = true


MainActivity:--dispatchTouchEvent--is called, process:--ACTION_UP()

ParentView:--dispatchTouchEvent--is called, process:--ACTION_UP()

ParentView:--onTouchEvent--is called, process:--ACTION_UP()-- result = true

通过打印信息我们发现,当onTouch和onTouchEvent都返回false时,事件被传递到了父View的onToucEvent,由父View进行响应消费了。由于onTouch和onTouchEvent的ACTION_DOWN事件都返回false,所以之后的ACTION_MOVEACTION_UP事件将不再响应。


5.总结

1.只要我们在onTouch或onTouchEvent返回true,则触摸事件将被消费,不再像下传递。
2.只要我们在onTouch或onTouchEvent的ACTION_DOWN事件返回false,则触摸事件之后的ACTION_MOVEACTION_UP事件将不再响应。
3.如果我们想拦截子View的触摸事件,通常有两种方法:
1)让父View的onInterceptTouchEvent()返回true,常用该方法; 
2)让子View的onTouch和onTouchEvent方法都返回false,事件将被传递到父View进行响应。

如有需要运行DEMO的,请猛戳http://download.csdn.net/detail/fwt336/9838192


0 0
原创粉丝点击