I have a ViewPager inside a custom pager. Both swipe in horizontal direction. As I understood the Android touch model, my custom pager should monitor touch events going through onInterceptTouchEvent, checking for a swipe gesture and intercepting when one is detected, but also respect requestDisallowInterceptTouchEvent so that if a child view should consume the same horizontal swipe gesture my custom view is monitoring for it is allowed to.
So far so good, my custom pager handles horizontal swipes well for non-scrolling or vertically scrolling child views and for horizontally scrolling child views, respects the disallow request so the child view can use the horizontal gesture.
However, if the horizontally scrolling child view has reached the end of its content so it cannot scroll any more, I would expect it to rescind its disallow request, so that the parent can intercept and consume the motion, but reading through the source (https://android.googlesource.com/platform/frameworks/support/+/refs/heads/master/v4/java/android/support/v4/view/ViewPager.java, https://android.googlesource.com/platform/frameworks/base/+/refs/heads/master/core/java/android/widget/ScrollView.java), I cannot see it ever calling requestDisallowInterceptTouchEvent with false argument, meaning once it has identified a gesture as a swipe, it will consume it regardless of whether it can actually make use of it or not.
Is my understanding of Android's touch handling system flawed, or are the standard view classes' touch handling too crude in this regard?
This is a question to improve my understanding of Android's touch handling model so I can work better with the system, not a request for workaround suggestions (though if the answer is authoritatively "the system does not allow for this", good workarounds are probably welcome to those who have a similar problem and find this question while searching)
Have you looked into ViewGroup.onNestedFling()?
The pertinent excerpt from its documentation:
If a nested scrolling child view would normally fling but it is at the edge of its own content, it can use this method to delegate the fling to its nested scrolling parent instead.
Related
I'm currently working on a project where I have a custom view (graph). It is located inside a RecyclerView which is inside a NestedScrollView. My GraphView has an onTouch event that allows to see additional info while you're dragging your finger over values. When I was initially trying to implement it I would frequently receive an ACTION_CANCEL which was due to parent view interfering with its scrolling. After that I found a hacky solution which was to disable the parent controls at all with requestDisallowInterceptTouchEvent(true).
But this solution isn't exactly what I'm looking for. Instead I would want my app to have both at the same time. So, basically, custom view would keep showing additional data and have its onTouchEvent() triggered but my RecyclerView or NestedScrollView would still be scrolling if it received a drag by y axis.
Is it possible to implement this kind of a behavior and what would I need to do exactly?
I have a horizontal slider in my app, and I want to make it so that if you have the slider selected, you can do the two-fingered swipe up or down gesture from anywhere on the screen (The scroll up or down gesture) to move the slider left or right. I haven't been able to find anything through google about how to change vertical swipe behavior for Talkback and was wondering if there was in fact a way to change this.
I'd really suggest not doing this, it isn't how Android works so it will confuse your users, and be a big source of bugs as any behavior like this can cause touch errors on completely separate views. I just fixed a problem on something similar in my company's app.
But if you really want to do this- your root ViewGroup needs to intercept touch events if there are two fingers down and it moves far enough to qualify. Read https://developer.android.com/training/gestures/viewgroup#intercept for an explanation of how a parent view group can steal touch events from the child. This is the same way ScrollView works to scroll its contents. In fact, looking up the AOSP implementation of ScrollView would give you good example code.
Such as touch the screen and move or use "input swipe" in adb shell, how android system decide to scroll or not and the distance to scroll?
Simple answer. Gesture detection. Scrolling views like ScrollView, ListView and RecyclerView will register to receive onTouchEvent events from the screen. They will then track those events over time to detect things like swipes, including the direction, velocity and acceleration of the swipe.
From there, they can make a decision on whether to scroll, how far to scroll, and in what direction. Each view will do this differently based on how their content is laid out, whether there is additional content that be scrolled to, etc..
In particular ScrollView uses a VelocityTracker to track the series of touch events, and uses that to calculate what to do. There's other options (like the GestureDetector class) available to do simple gesture detection in custom views.
Do yourself a favor, and look at the source code for ScrollView in your IDE.
In particular the onTouchEvent and onInterceptTouchEvent methods.
I have extended a FrameLayout and overriden the onInterceptTouchEvent() and onTouchEvent(). This custom Framelayout will be the root layout of my ListView rows.
My requirement is that, if I tap on a row in the ListView, the onItemClickListener should be called, if I scroll (up/down) the list scrolls as usual. However if I scroll (left/right) or swipe (left/right), the custom FrameLayout should take some action based upon which row I perform the action on.
My idea was that, I would override onInterceptTouchEvent, monitor the events and if I see that ACTION_MOVE has been called enough to make it a scroll/swipe then I take control and do the task. If it's a tap or some other gesture then let the default flow occur.
The problem I face now is that if my framelayout does not contain any touchable child, then I receive no calls post ACTION_DOWN. And if I receive the ACTION_DOWN in my onTouchEvent() so as to analyze the kind of gesture, then I have no control left to let the default flow occur if I calculate and find that the action was actually just a tap/vertical scroll.
Is there some way in which a child can get hold of the events (ACTION_DOWN and ACTION_MOVE) and if a condition satisfies then perform an action or else let the parent handle these same events?
Is there some other approach that I can take to satisfy this requirement?
Alright, this is giving me headache for over an hour. I've got several ScrollViews in ViewFlipper. I want to implement swipe left/right gesture while letting the scrollview to scroll. The idea is quite simple - intercept the event and viewflipper, parse it and pass it to the children no matter what happened. This way I can easily detect the swipe without making mess with the events.
Theoretically all I have to do is to handle onTouch or something like that and return false, so the event will be dispatched to children. But the Android is so smart he won't send my any other events than ACTION_DOWN if I return false, because it thinks I don't want them.
So how can I capture all the motion events coming to the ViewFlipper and it's children and still dispatch them to the children? I can handle detecting the swipe myself.
Android is very nasty about this kind of things. You have to subclass and implement dispatchTouchEvent the way you need. Or you can grab some android code and modify it. I tried to use onInterceptTouchEvent() but found it unusable.
check out onInterceptTouchEvent() for a view group, if you wish to analyze the touches on view flipper and return false to pass the actions to the child views. look at the following link for more information.
http://developer.android.com/reference/android/view/ViewGroup.html#onInterceptTouchEvent(android.view.MotionEvent)