Android触摸事件

来源:互联网 发布:淘宝设置信用卡支付 编辑:程序博客网 时间:2024/05/18 02:29


1 Android中三种比较重要的类型动作ACTION_DOWN,ACTION_MOVE,ACTION_UP。

2 ACTION_DOWN事件未处理时,后面的ACTION_MOVE,ACTION_UP也不会处理,,否则就成了无源之水,前面的动作是后面两个动作的基础。

3 ACTION_CANCEL发生的条件。若是ACTION_DOWN(或者ACTION_MOVE)时父View 的onInterceptTouchEvent返回false ,而在ACTION_MOVE(或者ACTION_UP)时父View onInterceptTouchEvent返回true,则会触发子View的ACTION_CANCEL事件。

3 ViewGroup包括

4 dispatchTouchEvent——》onInterceptTouchEvent——》onTouchEvent,按照此顺序进行动作的传递处理,尤其注意的是,每一种类型动作都是独立的从上到下传递

处理完成之后,然后在进行下一类型动作的传递处理。

5 对于View而言,触摸事件有两种处理方式,1:setOnTouchListener(OnTouchListener l);2 onTouchEvent()

5 onClick ,onLongClick等点击事件都是在onTouchEvent()中调用的。

6当你点击了某个控件,首先会去调用该控件所在布局的dispatchTouchEvent方法,然后在布局的dispatchTouchEvent方法中找到被点击的相应控件,再去调用该控件的dispatchTouchEvent方法。

每一个view的event方法有且只会被调用一次,而他们的返回值true:代表我要用这次这个touch事件,那么down事件之后的move、up都会传入此event方法;若返回值为false:代表不需要这个touch事件,那么如果有down事件经过之后的move、up事件都不会再被传入。
dispatchTouchEvent默认返回值:true
onInterceptTouchEvent默认返回值:false
onTouchEvent默认返回值:最外部可被点击的View默认值为true,父容器的默认值为false


8 若没有重写View.dispatchTouchEvent(),那么View.onTouchEvent()返回true,则父view认为子View消费了此次事件,即父View不会调用自己的onTouch()方法。但是若重写了View.dispatchTouchEvent(),并且返回true,则不管View.onTouchEvent()是否返回true,父View都会认为子View消费了此次事件,也就不会调用父View的onTouchEvent()方法,这是因为onTouchEvent()最终都是在dispatchTouchEvent()中调用的,因此确认一次事件最终是否被消费,都是通过dispatchTouchEvent()的返回值进行判断的。


在Activity的dispatchTouchEvent方法中直接将事件指定给了特定组件,如下:

<span style="font-size:18px;">public class MainActivity extends Activity {    private Button myButton;    @Override    public void onCreate(Bundle savedInstanceState) {        super.onCreate(savedInstanceState);        setContentView(R.layout.main);        myButton=(Button) findViewById(R.id.button);    }    @Override    public boolean dispatchTouchEvent(MotionEvent ev) {    return myButton.dispatchTouchEvent(ev);    }@Overridepublic boolean onTouchEvent(MotionEvent event) {switch (event.getAction()) {case MotionEvent.ACTION_DOWN:break;case MotionEvent.ACTION_MOVE:break;case MotionEvent.ACTION_UP:break;}return super.onTouchEvent(event);}}</span>


ViewGroup里含有:dispatchTouchEvent、onInterceptTouchEvent、onTouchEvent
View里只含有:dispatchTouchEvent、onTouchEvent



dispatchTouchEvent无论return true还是return false,事件都不再进行分发,

只有当其return super.dispatchTouchEvent(ev),才表明其具有向下层分发的愿望,

但是是否能够分发成功,则需要经过事件拦截onInterceptTouchEvent的审核。





一 Activity:包括dispatchTouchEvent()和onTouchEvent()两种处理

dispatchTouchEvent()

 public boolean dispatchTouchEvent(MotionEvent ev) {        if (ev.getAction() == MotionEvent.ACTION_DOWN) {            onUserInteraction();        }        if (getWindow().superDispatchTouchEvent(ev)) {//if里面的语句很重要,所有的事件分发和处理都是从此处开始的            return true;        }        return onTouchEvent(ev);  }
          (1)onUserInteraction()方法是空方法,暂且不管.

     *  (2)调用getWindow().superDispatchTouchEvent(ev)
     *     即调用了PhoneWindow的superDispatchTouchEvent(ev)方法.
     *     @Override
     *     public boolean superDispatchTouchEvent(MotionEvent event) {
     *        return mDecor.superDispatchTouchEvent(event);
     *     }
     *     在该方法中会调用DecorView的superDispatchTouchEvent(event)方法.
     *     DecorView是一个定义在PhoneWindow中的一个内部类.定义如下:
     *     private final class DecorView extends FrameLayout implements RootViewSurfaceTaker{}
     *     发现没有,它是继承自FrameLayout的?其实,系统会对任意一个Activity的最外层布局嵌套一个Framindow().superDispatchTouchEvent(ev)方法返回的false即事件未被消费.
     *    此时if条件eLayout.
     *     嗯哼,是不是暗示着什么........
     *     继续看DecorView的superDispatchTouchEvent(event)方法,源码如下:
     *     public boolean superDispatchTouchEvent(MotionEvent event) {
     *        return super.dispatchTouchEvent(event);
     *     }
     *     在该方法中方法中调用了super.dispatchTouchEvent(event);
     *     即调用了FrameLayout(也就是ViewGroup)的dispatchTouchEvent(event);
     *     剩下的流程就和ViewGroup的事件分发一致了.
     * (3)如果getW不满足,于是代码继续往下执行那么就会调用onTouchEvent(ev)
     *    也就是Activity的onTouchEvent(MotionEvent event).

onTouchEvent()


  • 二 ViewGroup:包括dispatchTouchEvent(),onInterceptTouchEvent()和onTouchEvent()三种处理


三 View:包括dispatchTouchEvent()和onTouchEvent()两种处理

   1. dispatchTouchEvent()

注意View有两种实现触摸事件方式,第一种是View.setOnTouchListener(OnTouchListener l),第二种是覆盖View的onTouchEvent(MotionEvent event)方法,如果View设置了setOnTouchListener并且在方法onTouch中返回true,那么即使View重写了onTouchEvent方法,其中的代码也不会执行,这可以通过下面的if语句进行判断,即第一种方式优先级高于第二种。同事view的click点击事件是在onTouchEvent()中处理的。

/**
     * 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 (!onFilterTouchEventForSecurity(event)) {
            return false;
        }

        if (mOnTouchListener != null && (mViewFlags & ENABLED_MASK) == ENABLED &&
                mOnTouchListener.onTouch(this, event)) {
            return true;
        }
        return onTouchEvent(event);

    }


      2. onTouchEvent()

/**     * Implement this method to handle touch screen motion events.     *     * @param event The motion event.     * @return True if the event was handled, false otherwise.     */    public boolean onTouchEvent(MotionEvent event) {        final int viewFlags = mViewFlags;
<span style="white-space:pre"></span>//<span style="color: rgb(51, 51, 51); font-family: Arial; font-size: 14.166666030883789px; line-height: 25.989582061767578px;">如果当前View是Disabled状态且是可点击则会消费掉事件(return true),如果不可点击则不消费此次事件(return false);</span>        if ((viewFlags & ENABLED_MASK) == DISABLED) {            // A disabled view that is clickable still consumes the touch            // events, it just doesn't respond to them.            return (((viewFlags & CLICKABLE) == CLICKABLE ||                    (viewFlags & LONG_CLICKABLE) == LONG_CLICKABLE));        }
<span style="white-space:pre"></span>//<span style="color: rgb(51, 51, 51); font-family: Arial; font-size: 14.166666030883789px; line-height: 25.989582061767578px;">TouchDelegate是用来增加控件的触摸范围的,<span style="color: rgb(51, 51, 51); font-family: Arial; font-size: 14.166666030883789px; line-height: 25.989582061767578px;">假设有两个View,分别是v1,v2,我们可以通过v1的setTouchDelegate(bounds, v2)来委      //派触摸事件,其中bounds是一个Rect。v1中,落在这个范围的TouchEvent都会传给v2。</span></span>        if (mTouchDelegate != null) {            if (mTouchDelegate.onTouchEvent(event)) {                return true;            }        }        if (((viewFlags & CLICKABLE) == CLICKABLE ||                (viewFlags & LONG_CLICKABLE) == LONG_CLICKABLE)) {            switch (event.getAction()) {                case MotionEvent.ACTION_UP:                    boolean prepressed = (mPrivateFlags & PREPRESSED) != 0;                    if ((mPrivateFlags & PRESSED) != 0 || prepressed) {                        // take focus if we don't have it already and we should in                        // touch mode.                        boolean focusTaken = false;                        if (isFocusable() && isFocusableInTouchMode() && !isFocused()) {                            focusTaken = requestFocus();                        }                        if (!mHasPerformedLongPress) {                            // This is a tap, so remove the longpress check                            removeLongPressCallback();                            // Only perform take click actions if we were in the pressed state                            if (!focusTaken) {                                // Use a Runnable and post this rather than calling                                // performClick directly. This lets other visual state                                // of the view update before click actions start.                                if (mPerformClick == null) {                                    mPerformClick = new PerformClick();                                }                                if (!post(mPerformClick)) {                                    performClick();                                }                            }                        }                        if (mUnsetPressedState == null) {                            mUnsetPressedState = new UnsetPressedState();                        }                        if (prepressed) {                            mPrivateFlags |= PRESSED;                            refreshDrawableState();                            postDelayed(mUnsetPressedState,                                    ViewConfiguration.getPressedStateDuration());                        } else if (!post(mUnsetPressedState)) {                            // If the post failed, unpress right now                            mUnsetPressedState.run();                        }                        removeTapCallback();                    }                    break;                case MotionEvent.ACTION_DOWN:                    if (mPendingCheckForTap == null) {                        mPendingCheckForTap = new CheckForTap();                    }                    mPrivateFlags |= PREPRESSED;
<span style="white-space:pre"></span>//<span style="color: rgb(51, 51, 51); font-family: Arial; font-size: 14.166666030883789px; line-height: 25.989582061767578px;">设置mHasPerformedLongPress=false;表示长按事件还未触发</span>                    mHasPerformedLongPress = false;
<span style="white-space:pre"></span>//<span style="color: rgb(51, 51, 51); font-family: Arial; font-size: 14.166666030883789px; line-height: 25.989582061767578px;">发送一个延迟为ViewConfiguration.getTapTimeout()的延迟消息,到达延时时间后会执行CheckForTap()里面的run方法</span>
<span style="white-space:pre"></span>//长按事件在<span style="color: rgb(51, 51, 51); font-family: Arial; font-size: 14.166666030883789px; line-height: 25.989582061767578px;">CheckForTap中处理</span>                    postDelayed(mPendingCheckForTap, ViewConfiguration.getTapTimeout());                    break;                case MotionEvent.ACTION_CANCEL:                    mPrivateFlags &= ~PRESSED;                    refreshDrawableState();                    removeTapCallback();                    break;                case MotionEvent.ACTION_MOVE:                    final int x = (int) event.getX();                    final int y = (int) event.getY();                    // Be lenient about moving outside of buttons                    int slop = mTouchSlop;                    if ((x < 0 - slop) || (x >= getWidth() + slop) ||                            (y < 0 - slop) || (y >= getHeight() + slop)) {                        // Outside button                        removeTapCallback();                        if ((mPrivateFlags & PRESSED) != 0) {                            // Remove any future long press/tap checks                            removeLongPressCallback();                            // Need to switch from pressed to not pressed                            mPrivateFlags &= ~PRESSED;                            refreshDrawableState();                        }                    }                    break;            }            return true;        }        return false;    }




0 0
原创粉丝点击