Android--触摸屏事件分发--View

1–1:例子

<?xml version="1.0" encoding="utf-8"?>
<RelativeLayout xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:tools="http://schemas.android.com/tools"
android:layout_width="match_parent"
android:id="@+id/layout"
android:layout_height="match_parent">

    <Button
android:id="@+id/button"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:text="Click" />

</RelativeLayout>
public class MainActivity extends AppCompatActivity implements View.OnClickListener, View.OnTouchListener {
    Button btn;
    RelativeLayout layout;

@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
        setContentView(R.layout.activity_main);
btn = (Button) findViewById(R.id.button);
layout = (RelativeLayout) findViewById(R.id.layout);

btn.setOnClickListener(this);
btn.setOnTouchListener(this);

layout.setOnTouchListener(this);
layout.setOnClickListener(this);
    }

@Override
public boolean onTouch(View v, MotionEvent event) {
        Log.d("----", "onTuch");
return false;
    }

@Override
public void onClick(View v) {
        Log.d("----", "onClick");
    }
}

1–2:代码分析:1– 布局文件中有RelativeLayout 和Button<属于ViewGroup的子类,ViewGroup属于View的子类>
2–对两个控件设置事件OnTuch(),OnClick()事件
3–稳稳点击Button;稳稳点击除Button之外的区域;点击Button时,晃动一下

可以看出:
1–OnTuch()与OnClick()相比,多一个MotionEvent类型的event参数,这样,可以在此处理 ACTION_DOWN、ACTION_UP、ACTION_MOVE等的触摸事件,
2–OnTuch()返回一个false;

1–3:修改OnTuch(),让返回为true——>可以看出,无法进行执行OnClick()事件!

1–4:总结
1—–Android控件的Listener事件触发顺序是先触发onTouch,其次onClick!
2—–如果控件的onTouch返回true将会阻止事件继续传递,返回false事件会继续传递!

2–1:dispatchTouchEvent()

package android.app----->Activity.java
public boolean dispatchTouchEvent(MotionEvent ev) {
if (ev.getAction() == MotionEvent.ACTION_DOWN) {
        onUserInteraction();
    }
if (getWindow().superDispatchTouchEvent(ev)) {
return true;
    }
return onTouchEvent(ev);
}
package android.view;---->View
/**
 * Pass the touch screen motion event down to the target view, or this
 * view if it is the target.
 *
 * @param event The motion event to be dispatched.
 * @return True if the event was handled by the view, false otherwise.
 */
public boolean dispatchTouchEvent(MotionEvent event) {
    if (event.isTargetAccessibilityFocus()) {
        if (!isAccessibilityFocusedViewOrHost()) {
            return false;
        }
        event.setTargetAccessibilityFocus(false);
    }

    boolean result = false;

    if (mInputEventConsistencyVerifier != null) {
        mInputEventConsistencyVerifier.onTouchEvent(event, 0);
    }

    final int actionMasked = event.getActionMasked();
    if (actionMasked == MotionEvent.ACTION_DOWN) {
        stopNestedScroll();
    }

    if (onFilterTouchEventForSecurity(event)) {
        ListenerInfo li = mListenerInfo;
        if (li != null && li.mOnTouchListener != null
                && (mViewFlags & ENABLED_MASK) == ENABLED
                && li.mOnTouchListener.onTouch(this, event)) {
            result = true;
        }

        if (!result && onTouchEvent(event)) {
            result = true;
        }
    }

    if (!result && mInputEventConsistencyVerifier != null) {
        mInputEventConsistencyVerifier.onUnhandledEvent(event, 0);
    }
    if (actionMasked == MotionEvent.ACTION_UP ||
            actionMasked == MotionEvent.ACTION_CANCEL ||
            (actionMasked == MotionEvent.ACTION_DOWN && !result)) {
        stopNestedScroll();
    }

    return result;
}

分析:
1–

if (onFilterTouchEventForSecurity(event))语句判断是否被遮盖等

2–

ListenerInfo li = mListenerInfo;

ListenerInfo是View的静态内部类,用来定义一下监听方法;
3–

if (li != null && li.mOnTouchListener != null
        && (mViewFlags & ENABLED_MASK) == ENABLED
        && li.mOnTouchListener.onTouch(this, event)) {
    result = true;
}
//对mOnTouchListener 进行赋值
public void setOnTouchListener(OnTouchListener l) {
    getListenerInfo().mOnTouchListener = l;
}

可以看出mOnTouchListener 的判断取决于View(控件)是否设置
setOnTouchListener()事件;通过位与运算判断控件(View)是否为ENABLED,所以控件显示状态默认为ENABLED!;判断onTouch()事件返回值是否为TRUE;综合—>以上判断若都返回true,则

if (!result && onTouchEvent(event)) {
    result = true;
}

不会执行,
result变量为true; 则dispatchTouchEvent()也会返回;

2- -1在dispatchTouchEvent方法中如果onTouch返回false或者mOnTouchListener为null(控件没有设置setOnTouchListener方法)或者控件不是enable的情况下会调运onTouchEvent:

public boolean onTouchEvent(MotionEvent event) {
    final float x = event.getX();
    final float y = event.getY();
    final int viewFlags = mViewFlags;
    final int action = event.getAction();

    if ((viewFlags & ENABLED_MASK) == DISABLED) {
        if (action == MotionEvent.ACTION_UP && (mPrivateFlags & PFLAG_PRESSED) != 0) {
            setPressed(false);
        }
        return (((viewFlags & CLICKABLE) == CLICKABLE
                || (viewFlags & LONG_CLICKABLE) == LONG_CLICKABLE)
                || (viewFlags & CONTEXT_CLICKABLE) == CONTEXT_CLICKABLE);
    }

    if (mTouchDelegate != null) {
        if (mTouchDelegate.onTouchEvent(event)) {
            return true;
        }
    }

    if (((viewFlags & CLICKABLE) == CLICKABLE ||
            (viewFlags & LONG_CLICKABLE) == LONG_CLICKABLE) ||
            (viewFlags & CONTEXT_CLICKABLE) == CONTEXT_CLICKABLE) {
        switch (action) {
            case MotionEvent.ACTION_UP:
                boolean prepressed = (mPrivateFlags & PFLAG_PREPRESSED) != 0;
                if ((mPrivateFlags & PFLAG_PRESSED) != 0 || prepressed) {
                    boolean focusTaken = false;
                    if (isFocusable() && isFocusableInTouchMode() && !isFocused()) {
                        focusTaken = requestFocus();
                    }

                    if (prepressed) {
                        setPressed(true, x, y);
                   }

                    if (!mHasPerformedLongPress && !mIgnoreNextUpEvent) {
                        removeLongPressCallback();

                        if (!focusTaken) {
                            if (mPerformClick == null) {
                                mPerformClick = new PerformClick();
                            }
                            if (!post(mPerformClick)) {
                                performClick();
                            }
                        }
                    }

                    if (mUnsetPressedState == null) {
                        mUnsetPressedState = new UnsetPressedState();
                    }

                    if (prepressed) {
                        postDelayed(mUnsetPressedState,
                                ViewConfiguration.getPressedStateDuration());
                    } else if (!post(mUnsetPressedState)) {
                        mUnsetPressedState.run();
                    }

                    removeTapCallback();
                }
                mIgnoreNextUpEvent = false;
                break;

            case MotionEvent.ACTION_DOWN:
                mHasPerformedLongPress = false;

                if (performButtonActionOnTouchDown(event)) {
                    break;
                }
                boolean isInScrollingContainer = isInScrollingContainer();
                if (isInScrollingContainer) {
                    mPrivateFlags |= PFLAG_PREPRESSED;
                    if (mPendingCheckForTap == null) {
                        mPendingCheckForTap = new CheckForTap();
                    }
                    mPendingCheckForTap.x = event.getX();
                    mPendingCheckForTap.y = event.getY();
                    postDelayed(mPendingCheckForTap, ViewConfiguration.getTapTimeout());
                } else {
                    setPressed(true, x, y);
                    checkForLongClick(0);
                }
                break;

            case MotionEvent.ACTION_CANCEL:
                setPressed(false);
                removeTapCallback();
                removeLongPressCallback();
                mInContextButtonPress = false;
                mHasPerformedLongPress = false;
                mIgnoreNextUpEvent = false;
                break;

            case MotionEvent.ACTION_MOVE:
                drawableHotspotChanged(x, y);
                if (!pointInView(x, y, mTouchSlop)) {
                    removeTapCallback();
                    if ((mPrivateFlags & PFLAG_PRESSED) != 0) {
                        removeLongPressCallback();

                        setPressed(false);
                    }
                }
                break;
        }

        return true;
    }

    return false;
}

前面几行代码可以看出如果控件(View)是disenable状态,同时是可以clickable的则onTouchEvent直接消费事件返回true,反之如果控件(View)是disenable状态,同时是disclickable的则onTouchEvent直接false。多说一句,关于控件的enable或者clickable属性可以通过java或者xml直接设置,每个view都有这些属性。

if (((viewFlags & CLICKABLE) == CLICKABLE ||
        (viewFlags & LONG_CLICKABLE) == LONG_CLICKABLE) ||
        (viewFlags & CONTEXT_CLICKABLE) == CONTEXT_CLICKABLE) 

这个条件如果不成立,则直接返回false,则进入Action事件判断
<不成立:控件是enable且disclickable>

switch的ACTION_DOWN与ACTION_MOVE都进行了一些必要的设置与置位,接着到手抬起来ACTION_UP时你会发现,首先判断了是否按下过,同时是不是可以得到焦点,然后尝试获取焦点,然后判断如果不是longPressed则通过post在UI Thread中执行一个PerformClick的Runnable,也就是performClick方法

public boolean performClick() {
    final boolean result;
    final ListenerInfo li = mListenerInfo;
    if (li != null && li.mOnClickListener != null) {
        playSoundEffect(SoundEffectConstants.CLICK);
        li.mOnClickListener.onClick(this);
        result = true;
    } else {
        result = false;
    }
    sendAccessibilityEvent(AccessibilityEvent.TYPE_VIEW_CLICKED);
    return result;
}

这个方法定义了一个类型为ListenerInfo 的li变量;判断li.
mOnClickListener ()是否为NULL,根据判断,确定是否执行OnClick()

可以看出:mOnClickListener的赋值:
public boolean hasOnClickListeners() {
    ListenerInfo li = mListenerInfo;
    return (li != null && li.mOnClickListener != null);
}

可以总结出:控件只要监听了OnClick()方法,则mOnClickListener() 就不会为NULL!

总结:
onTouchEvent方法中会在ACTION_UP分支中触发onClick的监听。
当dispatchTouchEvent在进行事件分发的时候,只有前一个action返回true,才会触发下一个action!

三:例子

public class CustomButton extends Button {
    public CustomButton(Context context, AttributeSet attrs, int defStyleAttr) {
        super(context, attrs, defStyleAttr);
    }

    public CustomButton(Context context, AttributeSet attrs) {
        super(context, attrs);
    }

    public CustomButton(Context context) {
        super(context);
    }

    @Override
    public boolean dispatchTouchEvent(MotionEvent event) {
        Log.i("----","dispatchTouchEvent"+event.getAction()+"  ");
        return super.dispatchTouchEvent(event);
    }


    @Override
    public boolean onTouchEvent(MotionEvent event) {
        Log.i("----","onTouchEvent"+event.getAction()+"  ");
        return super.onTouchEvent(event);
    }
}


@Override
public boolean dispatchTouchEvent(MotionEvent event) {
    Log.i("----","dispatchTouchEvent"+event.getAction()+"  ");
    return super.dispatchTouchEvent(event);
}


@Override
public boolean onTouchEvent(MotionEvent event) {
    Log.i("----","onTouchEvent"+event.getAction()+"  ");
    return super.onTouchEvent(event);
}
ACTION_DOWN   ==0
ACTION_UP  ==1
ACTION_MOVE  ==2

1–1:正常情况
dispatchTouchEvent方法先派发down事件返回true,之后调用onTouch的down()事件,返回true,然后调运onTouchEvent的down()返回true,同时dispatchTouchEvent返回true,然后dispatchTouchEvent继续派发move或者up事件,循环,直到onTouchEvent处理up事件时调运onClick事件,完事返回true,同时dispatchTouchEvent返回true;一次完整的View事件派发流程结束。

2–1
修改onTouchEvent的返回值,直接为true,
直接不使用:return super.onTouchEvent(event);
当自定义了控件(View)的onTouchEvent直接返回true而不调运super方法时,事件派发机制类似,只是最后up事件没有触发onClick而已(因为没有调用super),就是说OnClick()无效

3–1

@Override
public boolean onTouchEvent(MotionEvent event) {
    Log.i("----", "onTouchEvent--action      " + event.getAction() + "  ");
    super.onTouchEvent(event);
    return true;
}

修改代码,使用super(),然后保证返回值为true,则View的事件派发机制正常!

4–1:
修改onTouchEvent()返回值为false的时候,可以看到只派发的down()事件就停止了,所以需要理解的就是当返回false的时候,会阻止事件的派发

@Override
public boolean onTouchEvent(MotionEvent event) {
    super.onTouchEvent(event);
    return false;
}

5–1:
修改dispatchTouchEvent()直接返回为true,不调用super(),则任何事件都得不到触发
@Override
public boolean dispatchTouchEvent(MotionEvent event) {
return true;
}

当调用super方法的时候,返回true,则事件可以正常派发

@Override
public boolean dispatchTouchEvent(MotionEvent event) {
    super.dispatchTouchEvent(event);
    return true;
}

6–1:
修改dispatchTouchEvent()直接返回为false,任何事件都不触发

@Override
public boolean dispatchTouchEvent(MotionEvent event) {
    return false;
}

使用时,

dispatchTouchEvent(),onTuch(),onTuchEvent()down()事件

可以执行,因为返回为false,则其他事件无法继续!

@Override
public boolean dispatchTouchEvent(MotionEvent event) {
    super.dispatchTouchEvent(event);
    return false;
}

7–1
修改dispatchTouchEvent返回值为true,onTouchEvent为false:

@Override
public boolean dispatchTouchEvent(MotionEvent event) {
     super.dispatchTouchEvent(event);
    return true;
}


@Override
public boolean onTouchEvent(MotionEvent event) {
    super.onTouchEvent(event);
    return false;
}

8–1
修改dispatchTouchEvent返回值为false,onTouchEvent为true:

@Override
public boolean dispatchTouchEvent(MotionEvent event) {
     super.dispatchTouchEvent(event);
    return false;
}


@Override
public boolean onTouchEvent(MotionEvent event) {
    super.onTouchEvent(event);
    return true;
}

dispatchTouchEvent事件派发是传递的,如果返回值为false将停止下次事件派发,如果返回true将继续下次派发。譬如,当前派发down事件,如果返回true则继续派发up,如果返回false派发完down就停止了!

总结View触摸屏事件传递机制

综合得出Android View的触摸屏事件传递机制有如下特征:
1–触摸控件(View)首先执行dispatchTouchEvent方法。
2–在dispatchTouchEvent方法中先执行onTouch方法,后执行onClick方法!
3–如果控件(View)的onTouch返回false或者mOnTouchListener为null(控件没有设置setOnTouchListener方法)或者控件不是enable的情况下会调运onTouchEvent,dispatchTouchEvent返回值与onTouchEvent返回一样。
4–如果控件不是enable的设置了onTouch方法也不会执行,只能通过重写控件的onTouchEvent方法处理(上面已经处理分析了),dispatchTouchEvent返回值与onTouchEvent返回一样。
5–如果控件(View)是enable且onTouch返回true情况下,dispatchTouchEvent直接返回true,不会调用onTouchEvent方法。
6–当dispatchTouchEvent在进行事件分发的时候,只有前一个action返回true,才会触发下一个action(也就是说dispatchTouchEvent返回true才会进行下一次action派发)。

  • 0
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值