Multiple Views OnTouch Events - android

We are developing an application where we need to capture MotionEvents from multiple views simultaneously. When we try to do so, Android only dispatches events to the first touched view and, when touching another view simultaneously, it gives us an ACTION_POINTER_DOWN on the first view only, even if the pointer coordinates are outside its bounds and inside the other views'.
Is there any way we can get events dispatched to every touched view (in separate calls to OnTouch)?
We believe intercepting the touch events from a parent view and then manually dispatching them to each view might work, but even if it does, it would not be practical for what we are trying to achieve. Is there an easier way?

At least Android 3.0 adds support for splitting touch events between multiple Views.
Split touch events is enabled by default when AndroidManifest <uses-sdk> defines either android:minSdkVersion="11" or android:targetSdkVersion="11". For explanation See android:splitMotionEvents and android:windowEnableSplitTouch

Touch event is first catched by the view currently in focus. If you touched 1 view and have "consumed" event and are working with it, any other events will be dispatched to this view, no matter where you click.
If you return false from your touchListener (thus saying that event is not consumed) it will be delegated down to other views, but on another separate click new event will be dispatched to other view.
http://www.zdnet.com/blog/burnette/how-to-use-multi-touch-in-android-2-part-3-understanding-touch-events/1775
http://developer.android.com/guide/topics/ui/ui-events.html

While this should work automatically with Android SDK 11+, it wasn't working for my app. I was trying to capture touches in a main view, plus some views that were subviews of the main view; in that case, touches were only captured by the view "level" that received the first touch. I reorganized my view structure so that all the views were siblings and now they are capturing events as expected.

Related

Android touch event dispatching from parent to child

I have a parent Viewgroup with a child View inside of it. I'm am noticing some behavior during touch events that differs from my mental model of how touch events are dispatched.
Viewgroup A takes up the full screen and View B is programmatically added to A (using A.addView(B)) and takes up only part of the screen. For both A and B I am overriding the onTouchEvent(MotionEvent event) method, but nothing else.
If I touch my finger to the screen and lift it within the bounds of View B, I notice that the touch event is directly routed to B's onTouchEvent() method without ever hitting A's onTouchEvent() method. B receives the DOWN, MOVE and UP actions and returns true in for each action.
If I then touch my finger to the screen and lift within the bounds of Viewgroup A but outside of the bounds of View B then I see behavior that violates my mental model of how touch events are dispatched. When I do this View B again receives the DOWN, MOVE and UP actions in its onTouchEvent() even though B was not clicked. I would have expected A to receive the actions. If I then do the exact same motion with my finger (touch and lift inside of A but outside of B) then A's onTouchEvent() receives the DOWN, MOVE and UP actions as expected.
It's almost as if after one of the Views receives a touch event, it is setup by the system to receive the next touch regardless of the location of the touch.
Is there something basic I'm not understanding here?
Also, one of my resources mentions that I need to manually manage hit testing within my Viewgroup / Views to determine if when a viewgroup receives a hit if it should dispatch the hit down to a child or not. That makes sense to me, however, it seems like the system already does that for me in the first case I mention as the hit is automatically dispatched to the child. Which is the right way to do it, let the system handle it or handle the hit logic myself?
I used these resources to get me this far but they don't seem to explicitly cover this:
http://codetheory.in/understanding-android-input-touch-events/
https://guides.codepath.com/android/Gestures-and-Touch-Events

Android : How to detect onTouch event on a view, with touch down outside the view?

In an Android fragment, I have a GridLayout with views in it.
I would like to be able to detect when a user swipes over a view, even if he initially touches down outside of it. I've included a diagram to illustrate what I mean here :
Is there a simple way to do this (without using coordinates to see if the touch point is in the view)?
Thanks!
You solve this using View.OnTouchListeners.
Set OnTouchListeners both root view as well as your view.
Check for Events and actions ACTION_DOWN(touch event start),ACTION_UP(touch event over)
Set flags as per need for inside touch or outside touch.
basically you need capture events as well as points too.

Android ChatHead is limiting the touch to itself only, BUT I need support second finger simultaenously second touching the area outside the chathead,

http://www.piwai.info/chatheads-basics/
By following this good guide, I can make a chathead and also detect the touch event.
However, if I touch the chathead with first finger, and try to touch other area (outside) the chathead with second finger, the second touch is not possible.
(The area outside can be the home screen, or another app, activity)
Similarly, IF I first touch the outside, and try to use second finger to touch the chathead, it is not possible.
I tried the similar interaction with facebook messenger chathead and it is the same.
My question is: is it possible to support the second touch?
maybe using dispatch touch event? but afaik dispatch is only for activity.
the chathead uses service and window.
Any help would be deeply appreciated!
Yes its possible using the following workaround.
Have a transparent layout surrounding your chathead.
This
transparent layout will intercept the touch and you can do the necessary
handling.
You can then pass this touch event up the hierarchy/other apps by returning false from OnTouchEvent().
To let the other apps handle touch event,the transparent view can only be activated when the user is already touching your chathead.This way you cna make sure that the user is planning to do some gesture with your chathead.
This isn't possible using layouts manually added to the WindowManager as a system overlay when the underlying view is from a completely different hierarchy.
Once you start a touch event on the first view, all subsequent touch events will be sent to the same view heirarchy until ALL MotionEvents are finished (I.e ACTION_UP or ACTION_CANCEL has occurred).
Basically, once you are interacting with one view heirarchy, any outside touches are interpreted as touches outside the current heirarchy, and ignore any underlying view heirarchies which may or may not occupy the same screen position.

How are Android touch events delivered?

I'm not asking how to handle touch events, but what is going on behind the scenes? If there are several nested widgets, what order do they see the events in? Does the developer have any control over it? Ideally I would like a document on the subject.
Let's take a look at a visual example.
When a touch event occurs, first everyone is notified of the event, starting at the Activity and going all the way to the view on top. Then everyone is given a chance to handle the event, starting with the view on top (view having highest Z order in the touch region) and going all the way back to the Activity. So the Activity is the first to hear of it and the last to be given a chance to handle it.
If some ViewGroup wants to handle the touch event right away (and not give anyone else down the line a chance at it) then it can just return true in its onInterceptTouchEvent(). An Activity doesn't have onInterceptTouchEvent() but you can override dispatchTouchEvent() to do the same thing.
If a View (or a ViewGroup) has an OnTouchListener, then the touch event is handled by OnTouchListener.onTouch(). Otherwise it is handled by onTouchEvent(). If onTouchEvent() returns true for any touch event, then the handling stops there. No one else down the line gets a chance at it.
More detailed explanation
The above diagram makes things a little more simple than they actually are. For example, between the Activity and ViewGroup A (the root layout) there is also the Window and the DecorView. I left them out above because we generally don't have to interact with them. However, I will include them below. The description below follows a touch event through the source code. You can click a link to see the actual source code.
(Update: the source code has been updated so the line numbers are off now, but clicking the links will still get you to the right file. Just do a search for the method name.)
The Activity's dispatchTouchEvent() is notified of a touch event. The touch event is passed in as a MotionEvent, which contains the x,y coordinates, time, type of event, and other information.
The touch event is sent to the Window's superDispatchTouchEvent(). Window is an abstract class. The actual implementation is PhoneWindow.
The next in line to get the notification is DecorView's superDispatchTouchEvent(). DecorView is what handles the status bar, navigation bar, content area, etc. It is actually just a FrameLayout subclass, which is itself a subclass of ViewGroup.
The next one to get the notification (correct me if I'm wrong) is the content view of your activity. That is what you set as the root layout of your activity in xml when you create the layout in the Android Studio's Layout Editor. So whether you choose a RelativeLayout, a LinearLayout, or a ConstraintLayout, they are all subclasses of ViewGroup. And ViewGroup gets notified of the touch event in dispatchTouchEvent(). This is the ViewGroup A in my diagrams above.
The ViewGroup will notify any children it has of the touch event, including any ViewGroup children. This is ViewGroup B in my diagrams above.
Anywhere along the way, a ViewGroup can short-circuit the notification process by returning true for onInterceptTouchEvent().
Assuming no ViewGroup cut the notifications short, the natural end of the line for the notifications is when the View's dispatchTouchEvent() get's called.
Now it is time, to start handling the events. If there is an OnTouchListener, then it gets the first chance at handling the touch event with onTouch(). Otherwise, the View's onTouchEvent() gets to handle it.
Now all the ViewGroups recursively up the line get a chance to handle the touch event in the same way that View did. Although, I didn't indicate this in the diagram above, a ViewGroup is a View subclass, so everything I described about OnTouchListener.onTouch() and onTouchEvent() also applies to ViewGroups.
Finally, if no one else wanted it, the Activity also gets the last chance to handle the event with onTouchEvent().
FAQ
When would I ever need to override dispatchTouchEvent()?
Override it in the Activity if you want to catch a touch event before any of the views get a chance at it. For a ViewGroup (including the root view), then just override onInterceptTouchEvent() and onTouchEvent().
When would I ever need to override onInterceptTouchEvent()?
If you just want to spy of the touch notifications that are coming in, you can do it here and return false.
However, the main purpose of overriding this method is to let the ViewGroup handle a certain type of touch event while letting the child handle another type. For example, a ScrollView does this to handle scrolling while letting its child handle something like a Button click. Conversely, if the child view doesn't want to let its parent steal its touch event, it can call requestDisallowTouchIntercept().
What are the touch event types?
The main ones are
ACTION_DOWN - This is the start of a touch event. You should always return true for the ACTION_DOWN event in onTouchEvent if you want to handle a touch event. Otherwise, you won't get any more events delivered to you.
ACTION_MOVE - This event is continuously fired as you move your finger across the screen.
ACTION_UP - This is the last event of a touch event.
A runner up is ACTION_CANCEL. This gets called if a ViewGroup up the tree decides to intercept the touch event.
You can view the other kinds of MotionEvents here. Since Android is multi-touch, events are also fired when other fingers ("pointers") touch the screen.
Further study
Android onTouchEvent Part 1, Part 2, and Part 3 (YouTube video - good summary of some of the links below)
Mastering the Android Touch System (thorough video by Google developer)
Android UI Internal : Pipeline of View's Touch Event Handling
Managing Touch Events in a ViewGroup (Android docs)
Input Events (Android docs)
Gestures and Touch Events
From Activity viewpoint:
Touch events are delivered first to Activity.dispatchTouchEvent. It's where you may catch them first.
Here they get dispatched to Window, where they traverse View hierarchy, in such order that Widgets that are drawn last (on top of other widgets) have chance to process touch in View.onTouchEvent first. If some View returns true in onTouchEvent, then traversal stops and other Views don't receive touch event.
Finally, if no View consumes touch, it's delivered to Activity.onTouchEvent.
That's all your control. And it's logical that what you see drawn on top of something else, has chance to process touch event before something drawn below it.
Android Touch event
I have prepared a high level diagram that should illustrate a simple flow.
dispatchTouchEvent() - Activity, ViewGroup, View
onInterceptTouchEvent() - ViewGroup
onTouch() - ViewGroup, View. Using setOnTouchListener()
onTouchEvent() - Activity, ViewGroup, View
[iOS onTouch]
following Suragch's answer,
pseudocode:
public boolean dispatchTouchEvent(MotionEvent ev) {
boolean consume = false;
if (onInterceptTouchEvent(ev) {
consume = onTouchEvent(ev);
} else {
consume = child.dispatchTouchEvent(ev);
}
return consume;
}
ref:Android开发艺术探索

Session management on UI in android

In my app, I have to handle the client side session management regarding UI that means when the user never touches on the screen for a while(eg: 1min), he will be prompted to login screen(in my case). for this I planned like this: added ontouch listener to the root view of every layout and placed my session logic. But the problem is, every layout contains lot of textviews,listviews,buttons,etc. I dont want to add ontouch listener to all these views, for this I added to only root view of my layout. when i touch on textviews, the root view's ontouch listener is getting that touch event and working fine. but when i touch on the remaining views, the root view's ontouch listener is not getting that touch event. Can someone tell me whether my approach is correct or not? If it is correct then how can I do for the remaining views?
thanks,venu
I don't know, if you're doing it right, but to get notification for all touch events in a parent view you can override the onInterceptTouchEvent(MotionEvent ev) method of the ViewGroup class.
For details check:
http://developer.android.com/reference/android/view/ViewGroup.html#onInterceptTouchEvent(android.view.MotionEvent)
Regards:
Ripityom

Categories

Resources