Dispatch TouchEvent to Listview after it's intercepted by parent - android

I have a layout which looks something like this:
Inside the ViewPager are a number of Fragments, of which each holds a ListView.
Now I want to do this:
By scrolling down, the ListView mustn't scroll. Instead the header has to be transformed so it looks like a native ActionBar.
I'm doing this by intercepting the TouchEvent in the root layout of my activity.
I have overwritten the onInterceptTouchEvent() method of my parent layout and check if a TouchEvent should be intercepted or not, by retrieving the current visible ListView and check if the firstVisibleItem is the first and it's completely visible.
If the TouchEvent should be intercepted, my custom OnGesutreListener's onScroll() method is called. So the "animation" progress depends on the scroll offset.
All that works really fine. But now here's my problem:
The transformation is ready and the user still scrolls down. As a user i would expect the ListView to scroll down. But the TouchEvent isn't dispatched to the ListView because it's still intercepted by the parent layout. Only after moving the finger up and down again onInterceptTouchEvent is called again (now the parent layout hasn't to intercept) and the list is scrollable.
I tried a few things but they didn't work out for me:
I tried to call my listView's onTouchEvent manually in the
onTouchEvent() method of my parent's layout - noting happens
I tried to call my listView's onTouchEvent manually in my onScroll method,
also this didn't worked out.
Maybe some of you have a clue how to work this out.
Thanks a lot and greetz!

Could you try getting the x position moved by the finger in the parent onTouchEvent and then using
listView.smoothScrollByOffset(int offset);
To scroll the list view?
Edit: Based on feedback it should be:
smoothScrollBy((int) distanceY, 1);

Thanks to MungoRae's answer I could figure it out (please give him an upvote!)
listView.smoothScrollBy(int offset, int durationInMillis)
this works fine for me. The offset is the scrolled distanceY and the duration is set to 1 msec

You can use View.requestDisallowInterceptTouchEvent(). It will enable and disable the ability of a view to intercept touch event.
set requestDisallowInterceptTouchEvent(true) on the parent layout while touching , it will enable the listview to handle the touch event.

Related

Nested RecyclerView and ViewPager content wrapping and scrolling don't play nice together

I have nested ViewPagers and RecyclerViews as depicted in the image:
Requirements:
The first level ViewPager1 swipes from left to right. It uses FragmentStatePagerAdapter.
The first level RecyclerView1 scrolls vertically.
The second level ViewPager2 does not swipe - the swipe motion is controlled by a TabLayout. It uses a custom PagerAdapter that returns a View as a page.
The second level RecyclerView does not scroll - it simply wraps a list of dynamic items
What I have working so far:
The first level ViewPager1 and RecyclerView1 works as intended.
The ViewPager2 does not show because its height is defined as "wrap_content"
The ViewPager2/RecyclerView2 prevents RecyclerView1 to scroll up/down.
What I have tried:
Setting RecyclerView1.setNestedScrollingEnabled(false) stops it from passing the onTouch event to its children, but because the ViewPager2/RecyclerView2 wraps its content, it does not know what the size it needs to scroll.
Setting the ViewPager2 to a fixed height solves the scrolling problem. But because it is a fixed height, the content of RecyclerView2 is cut off.
Overriding OnMeasure as described here makes ViewPager2's content wrap, perfectly, but the scrolling no longer work again. I assume it is because OnMeasure is called "after" the View has already been attached?
So basically I need help on how to get the content to wrap but in such a way that RecyclerView 1 knows what the height is so that it can scroll.
EDIT
It turns out I was totally off base with point 3. The OnMeasure workaround DOES work as intended and the scrolling problem is NOT caused by recyclerView not knowing the height. It in fact does. The reason why it doesn't scroll is due to multiple nested scrollable view groups. I found this out by putting Log.i on onTouchEvent() and onInterceptTouchEvent() on all the scrollable view groups. Some surface of the views work, but if the surface has another scrollable child, it starts to cause problems.
Setting RecyclerView2.setNestedScrollingEnabled(false) fixed the vertical scrolling. However, now, the ViewPager2's touch behaviour is interfering with ViewPager1's
On closer inspection, ViewPager1 intercepts touch event when hitting non-scrollable surface, causing the ViewPager1 to call its onTouchEvent() to scroll left and right. However, if I start the touch event over a the ViewPager2's surface, ViewPager1 never intercept and it never handles the swipe left to right.
Unlike a RecyclerView, there is no simple method to disable nestedScrolling. So I tried disabling ViewPager2, but that didn't work and caused the inside views such as buttons not clickable.
I tried to return false in ViewPager2's OnTouchEvent so that it bubbles up the chain, but still, the ViewPager1's OnTouchEvent is never fired.
So I'm stuck again, how do I pass the touch event to the parent when the parent did not intercept the event when it should have. Again, I'm assuming, and again I might be off-base, that ViewPager1 might not intercept because ViewPager2 has requested a disallowInterceptTouchEvent() somewhere in its code? I just don't know where or how to begin to fix this problem.

Cannot adjust seekbar value with swipe gesture in RecyclerView

I created a RecyclerView with "swap item positions" and "remove item with swipe gesture" as explained here. Everything works fine except for a seek bar which is contained in the items. I can only adjust the seekbar value by tapping on some spot but not changing the value by tapping on the draggable thumb and then moving it left or right. What happens in those cases is that the swipe gesture removes the item from the parent RecyclerView.
How can I achieve to change the value of the seekbar when a swipe gesture occurs on it and only remove it when a swipe gesture is performed outside the seekbar?
#Ben P.: I created my own class that extends from LinearLayout, implemented the onInterceptTouchEvent but I was not able to interrupt the swipe gesture. I guess because I would have needed to intercept the RecyclerView method, however, this class does not provide an onInterceptTouchEvent method which could be overridden.
Instead, I solved my problem as follows: I have two types of views I display in the RecyclerView (default and edit) and only the edit type contains a SeekBar. As it is absolutely okay to not allow to remove the currently-edited item, I distuingished the type of the ViewHolder in the getMovementFlags method which I override from ItemTouchHelper.Callback().
(Sorry that I didn't mention the piece of information with the two view types.)

Scrollview jumps/repositions when trying to scroll on a clickable child of it

I am using two (embedded) ScrollViews like suggested here, in order to create a '2D Scrollview'.
I add multiple childs to this view, and to some of them I set OnClickListener (I also tried with OnLongClickListener as well).
Functionally the result is what I have expected, although if I try to scroll (starting from a child, that has either of the listeners), the scrollview jumps/repositions to the ~opposite direction, I started the scroll to. So if I scroll like this e.g. upwards, the view jumps a big downwards so that I can scroll up at most to the original position.
I have been trying to play around with calling setFocusable(false) on the childs and setFocusable(true) on the ScrollView(s) (but also tried different permutations of it, as I am not sure about setFocusable()), but couldn't really get on top of it.. Any suggestions?
Just encase you are still wondering this is happening because when you start scrolling the other scroll view, ACTION_CANCEL is posted to the on touch method of the original ScrollView which causes a default scroll view to jump back to its original position.

How to prevent onTouchEvent() method to be called for more than one views at once?

I'm using a ViewPager and in one of my pages I have a View with overriden onTouchEvent() method.
Now when user moves his finger on a screen, onTouchEvent() of my View is being called, but also ViewPager is moving a page to left or right. I'd like to prevent somehow ViewPager from calling it's onTouchEvent() method IF a user is touching my View's area. Is it possible?
ViewPager will by default intercept all touch events. In fact, if you put anything inside anything else that scrolls, the container will intercept the touch event. You can override this behavior in certain cases. There is a similar problem with a solution here that might help you:
ViewPager intercepts all x-axis onTouch events. How to disable?

How to add touch event for a Layout that contains a ScrollView

The problem is the the scrolling will intercept with touch event has set to the parent layout.
Can I keep the onTouch event with the scroll in ScrollView ?
This is a very tricky part. There is an overriden method from Activity which is: public boolean onTouchEvent(MotionEvent event)
This is the general method that interprets all the touch events from the whole screen. And you could say, "ok, I can implement this and I am good to go..". And here comes the difficult part on how android works.
As you know every View has its own onTouchEvent() method that you could implement in order to add some custom implementation. So which method will listen? The ScrollView or the Activity? It appears that these touch events go from the "inside" elements to the "outside" elements. I mean parent-child relations.
Another thing to take into account is that the onTouchEvent method returns a boolean. This boolean parameter determines whether the touch event should go one level up or it is handled by the current View. Meaning that if you have a CustomViewA that implements the onTouchEvent() and CustomViewB implementing its own touch event, and the A is a child in B then the touch event would go through A first and if it is not handled it would go to B.
So basically yes it could be done. It depends on what touch event you wanted to do.
So in our case, the ScrollView returns true when the touch events are a horizontal. The activity's touch event will be handled only if the ScrollView touch event is not handled by itself then you are fine. Otherwise you have to override and implement the on touch event of scroll view and in some cases you have to return false so as for the whole layout to implement it. Good luck with the last part. I started to implement a fling effect but came up with some difficulties so I have implemented a 2 finger move with scroll view in it and it works like a charm.
This is about a week of research and experimenting and it is an overview of what I came up with. if you find anything else please let me know. Hope it helped.

Categories

Resources