Android:ViewGroup和View的Touch事件


Android中ViewGroup和View中的Touch事件傳遞機制分析

關鍵字:GroupView;View;Touch事件

基礎知識:

onInterceptTouchEvent():在ViewGroup中定義(View中無該方法),用於攔截手勢事件,觸發的每個Touch事件都會先調用onInterceptTouchEvent()。

onTouchEvent():分別View中(ViewGroup繼承自View,自然包含該方法),用於處理傳遞到View中的Touch事件。

View中的onTouchEvent()返回true時,表明:當次已經完全消費了該事件,不希望其他回調方法再次處理;反之則返回false。

事件回調過程:當我們手指點擊屏幕時候,先調用ACTION_DOWN事件,當onTouchEvent()里返回值是true的時候,onTouchEvent()會繼續調用ACTION_UP事件,如果onTouchEvent()里返回值是false,那么onTouch只會調用ACTION_DOWN而不調用ACTION_UP。關於返回值的問題,基本規則很清楚,如果return true,那么表示該方法消費了此次事件,如果return false,那么表示該方法並未處理完全,該事件仍然需要以某種方式傳遞下去繼續等待處理。

Touch事件包含:ACTION_DOWN(getAction()=0),ACTION_UP(getAction()=1),ACTION_MOVE(getAction()=2),ACTION_CANCEL等事件。針對down事件的處理返回值直接影響到后續的move和up事件的接收和傳遞。

Android SDK對onInterceptTouchEvent()的解釋:

由於onInterceptTouchEvent()的機制比較復雜,上面的說明寫的也比較復雜,總結一下,基本的規則是:

1. down事件首先會傳遞到onInterceptTouchEvent()方法;

2. 如果該ViewGroup的onInterceptTouchEvent()在接收到down事件處理完成之后return false,那么后續的move, up等事件將繼續會先傳遞給該ViewGroup,之后才和down事件一樣傳遞給最終的目標view的onTouchEvent()處理;

3. 如果該ViewGroup的onInterceptTouchEvent()在接收到down事件處理完成之后return true,那么后續的move, up等事件將不再傳遞給onInterceptTouchEvent(),而是和down事件一樣傳遞給該ViewGroup的onTouchEvent()處理,注意,目標view將接收不到任何事件;

4. 如果最終需要處理事件的view的onTouchEvent()返回了false,那么該事件將被傳遞至其上一層次的view的onTouchEvent()處理;

5. 如果最終需要處理事件的view 的onTouchEvent()返回了true,那么后續事件將可以繼續傳遞給該view的onTouchEvent()處理。

現通過如下的簡單實例分析Android中的點擊事件:

其中,整個布局是一個自定義ViewGroup,下面的Button同樣是自定義View。

整體布局如下:

<com.demo.view.DesignLayout xmlns:android="http://schemas.android.com/apk/res/android"
    xmlns:tools="http://schemas.android.com/tools"
    android:layout_width="match_parent"
    android:layout_height="match_parent"
    tools:context="com.demo.TouchEventDemo" >
    <com.demo.view.DesignView
        android:layout_width="match_parent"
        android:layout_height="wrap_content"
        android:text="@string/hello_world" />
</com.demo.view.DesignLayout>

自定義的ViewGroup為:

package com.demo.view;
import android.content.Context;
import android.util.AttributeSet;
import android.view.MotionEvent;
import android.widget.FrameLayout;
import com.demo.LogUtil;
public class DesignLayout extends FrameLayout {
    private static final String TAG = DesignLayout.class.getSimpleName();
    public DesignLayout(Context context, AttributeSet attrs) {
        super(context, attrs);
    }
    public DesignLayout(Context context) {
        super(context);
    }
    @Override
    public boolean onInterceptTouchEvent(MotionEvent ev) {
        // 默認返回false
        LogUtil.d(TAG,
                "DesignLayout:onInterceptTouchEvent::super.onInterceptTouchEvent(ev)="
                        + super.onInterceptTouchEvent(ev));
        return false;
    }
    @Override
    public boolean onTouchEvent(MotionEvent event) {
        // 默認返回false
        LogUtil.d(TAG, "DesignLayout:onTouchEvent::super.onTouchEvent(event)="
                + super.onTouchEvent(event));
        return super.onTouchEvent(event);
    }
}

自定義的View:

package com.demo.view;
import android.content.Context;
import android.util.AttributeSet;
import android.view.MotionEvent;
import android.widget.Button;
import com.demo.LogUtil;
public class DesignView extends Button {
    private static final String TAG = DesignView.class.getSimpleName();
    public DesignView(Context context, AttributeSet attrs) {
        super(context, attrs);
    }
    public DesignView(Context context) {
        super(context);
    }
    @Override
    public boolean onTouchEvent(MotionEvent event) {
        // 默認返回true
        LogUtil.d(TAG, "DesignView:onTouchEvent::super.onTouchEvent(event)="
                + super.onTouchEvent(event));
        return false;
    }
}

主體的TouchEventActivity如下:

package com.demo;
import android.app.Activity;
import android.os.Bundle;
public class TouchEventDemo extends Activity {
    @Override
    protected void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        setContentView(R.layout.main);
    }
}

測試結果如下:

Layout中的onInterceptTouchEvent()默認返回的是false,表示不攔截Touch事件,也就是說:Touch事件將傳遞到View控件中;onTouchEvent(),默認返回的是false;

View中的onTouchEvent()默認返回的是true;

實驗一:Layout中onInterceptTouchEvent()默認返回的是false,View的onTouchEvent()返回true,執行以下邏輯:

D/Demo    (13207): [DesignLayout] DesignLayout:onInterceptTouchEvent::super.onInterceptTouchEvent(ev)=false
D/Demo    (13207): [DesignLayout] onInterceptTouchEvent::MotionEvent.ACTION_DOWN
D/Demo    (13207): [DesignView] DesignView:onTouchEvent::super.onTouchEvent(event)=true
D/Demo    (13207): [DesignView] DesignView:onTouchEvent::MotionEvent.ACTION_DOWN
D/Demo    (13207): [DesignLayout] DesignLayout:onInterceptTouchEvent::super.onInterceptTouchEvent(ev)=false
D/Demo    (13207): [DesignLayout] onInterceptTouchEvent::MotionEvent.ACTION_MOVE
D/Demo    (13207): [DesignView] DesignView:onTouchEvent::super.onTouchEvent(event)=true
D/Demo    (13207): [DesignView] DesignView:onTouchEvent::MotionEvent.ACTION_MOVE
D/Demo    (13207): [DesignLayout] DesignLayout:onInterceptTouchEvent::super.onInterceptTouchEvent(ev)=false
D/Demo    (13207): [DesignLayout] onInterceptTouchEvent::MotionEvent.ACTION_MOVE
D/Demo    (13207): [DesignView] DesignView:onTouchEvent::super.onTouchEvent(event)=true
D/Demo    (13207): [DesignView] DesignView:onTouchEvent::MotionEvent.ACTION_MOVE
D/Demo    (13207): [DesignLayout] DesignLayout:onInterceptTouchEvent::super.onInterceptTouchEvent(ev)=false
D/Demo    (13207): [DesignLayout] onInterceptTouchEvent::MotionEvent.ACTION_UP
D/Demo    (13207): [DesignView] DesignView:onTouchEvent::super.onTouchEvent(event)=true
D/Demo    (13207): [DesignView] DesignView:onTouchEvent::MotionEvent.ACTION_UP

Layout不攔截Touch事件,傳遞到子View中;子View消費Touch事件,不會去執行父ViewGroup的onTouchEvent(),本次Touch事件已消費並停止;Layout和子View會多次接收和消費其他Touch事件,比如ACTION_MOVE和ACTION_UP事件。

實驗二:讓ViewGroup的onInterceptTouchEvent()返回默認的false且onTouchEvent()返回false,但是讓View的onTouchEvent()返回false,執行如下邏輯:

D/Demo    (13869): [DesignLayout] DesignLayout:onInterceptTouchEvent::super.onInterceptTouchEvent(ev)=false
D/Demo    (13869): [DesignLayout] onInterceptTouchEvent::MotionEvent.ACTION_DOWN
D/Demo    (13869): [DesignView] DesignView:onTouchEvent::super.onTouchEvent(event)=true
D/Demo    (13869): [DesignView] DesignView:onTouchEvent::MotionEvent.ACTION_DOWN
D/Demo    (13869): [DesignLayout] DesignLayout:onTouchEvent::super.onTouchEvent(event)=false
D/Demo    (13869): [DesignLayout] onTouchEvent::MotionEvent.ACTION_DOWN

Touch事件未被ViewGroup攔截,並傳遞到底層的View中;View的onTouchEvent()返回false時,表明不消費該Touch事件,會回傳到其父ViewGroup的onTouchEvent()中。View里的onTouchEvent只消費了一次點擊事件也就是ACTION_DOWN,還沒有執行ACTION_UP,然后跑到ViewGroup里又去執行OnTouchEvent事件;此時ViewGroup中onTouchEvent()返回為false,也僅會消費一次ACTION_DOWN事件(或者說,本次Touch事件並未被消費,所以其他的ACTION_MOVE和ACTION_UP事件無法接收)。

點擊非Button區域時,也只會去執行ViewGroup的onTouchEvent(),並消費一次ACTION_DOWN。

D/Demo    (14433): [DesignLayout] DesignLayout:onInterceptTouchEvent::super.onInterceptTouchEvent(ev)=false
D/Demo    (14433): [DesignLayout] onInterceptTouchEvent::MotionEvent.ACTION_DOWN
D/Demo    (14433): [DesignLayout] DesignLayout:onTouchEvent::super.onTouchEvent(event)=false
D/Demo    (14433): [DesignLayout] onTouchEvent::MotionEvent.ACTION_DOWN

實驗三:讓ViewGroup的onInterceptTouchEvent()返回默認的false且onTouchEvent()返回true,但是讓View的onTouchEvent()返回false,執行如下邏輯:

點擊Button區域,並滑動時:

D/Demo    (14631): [DesignLayout] DesignLayout:onInterceptTouchEvent::super.onInterceptTouchEvent(ev)=false
D/Demo    (14631): [DesignLayout] onInterceptTouchEvent::MotionEvent.ACTION_DOWN
D/Demo    (14631): [DesignView] DesignView:onTouchEvent::super.onTouchEvent(event)=true
D/Demo    (14631): [DesignView] DesignView:onTouchEvent::MotionEvent.ACTION_DOWN
D/Demo    (14631): [DesignLayout] DesignLayout:onTouchEvent::super.onTouchEvent(event)=false
D/Demo    (14631): [DesignLayout] onTouchEvent::MotionEvent.ACTION_DOWN
D/Demo    (14631): [DesignLayout] DesignLayout:onTouchEvent::super.onTouchEvent(event)=false
D/Demo    (14631): [DesignLayout] onTouchEvent::MotionEvent.ACTION_MOVE
D/Demo    (14631): [DesignLayout] DesignLayout:onTouchEvent::super.onTouchEvent(event)=false
D/Demo    (14631): [DesignLayout] onTouchEvent::MotionEvent.ACTION_UP

Touch事件未被ViewGroup攔截,並傳遞到底層的View中;View的onTouchEvent()返回false時,表明不消費該Touch事件,會回傳到其父ViewGroup的onTouchEvent()中。View里的onTouchEvent只接收一次點擊事件也就是ACTION_DOWN(不再接收其他Touch事件),還沒有執行ACTION_UP,然后跑到ViewGroup里又去執行OnTouchEvent事件;此時ViewGroup中onTouchEvent()返回為true,也就是消費了本次ACTION_DOWN事件(並表明身份:我能消費Touch事件,以后的事件都由我來消費吧!)。

點擊非Button區域,並滑動時:

D/Demo    (14631): [DesignLayout] DesignLayout:onInterceptTouchEvent::super.onInterceptTouchEvent(ev)=false
D/Demo    (14631): [DesignLayout] onInterceptTouchEvent::MotionEvent.ACTION_DOWN
D/Demo    (14631): [DesignLayout] DesignLayout:onTouchEvent::super.onTouchEvent(event)=false
D/Demo    (14631): [DesignLayout] onTouchEvent::MotionEvent.ACTION_DOWN
D/Demo    (14631): [DesignLayout] DesignLayout:onTouchEvent::super.onTouchEvent(event)=false
D/Demo    (14631): [DesignLayout] onTouchEvent::MotionEvent.ACTION_MOVE
D/Demo    (14631): [DesignLayout] DesignLayout:onTouchEvent::super.onTouchEvent(event)=false
D/Demo    (14631): [DesignLayout] onTouchEvent::MotionEvent.ACTION_UP

每次點擊、滑動並抬起時,其消費模式:一次ACTION_DOWN、多次ACTION_MOVE和一次ACTION_UP事件。

實驗四:將Layout中的onInterceptTouchEvent()返回值修改為true且onTouchEvent()返回默認值false。點擊包含有View的Button區域時,執行下述邏輯:

D/Demo    (10417): [DesignLayout] DesignLayout:onInterceptTouchEvent::super.onInterceptTouchEvent(ev)=false
D/Demo    (10417): [DesignLayout] onInterceptTouchEvent::MotionEvent.ACTION_DOWN
D/Demo    (10417): [DesignLayout] DesignLayout:onTouchEvent::super.onTouchEvent(event)=false

不會執行到View中的onTouchEvent(),表明Touch事件被ViewGroup攔截,會執行ViewGroup的onTouchEvent()(本次只會傳遞一次ACTION_DOWN事件,不會傳遞其他事件如:ACTION_MOVE和ACTION_UP)。點擊非Button區域時,Logcat打印結果和上述相同。

實驗五:實驗環境同實驗二,不同之處在於ViewGroup的onTouchEvent()返回true,點擊包含有View的Button區域時,執行以下邏輯:

D/Demo    (15835): [DesignLayout] DesignLayout:onInterceptTouchEvent::super.onInterceptTouchEvent(ev)=false
D/Demo    (15835): [DesignLayout] onInterceptTouchEvent::MotionEvent.ACTION_DOWN
D/Demo    (15835): [DesignLayout] DesignLayout:onTouchEvent::super.onTouchEvent(event)=false
D/Demo    (15835): [DesignLayout] onTouchEvent::MotionEvent.ACTION_DOWN
D/Demo    (15835): [DesignLayout] DesignLayout:onTouchEvent::super.onTouchEvent(event)=false
D/Demo    (15835): [DesignLayout] onTouchEvent::MotionEvent.ACTION_MOVE
D/Demo    (15835): [DesignLayout] DesignLayout:onTouchEvent::super.onTouchEvent(event)=false
D/Demo    (15835): [DesignLayout] onTouchEvent::MotionEvent.ACTION_MOVE
D/Demo    (15835): [DesignLayout] DesignLayout:onTouchEvent::super.onTouchEvent(event)=false
D/Demo    (15835): [DesignLayout] onTouchEvent::MotionEvent.ACTION_UP

不會執行到View中的onTouchEvent(),表明Touch事件被ViewGroup攔截,會執行ViewGroup的onTouchEvent();但不同之處在於:不但會接收到ACTION_DOWN事件,其他事件也會接收到如:ACTION_MOVE和ACTION_UP。

點擊非Button區域時,執行以下邏輯:

D/Demo    (15835): [DesignLayout] DesignLayout:onInterceptTouchEvent::super.onInterceptTouchEvent(ev)=false
D/Demo    (15835): [DesignLayout] onInterceptTouchEvent::MotionEvent.ACTION_DOWN
D/Demo    (15835): [DesignLayout] DesignLayout:onTouchEvent::super.onTouchEvent(event)=false
D/Demo    (15835): [DesignLayout] onTouchEvent::MotionEvent.ACTION_DOWN
D/Demo    (15835): [DesignLayout] DesignLayout:onTouchEvent::super.onTouchEvent(event)=false
D/Demo    (15835): [DesignLayout] onTouchEvent::MotionEvent.ACTION_MOVE
D/Demo    (15835): [DesignLayout] DesignLayout:onTouchEvent::super.onTouchEvent(event)=false
D/Demo    (15835): [DesignLayout] onTouchEvent::MotionEvent.ACTION_UP
D/Demo    (15835): [DesignLayout] DesignLayout:onTouchEvent::super.onTouchEvent(event)=false
D/Demo    (15835): [DesignLayout] onTouchEvent::MotionEvent.ACTION_MOVE


免責聲明!

本站轉載的文章為個人學習借鑒使用,本站對版權不負任何法律責任。如果侵犯了您的隱私權益,請聯系本站郵箱yoyou2525@163.com刪除。



 
粵ICP備18138465號   © 2018-2025 CODEPRJ.COM