android 事件分發(fā)

事件分發(fā)的核心方法

組件 dispatchTouchEvent onTouchEvent onInterceptTouchEvent
Activity 存在 存在 不存在
ViewGroup 存在 存在 存在
View 存在 存在 不存在

事件分發(fā)流程圖

image.png

源碼分析

1、事件從點(diǎn)擊屏幕時(shí)觸發(fā),由手指點(diǎn)擊屏幕,硬件通知底層,再調(diào)用java層,最后調(diào)用Activity的 public boolean dispatchTouchEvent(MotionEvent ev) 方法

/**
 * 當(dāng)一個(gè)點(diǎn)擊操作發(fā)生時(shí),事件傳遞順序 Acivity - > Window ->DecorView DecorView 一般
 * 就是當(dāng)前界面的底層容器(即setContentView 所設(shè)置的View的 父容器),一個(gè)點(diǎn)擊操作要是沒有被Activity
 * 下的任何View處理,即頂層DecorView的dispatchTouchEvent()方法返回false的話
 * 則Activity的onTouchEvent()方法會(huì)調(diào)用
 */
public boolean dispatchTouchEvent(MotionEvent ev) {
    // 如果是down,說明是一個(gè)新的事件
    if (ev.getAction() == MotionEvent.ACTION_DOWN) {
        onUserInteraction(); // 空方法 添加自己的業(yè)務(wù)
    }
    //注釋1 調(diào)用Window的superDispatchTouchEvent方法,吧事件從Activity分離到DecorView
    if (getWindow().superDispatchTouchEvent(ev)) {
        return true;  //如果返回true
    }
    return onTouchEvent(ev);
}

注釋1中調(diào)用Window的superDispatchTouchEvent,也就是調(diào)用PhoneWindow的superDispatchTouchEvent,而PhoneWindow的superDispatchTouchEvent又是調(diào)用DecorView的superDispatchTouchEvent,DecorView本身就是ViewGroup的實(shí)現(xiàn)類,相當(dāng)于調(diào)用了ViewGroup的dispatchTouchEvent

## PhoneWindow
@Override
public boolean superDispatchTouchEvent(MotionEvent event) {
    return mDecor.superDispatchTouchEvent(event);
}

## DecorView
public boolean superDispatchTouchEvent(MotionEvent event) {
    return super.dispatchTouchEvent(event);
}

滑動(dòng)沖突的解決方法

1、制定合適的滑動(dòng)策略
2、按滑動(dòng)策略分發(fā)事件
制定合適的滑動(dòng)策略
如何判斷水平滑動(dòng)還是豎直滑動(dòng)?1、可以根據(jù)水平方向或者豎直方向的距離差來判斷。2、可以根據(jù)滑動(dòng)路徑和水平方向的夾角來判斷,小于30°則為水平方向,大于60°為豎直方向,30到60之間不處理,或者根據(jù)具體業(yè)務(wù)需求

image.png

按滑動(dòng)策略分發(fā)事件

  • 外部攔截法
    外部攔截法一般用在容器中,父容器攔截子容器的事件,子容器沒有響應(yīng)。
public boolean onInterceptTouchEvent(MotionEvent event) {
    boolean intercepted = false;
    switch (event.getAction()) {
        case MotionEvent.ACTION_DOWN:
            intercepted = false; // 必須false,否則后續(xù)的MOVE和UP不再傳遞給子View
            break;
        case MotionEvent.ACTION_MOVE:
            if (父容器需要當(dāng)前點(diǎn)擊事件) {
                intercepted = true;
            } else {
                intercepted = false;
            }
            break;
        case MotionEvent.ACTION_UP:
            intercepted = false; // 必須false,會(huì)影響子View的onClick是否被觸發(fā)
            break;
    }
    return intercepted;
}
  • 內(nèi)部攔截法
    內(nèi)部攔截法一般在子容器中實(shí)現(xiàn),比如在ScrollView中嵌套的ScrollView,在第二個(gè)ScrollView中實(shí)現(xiàn)是否攔截事件。
public boolean dispatchTouchEvent(MotionEvent event) {
    switch (event.getAction()) {
        case MotionEvent.ACTION_DOWN:
            // 關(guān)閉父元素的攔截功能,當(dāng)前容器攔截
            requestDisallowInterceptTouchEvent(true);
            break;
        case MotionEvent.ACTION_MOVE:
            if (父容器需要此類點(diǎn)擊事件) {
                // 激活父元素原本的攔截功能 ,父容器攔截
                requestDisallowInterceptTouchEvent(false);
                break;
            }
            break;
        case MotionEvent.ACTION_UP:
            break;
    }
    return super.dispatchTouchEvent(event);
}

判斷滑動(dòng)的方向

/**
     * @param startX 按下x  這個(gè)值不能在 onTouchEvent當(dāng)局部變量,因?yàn)榫植孔兞棵看伟聪聏,y都是0
     * @param startY 按下y
     * @param endX   結(jié)束x
     * @param endY   結(jié)束y
     * @return top = 1  bottom = 2 left = 3 right = 4
     */
    public int getSlideDirection(float startX, float startY, float endX, float endY) {
        final int top = 1;
        final int bottom = 2;
        final int left = 3;
        final int right = 4;

        float x = Math.abs(endX - startX);
        float y = Math.abs(endY - startY);
        double z = Math.sqrt(x * x + y * y);
        int jiaodu = Math.round((float) (Math.asin(y / z) / Math.PI * 180));//角度
        if (endY < startY && jiaodu > 45) {//上
            Log.d(TAG, "角度:" + jiaodu + ", 動(dòng)作:上");
            return top;
        } else if (endY > startY && jiaodu > 45) {//下
            Log.d(TAG, "角度:" + jiaodu + ", 動(dòng)作:下");
            return bottom;
        } else if (endX < startX && jiaodu <= 45) {//左
            Log.d(TAG, "角度:" + jiaodu + ", 動(dòng)作:左");
            return left;
        } else if (endX > startX && jiaodu <= 45) {//右
            Log.d(TAG, "角度:" + jiaodu + ", 動(dòng)作:右");
            return right;
        }
        return -1;
    }

    float X = 0;
    float Y = 0;

    public boolean onTouchEvent(MotionEvent event) {
        switch (event.getAction()) {
            case MotionEvent.ACTION_DOWN: // 按下
                X = event.getX();
                Y = event.getY();
            case MotionEvent.ACTION_UP: // 抬起
                float upX = event.getX();
                float upY = event.getY();
                getSlideDirection(X, Y, upX, upY);
        }
        return super.onTouchEvent(event);
    }
最后編輯于
?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請(qǐng)聯(lián)系作者
【社區(qū)內(nèi)容提示】社區(qū)部分內(nèi)容疑似由AI輔助生成,瀏覽時(shí)請(qǐng)結(jié)合常識(shí)與多方信息審慎甄別。
平臺(tái)聲明:文章內(nèi)容(如有圖片或視頻亦包括在內(nèi))由作者上傳并發(fā)布,文章內(nèi)容僅代表作者本人觀點(diǎn),簡(jiǎn)書系信息發(fā)布平臺(tái),僅提供信息存儲(chǔ)服務(wù)。

相關(guān)閱讀更多精彩內(nèi)容

友情鏈接更多精彩內(nèi)容