2013-07-24 147 views
3

我有一個自定義視圖,其中有觸摸事件(滑動等)的功能。現在可能會發生,這個自定義視圖在ScrollableLayout中使用。問題在於,當用戶在我的自定義視圖中滑動時,父級(ScrollableLayout)也將處理滑動手勢,因此它會滾動,但不應該。如何防止TouchEvent滾動

我需要類似於JavaScript的event.preventDefaults()

我覆蓋View#onTouchEvent並始終返回true。 我想,當我從onTouchEvent返回true時,這意味着事件被消耗,沒有其他視圖會獲得onTouchEvent,但這是錯誤的。

任何人都可以幫助我嗎?

我的看法是很容易的對它進行測試:

public class PreventingTouchEventView extends View { 

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

    @Override 
    protected void onDraw(Canvas canvas) { 
     canvas.drawColor(Color.RED); 
    } 

    @Override 
    protected void onMeasure(int widthMeasureSpec, int heightMeasureSpec) { 
     setMeasuredDimension(200, 200); 
    } 

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

我推一個例子Android項目到GitHub上: https://github.com/jjoe64/android-preventing-touch-test

如果觸摸和紅色帆布內滾動時,ScrollableLayout應該滾動。

回答

12

爲了防止從佈局倒是有以下幾個選項:

1)設置OnTouchListener,這將始終返回true。

2)覆蓋dispatchTouchEvent(MotionEvent事件)總是返回true

UPD:歐凱,通常你必須設置OnTouchListener,這將始終返回true,正如我前面所說,以防止被其他視圖稱爲onTouch,但與ScrollView它完全是另一回事。

首先我會告訴dispatchTouchEvent是如何工作的。它開始從根視圖調度事件到它的子視圖,這意味着父節點的dispatchTouchEvent被稱爲之前dispatchTouchEvent它的子節點。

然後在ViewGroup.dispatchTouchEvent()有一段代碼

final boolean disallowIntercept = (mGroupFlags & FLAG_DISALLOW_INTERCEPT) != 0; 
if (!disallowIntercept) { 
    intercepted = onInterceptTouchEvent(ev); 
    ev.setAction(action); // restore action in case it was changed 
} else { 
    intercepted = false; 
} 

它決定如果這個觸摸事件應被攔截,這意味着如果intercepted標誌爲真,則該視圖將攔截所有連續事件。並在ScrollViewonInterceptTouchEvent實施攔截行動,如果他們正在執行垂直滾動。

所以我的第一個想法是設定FLAG_DISALLOW_INTERCEPT您的自定義視圖的父母不允許這個機會攔截事件,但這個想法失敗,因爲代碼在ViewGroup下一行:

if (actionMasked == MotionEvent.ACTION_DOWN) { 
    // Throw away all previous state when starting a new touch gesture. 
    // The framework may have dropped the up or cancel event for the previous gesture 
    // due to an app switch, ANR, or some other state change. 
    cancelAndClearTouchTargets(ev); 
    resetTouchState(); 
} 

使意味着在MotionEvent.ACTION_DOWN所有狀態被清除後(FLAG_DISALLOW_INTERCEPT也被清除)。

所以最終的解決方案是非常簡單的

public class PreventingTouchEventView extends View { 

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

    @Override 
    protected void onDraw(Canvas canvas) { 
     canvas.drawColor(Color.RED); 
    } 

    @Override 
    public boolean dispatchTouchEvent(MotionEvent event) { 
     if (getParent() != null && event.getAction() == MotionEvent.ACTION_DOWN) { 
      getParent().requestDisallowInterceptTouchEvent(true); 
     } 
     return super.dispatchTouchEvent(event); 
    } 
} 

這將第一MotionEvent.ACTION_DOWN權後募集FLAG_DISALLOW_INTERCEPT,這將dissallow母公司未來行動的進一步攔截。

但請注意,如果你的父視圖上做一些MotionEvent.ACTION_DOWN瘋狂的東西,並攔截此事件,那麼你就不能做任何事情,因爲你的父母的dispatchTouchEvent是您dispatchTouchEvent之前調用。

+0

在這裏相同。這些更改屬於父視圖。我只能改變我自己的自定義視圖... – appsthatmatter

+0

即使我可以對父級進行更改,也會完全禁用滾動。當我觸摸我的自定義視圖時,它只應該不滾動。 – appsthatmatter

+0

你爲什麼說這個改變屬於父視圖?您只將「OnTouchListener」設置爲您的自定義視圖,因此如果您觸摸視圖外部 - 另一個視圖應該處理此事件,對於您的情況它是「ScrollableLayour」(您的自定義視圖的父級)。 – Desert

1

你必須覆蓋父的onInterceptTouchEvent方法

+0

所以它是**不可能**取消整個觸摸事件。我只有機會對自定義視圖進行更改。父視圖取決於使用我的自定義視圖的開發人員。真是恥辱 – appsthatmatter