From aae521f57e56c0790f957a2f254327057b8ee72e Mon Sep 17 00:00:00 2001 From: Denis Koroskin Date: Mon, 7 Mar 2016 20:06:23 -0800 Subject: [PATCH] Add ReactCompoundViewGroup interface that allows having both virtual and non-virtual (View) children Summary: In React, ReactCompoundView is supposed to be implemented by a View, but there is no ViewGroup counterpart that allows mixing virual nodes and non-virtual ones (Views) in the same parent. This is needed because TouchTargetHelper always considers child Views when looking for touch target before falling back to View/ReactCompoundView. This works incorrectly for e.g. layout-only / flattened nodes. ReactCompoundViewGroup allow intercepting touch event before it is dispatched to its children. In that sense, ReactCompoundView.reactTagForTouch() is like View.onTouchEvent() whereas ReactCompoundViewGroup.interceptsTouchEvent() is like ViewGroup.onInterceptTouchEvent(). Differential Revision: D3018028 fb-gh-sync-id: d2c70a55afb9ce9823275e7483d72e0ebedf52e4 shipit-source-id: d2c70a55afb9ce9823275e7483d72e0ebedf52e4 --- .../uimanager/ReactCompoundViewGroup.java | 24 +++++++++++++++++++ .../react/uimanager/TouchTargetHelper.java | 5 ++++ 2 files changed, 29 insertions(+) create mode 100644 ReactAndroid/src/main/java/com/facebook/react/uimanager/ReactCompoundViewGroup.java diff --git a/ReactAndroid/src/main/java/com/facebook/react/uimanager/ReactCompoundViewGroup.java b/ReactAndroid/src/main/java/com/facebook/react/uimanager/ReactCompoundViewGroup.java new file mode 100644 index 000000000..345dea9f9 --- /dev/null +++ b/ReactAndroid/src/main/java/com/facebook/react/uimanager/ReactCompoundViewGroup.java @@ -0,0 +1,24 @@ +/** + * Copyright (c) 2015-present, Facebook, Inc. + * All rights reserved. + * + * This source code is licensed under the BSD-style license found in the + * LICENSE file in the root directory of this source tree. An additional grant + * of patent rights can be found in the PATENTS file in the same directory. + */ + +package com.facebook.react.uimanager; + +/** + * This interface should be implemented be native ViewGroup subclasses that can represent more + * than a single react node. In that case, virtual and non-virtual (mapping to a View) elements + * can overlap, and TouchTargetHelper may incorrectly dispatch touch event to a wrong element + * because it priorities children over parents. + */ +public interface ReactCompoundViewGroup extends ReactCompoundView { + /** + * Returns true if react node responsible for the touch even is flattened into this ViewGroup. + * Use reactTagForTouch() to get its tag. + */ + boolean interceptsTouchEvent(float touchX, float touchY); +} diff --git a/ReactAndroid/src/main/java/com/facebook/react/uimanager/TouchTargetHelper.java b/ReactAndroid/src/main/java/com/facebook/react/uimanager/TouchTargetHelper.java index e998110b7..8c74639d5 100644 --- a/ReactAndroid/src/main/java/com/facebook/react/uimanager/TouchTargetHelper.java +++ b/ReactAndroid/src/main/java/com/facebook/react/uimanager/TouchTargetHelper.java @@ -209,6 +209,11 @@ public class TouchTargetHelper { } else if (pointerEvents == PointerEvents.AUTO) { // Either this view or one of its children is the target + if (view instanceof ReactCompoundViewGroup) { + if (((ReactCompoundViewGroup) view).interceptsTouchEvent(eventCoords[0], eventCoords[1])) { + return view; + } + } if (view instanceof ViewGroup) { return findTouchTargetView(eventCoords, (ViewGroup) view); }