关于Android的事件传递,应该从两个方面来说,5.0以前,5.0以后
在5.0以前:
我们一般关注三个方法
- dispatchTouchEvent
- onInterceptTouchEvent
- onTouchEvent
ViewGroup是继承自View的,而onInterceptTouchEvent方法是ViewGroup独有的,可以对事件进行拦截,拦截之后事件是无法传递到子view上的(包括本次事件序列的move、up等)
一个点击事件发生会依次经过activity的dispatchTouchEvent ,ViewGroup的dispatchTouchEvent,ViewGroup的onInterceptTouchEvent。
下面分两种情况: - onInterceptTouchEvent返回true
表示ViewGroup对本事件进行拦截,会直接调用ViewGroup的onTouchEvent,如果onTouchEvent不处理该事件,会调用Activity的onTouchEvent,后续的事件不会再传递到ViewGroup了。 - onInterceptTouchEvent返回false
事件会传递到子view上,会调用View的dispatchTouchEvent,View的onTouchEvent,如果子View的onTouchEvent没有处理事件,事件会经过ViewGroup的onTouchEvent,如果ViewGroup的onTouchEvent没有处理,会调用Activity的onTouchEvent,后续的事件不会再传递到ViewGroup了。
一些细节:
onInterceptTouchEvent在两种情况下会调用:
- down事件的时候坑定会调用
- 子view处理了down事件,后续的move也会执行到onInterceptTouchEvent
5.0以后:引入嵌套滑动
引入了嵌套滑动CoordinatorLayout之后,一般是要配合Behavior使用,在使用过程中我们需要关注两种情况:
- 某个view监听另一个view的状态变化,例如大小、位置、显示状态等
- 某个view监听CoordinatorLayout里的滑动状态
对于第一种情况,我们关心的是:
layoutDependsOn和onDependentViewChanged方法,
对于第二种情况,我们关心的是:
onStartNestedScroll和onNestedPreScroll方法。
先来说第一组方法:
layoutDependsOn和onDependentViewChanged的调用时机
这个主要是在CoordinatorLayout类的onAttachedToWindow()方法,内部定义了一个ViewTreeObserver,然后给这个ViewTreeObserver添加了一个监听器OnPreDrawListener,用来监听view的draw操作。然后在这个监听器中循环遍历所有的子View,一旦某个子View发生了draw操作,就循环遍历所有子view,如果发现某个view上有Behavior,就调用Behavior中的这些方法。
看下第二组方法:
写个demo来测试一下CoordinatorLayout、Behavior、子View之间事件的流程
package com.example.huozhenpeng.myapplication;
import android.content.Context;
import android.support.design.widget.CoordinatorLayout;
import android.util.AttributeSet;
import android.util.Log;
import android.view.MotionEvent;
/**
* 作者 huozhenpeng
* 日期 2018/8/10
* 邮箱 huohacker@sina.com
*/
public class MCoordinateLayout extends CoordinatorLayout {
public MCoordinateLayout(Context context) {
super(context);
}
public MCoordinateLayout(Context context, AttributeSet attrs) {
super(context, attrs);
}
public MCoordinateLayout(Context context, AttributeSet attrs, int defStyleAttr) {
super(context, attrs, defStyleAttr);
}
@Override
public boolean onInterceptTouchEvent(MotionEvent ev) {
Log.e("abc","coordinatorlayout------onInterceptTouchEvent");
return super.onInterceptTouchEvent(ev);
}
@Override
public boolean onTouchEvent(MotionEvent ev) {
Log.e("abc","coordinatorlayout------onTouchEvent");
return super.onTouchEvent(ev);
}
}
package com.example.huozhenpeng.myapplication;
import android.content.Context;
import android.support.design.widget.CoordinatorLayout;
import android.util.AttributeSet;
import android.util.Log;
import android.view.MotionEvent;
import android.view.View;
/**
* 作者 huozhenpeng
* 日期 2018/8/10
* 邮箱 huohacker@sina.com
*/
public class MBehavior extends CoordinatorLayout.Behavior<View> {
public MBehavior(Context context, AttributeSet attrs) {
super(context, attrs);
}
@Override
public boolean onInterceptTouchEvent(CoordinatorLayout parent, View child, MotionEvent ev) {
Log.e("abc","behavior----onInterceptTouchEvent");
return super.onInterceptTouchEvent(parent, child, ev);
// return true;
}
@Override
public boolean onTouchEvent(CoordinatorLayout parent, View child, MotionEvent ev) {
Log.e("abc","behavior----onTouchEvent");
return super.onTouchEvent(parent, child, ev);
}
}
package com.example.huozhenpeng.myapplication;
import android.content.Context;
import android.support.annotation.Nullable;
import android.util.AttributeSet;
import android.util.Log;
import android.view.MotionEvent;
import android.widget.TextView;
/**
* 作者 huozhenpeng
* 日期 2018/8/10
* 邮箱 huohacker@sina.com
*/
public class MTextView extends android.support.v7.widget.AppCompatTextView {
public MTextView(Context context) {
super(context);
}
public MTextView(Context context, @Nullable AttributeSet attrs) {
super(context, attrs);
}
public MTextView(Context context, @Nullable AttributeSet attrs, int defStyleAttr) {
super(context, attrs, defStyleAttr);
}
@Override
public boolean onTouchEvent(MotionEvent event) {
Log.e("abc","textview---------onTouchEvent");
return super.onTouchEvent(event);
// return true;
}
}
<?xml version="1.0" encoding="utf-8"?>
<android.support.constraint.ConstraintLayout xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:app="http://schemas.android.com/apk/res-auto"
xmlns:tools="http://schemas.android.com/tools"
android:layout_width="match_parent"
android:layout_height="match_parent"
tools:context="com.example.huozhenpeng.myapplication.MainActivity">
<com.example.huozhenpeng.myapplication.MCoordinateLayout
android:layout_width="match_parent"
android:layout_height="match_parent">
<com.example.huozhenpeng.myapplication.MTextView
android:layout_width="100dp"
android:layout_height="50dp"
android:background="#ff00ff"
android:text="hhh "
app:layout_behavior="com.example.huozhenpeng.myapplication.MBehavior"
/>
</com.example.huozhenpeng.myapplication.MCoordinateLayout>
</android.support.constraint.ConstraintLayout>
image.png
package com.example.huozhenpeng.myapplication;
import android.content.Context;
import android.support.design.widget.CoordinatorLayout;
import android.util.AttributeSet;
import android.util.Log;
import android.view.MotionEvent;
import android.view.View;
/**
* 作者 huozhenpeng
* 日期 2018/8/10
* 邮箱 huohacker@sina.com
*/
public class MBehavior extends CoordinatorLayout.Behavior<View> {
public MBehavior(Context context, AttributeSet attrs) {
super(context, attrs);
}
@Override
public boolean onInterceptTouchEvent(CoordinatorLayout parent, View child, MotionEvent ev) {
Log.e("abc","behavior----onInterceptTouchEvent");
// return super.onInterceptTouchEvent(parent, child, ev);
return true;
}
@Override
public boolean onTouchEvent(CoordinatorLayout parent, View child, MotionEvent ev) {
Log.e("abc","behavior----onTouchEvent");
return super.onTouchEvent(parent, child, ev);
}
}
image.png
package com.example.huozhenpeng.myapplication;
import android.content.Context;
import android.support.annotation.Nullable;
import android.util.AttributeSet;
import android.util.Log;
import android.view.MotionEvent;
import android.widget.TextView;
/**
* 作者 huozhenpeng
* 日期 2018/8/10
* 邮箱 huohacker@sina.com
*/
public class MTextView extends android.support.v7.widget.AppCompatTextView {
public MTextView(Context context) {
super(context);
}
public MTextView(Context context, @Nullable AttributeSet attrs) {
super(context, attrs);
}
public MTextView(Context context, @Nullable AttributeSet attrs, int defStyleAttr) {
super(context, attrs, defStyleAttr);
}
@Override
public boolean onTouchEvent(MotionEvent event) {
Log.e("abc","textview---------onTouchEvent");
// return super.onTouchEvent(event);
return true;
}
}
image.png
大概总结一下:事件传递到CoordinatorLayout的时候,CoordinatorLayout会把事件交给Behavior(不管是在onInterceptTouchEvent()方法中还是在onTouchEvent()方法中),Behavior具有中断事件的能力,如果Behavior中断了事件,事件也是传递不到子view的。
我们以NestedScrollView为例来研究下第二组方法
在onTouchEvent()方法中调用startNestedScroll()
@Override
public boolean startNestedScroll(int axes, int type) {
return mChildHelper.startNestedScroll(axes, type);
}
紧接着
public boolean startNestedScroll(@ScrollAxis int axes, @NestedScrollType int type) {
if (hasNestedScrollingParent(type)) {
// Already in progress
return true;
}
if (isNestedScrollingEnabled()) {
ViewParent p = mView.getParent();
View child = mView;
while (p != null) {
if (ViewParentCompat.onStartNestedScroll(p, child, mView, axes, type)) {
setNestedScrollingParentForType(type, p);
ViewParentCompat.onNestedScrollAccepted(p, child, mView, axes, type);
return true;
}
if (p instanceof View) {
child = (View) p;
}
p = p.getParent();
}
}
return false;
}
public static boolean onStartNestedScroll(ViewParent parent, View child, View target,
int nestedScrollAxes, int type) {
if (parent instanceof NestedScrollingParent2) {
// First try the NestedScrollingParent2 API
return ((NestedScrollingParent2) parent).onStartNestedScroll(child, target,
nestedScrollAxes, type);
} else if (type == ViewCompat.TYPE_TOUCH) {
// Else if the type is the default (touch), try the NestedScrollingParent API
return IMPL.onStartNestedScroll(parent, child, target, nestedScrollAxes);
}
return false;
}
其实就是去调用CoordinatorLayout 类的onStartNestedScroll方法了
@Override
public boolean onStartNestedScroll(View child, View target, int axes, int type) {
boolean handled = false;
final int childCount = getChildCount();
for (int i = 0; i < childCount; i++) {
final View view = getChildAt(i);
if (view.getVisibility() == View.GONE) {
// If it's GONE, don't dispatch
continue;
}
final LayoutParams lp = (LayoutParams) view.getLayoutParams();
final Behavior viewBehavior = lp.getBehavior();
if (viewBehavior != null) {
final boolean accepted = viewBehavior.onStartNestedScroll(this, view, child,
target, axes, type);
handled |= accepted;
lp.setNestedScrollAccepted(type, accepted);
} else {
lp.setNestedScrollAccepted(type, false);
}
}
return handled;
}
接着就去调用Behavior的onStartNestedScroll()方法了,onNestedScrollAccepted()也是这个调用流程。
然后去调用dispatchNestedScroll()方法
网友评论