学习目标:
Android事件传递学习内容:
再多的文字也不如一张图清晰,上图。
这个图已经讲的很清楚了,主要再记一些细节。
- 我们手指触摸屏幕然后滑动一段距离最后抬起,所以在这个过程中所产生的一系列事件从down开始中间有许多move事件最后以up结束。
- 正常情况下,一个事件序列只能被一个view拦截且消耗,但可以通过一定的方式强行传给别的view处理。
- 当某个view决定一旦拦截事件,那么这个序列事件都会由他处理,并且它的
onInterceptTouchEvent
不会再调用。 - 当某个view一旦开始处理事件,如果不消耗ACTIO_DOWN事件(返回false),那么同一事件序列中的其他事件都不会再在交给它处理。
- 如果这个view不消耗除ACTION_DOWN以外的事件,那么这个点击事件不会被父元素所调用,而会被activity调用。
- viewGroup默认是不拦截任何事件的,它的
onInterceptTouchEvent
返回false。 - view没有
onInterceptTouchEvent
方法,一旦有事件,则会调用它的onTouchEvent
- view的
onTouchEvent
默认会消耗事件(返回true),除非它的longClickable和clickable为false. - 我们可以在子元素干预父元素的事件分发过程,通过调用
requestDisallowInterceptTouchEvent(true)
,但是ACTION_DOWN除外。
好的,我们下面来实现一个viewgroup,拦截滑动事件,但不拦截点击事件的demo。
package com.suyong.componentization;
import android.content.Context;
import android.content.res.Configuration;
import android.graphics.Point;
import android.graphics.PointF;
import android.util.AttributeSet;
import android.util.Log;
import android.view.MotionEvent;
import android.view.View;
import android.view.ViewConfiguration;
import android.view.ViewGroup;
public class MyViewGroup extends ViewGroup {
private String TAG = "MyViewGroup";
private PointF DownPoint;
public MyViewGroup(Context context) {
super(context);
init();
}
public MyViewGroup(Context context, AttributeSet attrs) {
super(context, attrs);
init();
}
public MyViewGroup(Context context, AttributeSet attrs, int defStyleAttr) {
super(context, attrs, defStyleAttr);
init();
}
private void init() {
DownPoint = new PointF();
}
@Override
protected void onMeasure(int widthMeasureSpec, int heightMeasureSpec){
super.onMeasure(widthMeasureSpec,heightMeasureSpec);
measureChildren(widthMeasureSpec, heightMeasureSpec);
}
@Override
protected void onLayout(boolean changed, int l, int t, int r, int b) {
Log.d(TAG,"onLayout");
int childCount = getChildCount();
int bottom = 0;
for (int i = 0; i < childCount; i++){
View childView = getChildAt(i);
int cWidth = childView.getMeasuredWidth();
int cHeight = childView.getMeasuredHeight();
MarginLayoutParams layoutParams = (MarginLayoutParams) childView.getLayoutParams();
childView.layout(layoutParams.leftMargin, bottom, cWidth, bottom + cHeight);
bottom += cHeight;
}
}
@Override
public boolean dispatchTouchEvent(MotionEvent ev) {
Log.d(TAG,"dispatchTouchEvent");
return super.dispatchTouchEvent(ev);
}
@Override
public boolean onInterceptTouchEvent(MotionEvent ev) {
int action = ev.getAction();
if (action == MotionEvent.ACTION_CANCEL || action == MotionEvent.ACTION_UP) {
return false; // 返回false,不拦截事件,交给子view
}
switch (action) {
case MotionEvent.ACTION_MOVE:
final float xDiff = calculateDistanceX(ev);
//如果滑动距离大于最小有效距离,那么就拦截下来
int scaledTouchSlop = ViewConfiguration.get(getContext()).getScaledTouchSlop();
if (xDiff > scaledTouchSlop) {
return true;
}
break;
case MotionEvent.ACTION_DOWN:
//手指落下时的坐标,用来计算滑动距离的
DownPoint.set(ev.getRawX(),ev.getRawY());
return false;
}
//其他情况下我们就不拦截触摸事件了,交给子view处理这个触摸事件
return false;
}
@Override
public boolean onTouchEvent(MotionEvent event) {
int action = event.getAction();
boolean intercept = false;
switch (action) {
case MotionEvent.ACTION_DOWN:
Log.d(TAG,"onTouchEvent "+"MotionEvent.ACTION_DOWN");
intercept = true;
break;
case MotionEvent.ACTION_MOVE:
Log.d(TAG,"onTouchEvent "+"MotionEvent.ACTION_MOVE");
intercept = true;
break;
case MotionEvent.ACTION_UP:
Log.d(TAG,"onTouchEvent "+"MotionEvent.ACTION_UP");
intercept = false;
break;
}
return intercept;
}
@Override
protected LayoutParams generateLayoutParams(LayoutParams p) {
return new MarginLayoutParams(p);
}
@Override
public LayoutParams generateLayoutParams(AttributeSet attrs) {
return new MarginLayoutParams(getContext(), attrs);
}
@Override
protected LayoutParams generateDefaultLayoutParams() {
return new MarginLayoutParams(LayoutParams.MATCH_PARENT, LayoutParams.MATCH_PARENT);
}
private float calculateDistanceX(MotionEvent ev) {
return Math.abs(ev.getRawX() - DownPoint.y);
}
}
package com.suyong.componentization;
import android.content.Context;
import android.util.AttributeSet;
import android.util.Log;
import android.view.MotionEvent;
import android.view.View;
import androidx.annotation.Nullable;
public class MyView extends View {
private String TAG = "MyView";
public MyView(Context context) {
super(context);
}
public MyView(Context context, @Nullable AttributeSet attrs) {
super(context, attrs);
}
public MyView(Context context, @Nullable AttributeSet attrs, int defStyleAttr) {
super(context, attrs, defStyleAttr);
}
@Override
public boolean dispatchTouchEvent(MotionEvent event) {
Log.d(TAG,"dispatchTouchEvent");
return super.dispatchTouchEvent(event);
}
@Override
public boolean onTouchEvent(MotionEvent event) {
return super.onTouchEvent(event);
}
}
<?xml version="1.0" encoding="utf-8"?>
<com.suyong.componentization.MyViewGroup xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:tools="http://schemas.android.com/tools"
android:layout_width="200dp"
android:layout_height="200dp"
android:background="@color/colorAccent"
tools:context=".MainActivity">
<com.suyong.componentization.MyView
android:onClick="click"
android:id="@+id/myview"
android:layout_width="100dp"
android:layout_height="100dp"
android:background="@color/colorPrimaryDark"
android:onClick="jumpToWalker"
/>
</com.suyong.componentization.MyViewGroup>
个人总结,欢迎留言讨论!