[TIMOB-26581] Android: Child view with "touchEnabled" false wrongly receives touch events if parent is touch enabled
GitHub Issue | n/a |
---|---|
Type | Bug |
Priority | Medium |
Status | Open |
Resolution | Unresolved |
Affected Version/s | n/a |
Fix Version/s | n/a |
Components | Android |
Labels | android, click, engSchedule, parity, touch, touchEnabled |
Reporter | Joshua Quick |
Assignee | Joshua Quick |
Created | 2018-11-17T02:05:45.000+0000 |
Updated | 2021-02-22T18:39:07.000+0000 |
Description
*Summary:*
If a parent view's "touchEnabled" property is set Touch and drag the blue square. (The square's "touchEnabled" property is set
true
, its child view "touchEnabled" is set false
, and then you tap on the child view... that child view will wrongly fire a touch/click event. The problem with this is that event will "bubble" to the parent view and its coordinates will be relative to the child, not the parent.
*Steps to reproduce:*
Build and run the below code on Android.
Touch and drag the blue square. (The square's "touchEnabled" property is set false
.)
If the square turns red, then it has wrongly fired a touch event.
function onTouch(event) {
if (event.source != parentView) {
Ti.API.error("@@@ Oh-no! Child view wrongly fired a touch event.");
childView.backgroundColor = "red";
}
if ((event.type === "pinch") || (event.type === "swipe")) {
return;
}
childView.center = { x: event.x, y: event.y };
}
var window = Ti.UI.createWindow();
var parentView = Ti.UI.createView({
touchEnabled: true,
backgroundColor: "gray",
width: Ti.UI.FILL,
height: Ti.UI.FILL,
});
var childView = Ti.UI.createView({
touchEnabled: false,
backgroundColor: "blue",
width: "100dp",
height: "100dp",
});
parentView.add(childView);
parentView.addEventListener("touchstart", onTouch);
parentView.addEventListener("touchmove", onTouch);
parentView.addEventListener("touchend", onTouch);
parentView.addEventListener("touchcancel", onTouch);
parentView.addEventListener("click", onTouch);
parentView.addEventListener("dblclick", onTouch);
parentView.addEventListener("doubletap", onTouch);
parentView.addEventListener("longpress", onTouch);
parentView.addEventListener("pinch", onTouch);
parentView.addEventListener("swipe", onTouch);
window.add(parentView);
window.open();
*Cause:*
The Java touch/gesture event listeners in our TiUIView.java
class are always listening. They fire an event if a JavaScript listener is set up on the view or any of its parent views by calling KrollProxy.hierarchyHasListener()
. This is partly the issue.
https://github.com/appcelerator/titanium_mobile/blob/master/android/titanium/src/java/org/appcelerator/titanium/view/TiUIView.java
*Recommended Solution:*
Replace the KrollProxy.hierarchyHasListener()
calls with KrollProxy.hasListener()
. Also check if touch events are enabled by calling [View.isClickable()](https://developer.android.com/reference/android/view/View.html#isClickable()) which I +think+ will return false
(we need to double check) when we set the "touchEnabled" property to false because changing that property ends up calling TiUIView.doSetClickable()
.
*Work-around:*
Check the event's "source" property to see which view has fired this event. The above test code does this and you can simply return out of the listener if it doesn't reference the expected view.
Thanks [~michael]. That PR might have introduced the issue, but the "cause" I've written up above is still true. I think the real issue here is that the code is walking up the parent hierarchy and is regarding the child as touch enabled if at least 1 of its parents is touch enabled. iOS does not work this way. This is the root cause.
The same scenario will trigger a longpress on the parent, even for a short click/tap. The above work around can be used to determine a true long press.
Same issue here with latest SDK available (8.2.0.GA). touchEnabled: false seems to be ignored.