Status Update
Comments
jg...@google.com <jg...@google.com> #2
st...@gmail.com <st...@gmail.com> #3
What is 'partially' exactly?
How do I see it?
Thanks!
jg...@google.com <jg...@google.com> #4
- Nesting scroll views with a scroll direction perpendicular to the ViewPager2's orientation inside ViewPager2 works
- Nesting scroll views with a scroll direction parallel to the ViewPager2's orientation inside ViewPager2 does not work
ag...@gmail.com <ag...@gmail.com> #5
Set a setNestedScrollingEnabled to the RecyclerView into the ViewPager2 (across reflection) resolves the problem
jg...@google.com <jg...@google.com>
je...@google.com <je...@google.com> #6
je...@google.com <je...@google.com> #7
Verified that it works correctly on a:
- Nexus 4 emulator with API 17
- Nexus 5X emulator with API 28
- Pixel 2 device with API 29
To reproduce:
- Check out the Android Jetpack source (at commit 256899f482ff85cddfb050f37550be7b5ec927ef) (see steps in [1])
- Apply the patch (`git apply 0001-Add-sample-where-a-horizontal-ViewPager2-is-nested-i.patch`)
- Build and install viewpager2's integration-tests app: `./gradlew viewpager2:integration-tests:testapp:installDebug`
- Run it
Closing the issue for now, but please reopen if you have a minimal reproduction app
[1]
jg...@google.com <jg...@google.com> #8
mu...@gmail.com <mu...@gmail.com> #9
I searched a lot for workarounds on Google and SO, but it seems no one really found a suitable solution yet.
The thing is that all other VerticalViewPagers out there have other problems that are similarly annoying. Most of them revolve around touch issues like "What's a click and what's a swipe" or "what's the fling threshold to snap to the next page".
So if someone knows of a working sample of VerticalViewPagers, I'd be happy to see a link :)
jg...@google.com <jg...@google.com> #10
One possible option for VerticalViewPager with nested scrolling in the same scroll direction is using a RecyclerView implementing NestedScrollingParent + PagerSnapHelper to get snapping.
You'd then have to implement the NestedScrollingParent/Child contract between parent/child RecyclerView instances.
mu...@gmail.com <mu...@gmail.com> #11
I will try implementing what you suggested and in case it is readable code, I will post it here as sample project or somthing similar.
jg...@google.com <jg...@google.com> #12
mu...@gmail.com <mu...@gmail.com> #13
But I figured out another way to achieve the same goal. I attached a demo project with the following setup:
1. A parent RecyclerView with PagerSnapHelper:
This vertical RecyclerView has two items/pages.
The upper page is just a simple TextView.
The lower page contains the nested RecyclerView.
Both pages are Fragments if that is of any concern.
2. A PagingLinearLayoutManager:
This is a LinearLayoutManager with the capability to enable/disable scrolling.
It is used for the parent RecyclerView.
3. A OnItemTouchListener:
This is also used for the parent RecyclerView.
It saved the initial y coordinate on ACTION_DOWN
During ACTION_MOVE event, it decides whether the PagingLinearLayoutManager is allowed to scroll (i.e. to switch between pages)
The important parts of the code are commented. Hope this helps someone else!
sq...@gmail.com <sq...@gmail.com> #14
jg...@google.com <jg...@google.com> #16
We're working on a solution that'll work reliably with ViewPager2 1.0. See
Not finalized at the time of writing, but might help some people already. Notice NestedScrollableHostLayout wrapping [1] a scrollable child.
[1]
ap...@google.com <ap...@google.com> #17
Branch: androidx-master-dev
commit 1316417bc87741ea9f739a453eb9e792512c9651
Author: Jelle Fresen <jellefresen@google.com>
Date: Tue Oct 01 16:58:57 2019
Add sample of nested scrolling in ViewPager2
This shows how nested scrolling can be achieved in ViewPager2. Every
child on a page that scrolls in the same direction as ViewPager2 must be
wrapped by a NestedScrollableHostLayout, and one must make sure that the
scrollable child implements canScrollHorizontally/Vertically in order
for this to work.
Bug: 123006042
Test: Manual, run ViewPager2 demo app
Change-Id: I79e24b7668cf7ff5c05c350ea3ab9134dd74b3a4
M viewpager2/integration-tests/testapp/src/main/AndroidManifest.xml
M viewpager2/integration-tests/testapp/src/main/java/androidx/viewpager2/integration/testapp/BrowseActivity.kt
A viewpager2/integration-tests/testapp/src/main/java/androidx/viewpager2/integration/testapp/NestedScrollableHost.kt
A viewpager2/integration-tests/testapp/src/main/java/androidx/viewpager2/integration/testapp/ParallelNestedScrollingActivity.kt
A viewpager2/integration-tests/testapp/src/main/res/layout/item_nested_recyclerviews.xml
M viewpager2/integration-tests/testapp/src/main/res/values/colors.xml
M viewpager2/integration-tests/testapp/src/main/res/values/strings.xml
jg...@google.com <jg...@google.com> #18
Notice how the scrollable children of ViewPager2 are wrapped in NestedScrollableHost -- optional for orthogonal elements, required for same-orientation elements.
fi...@gmail.com <fi...@gmail.com> #19
jg...@google.com <jg...@google.com> #20
We're keeping this bug open to track effort in the area.
sk...@gmail.com <sk...@gmail.com> #21
It worked just as I would expect it to work. A fling inside of the host flinged the recyclerview to the end. A second fling in the same direction changed the page in the viewpager. I had to lift my finger during a scroll all the way to the end of the recyclerview to get the viewpager to page. Great results.
js...@gmail.com <js...@gmail.com> #22
jg...@google.com <jg...@google.com> #23
To clarify, does the scrolling functionality work correctly with NestedScrollableHost (both: with 'pre-tap' and directly)?
js...@gmail.com <js...@gmail.com> #24
js...@gmail.com <js...@gmail.com> #25
To elaborate a little more, I initially added NestedScrollableHost after #21 reported that it solved the same problem we have. However, through elimination I discovered that the inclusion of NestedScrollableHost didn't seem to make a difference but the 'pre-tap' before attempting to scroll did.
js...@gmail.com <js...@gmail.com> #26
ViewPager2 (horizontal) > NestedScrollView (vertical) > ConstraintLayout > RecyclerView (horizontal)
ViewPager2 1.0.0
Core (NestedScrollView) 1.1.0
RecyclerView 1.1.0
jg...@google.com <jg...@google.com> #27
In the meantime, we still recommend NestedScrollableHost as per
Mu...@postuf.com <Mu...@postuf.com> #28
jg...@google.com <jg...@google.com> #29
Possibly you could override SeekBar's onInterceptTouchEvent similar to
vn...@gmail.com <vn...@gmail.com> #30
The good thing is it also allows my nested RV to recycle properly, compared to wrapping it in a NestedScrollView. Saved my day, thanks.
sa...@gmail.com <sa...@gmail.com> #31
jg...@google.com <jg...@google.com> #32
pa...@gmail.com <pa...@gmail.com> #33
Only the vertical scrolling of the map works, the horizontal scroll doesn't.
Any lead would be appreciated. Thanks.
jg...@google.com <jg...@google.com> #34
Re NestedScrollableHost
directly.
If that doesn't work, check out what it does and follow a similar pattern, the key is wrapping an element in a ViewGroup (e.g. FrameLayout
) and calling requestDisallowInterceptTouchEvent
when appropriate. In case it requires a custom solution like this, share it here so others can benefit.
al...@tutanota.com <al...@tutanota.com> #35
cp...@gmail.com <cp...@gmail.com> #36
Before recommending migration from ViewPager documentation must declare features removed - like nested scroll, scroll interpolators, ability to use Adapter not bound to RecyclerView. It's total pita to restore current functionality provided by ViewPager by default :(
jo...@live.com <jo...@live.com> #37
I tried the NestedScrollableHost solution but the problem is, if I use a wrapper on this ConstraintLayout, the code will break since the View that is defined as a BottomSheetBehaviour has to be a direct child of CoordinatorLayout (Which is the root view for the ViewPager).
we...@gmail.com <we...@gmail.com> #38
I was unable to get horizontal scrolls with NestedScrollableHost
to work when I had ViewPager2 (H) > ViewPager2 (V) > ViewPager2 (H)
because it would only check to see if the immediate ancestor ViewPager2
would scroll in the same direction. I made a modification to solve this issue, which checks all ancestor ViewPager2
s.
package live.spin.ui.main
/*
* Copyright 2019 The Android Open Source Project
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
import android.content.Context
import android.util.AttributeSet
import android.view.MotionEvent
import android.view.View
import android.view.ViewConfiguration
import android.widget.FrameLayout
import androidx.viewpager2.widget.ViewPager2
import androidx.viewpager2.widget.ViewPager2.ORIENTATION_HORIZONTAL
import kotlin.math.absoluteValue
import kotlin.math.sign
/**
* Based on https://github.com/android/views-widgets-samples/blob/master/ViewPager2/app/src/main/java/androidx/viewpager2/integration/testapp/NestedScrollableHost.kt
* but modified by Seth Westphal (westy92@gmail.com) to support nested ViewPager2.
*/
/**
* Layout to wrap a scrollable component inside a ViewPager2. Provided as a solution to the problem
* where pages of ViewPager2 have nested scrollable elements that scroll in the same direction as
* ViewPager2. The scrollable element needs to be the immediate and only child of this host layout.
*
* This solution has limitations when using multiple levels of nested scrollable elements
* (e.g. a horizontal RecyclerView in a vertical RecyclerView in a horizontal ViewPager2).
*/
class NestedScrollableHost : FrameLayout {
constructor(context: Context) : super(context)
constructor(context: Context, attrs: AttributeSet?) : super(context, attrs)
private var touchSlop = 0
private var initialX = 0f
private var initialY = 0f
private val ancestorViewPagers: List<ViewPager2>
get() {
val ancestors = mutableListOf<ViewPager2>()
var v: View? = parent as? View
while (v != null) {
if (v is ViewPager2) ancestors.add(v)
v = v.parent as? View
}
return ancestors
}
private val child: View? get() = if (childCount > 0) getChildAt(0) else null
init {
touchSlop = ViewConfiguration.get(context).scaledTouchSlop
}
private fun canChildScroll(orientation: Int, delta: Float): Boolean {
val direction = -delta.sign.toInt()
return when (orientation) {
0 -> child?.canScrollHorizontally(direction) ?: false
1 -> child?.canScrollVertically(direction) ?: false
else -> throw IllegalArgumentException()
}
}
override fun onInterceptTouchEvent(e: MotionEvent): Boolean {
handleInterceptTouchEvent(e)
return super.onInterceptTouchEvent(e)
}
private fun handleInterceptTouchEvent(e: MotionEvent) {
for (ancestor in ancestorViewPagers) {
val orientation = ancestor.orientation
// Early return if child can't scroll in same direction as parent
if (!canChildScroll(orientation, -1f) && !canChildScroll(orientation, 1f)) {
continue
}
if (e.action == MotionEvent.ACTION_DOWN) {
initialX = e.x
initialY = e.y
parent.requestDisallowInterceptTouchEvent(true)
} else if (e.action == MotionEvent.ACTION_MOVE) {
val dx = e.x - initialX
val dy = e.y - initialY
val isVpHorizontal = orientation == ORIENTATION_HORIZONTAL
// assuming ViewPager2 touch-slop is 2x touch-slop of child
val scaledDx = dx.absoluteValue * if (isVpHorizontal) .5f else 1f
val scaledDy = dy.absoluteValue * if (isVpHorizontal) 1f else .5f
if (scaledDx > touchSlop || scaledDy > touchSlop) {
if (isVpHorizontal == (scaledDy > scaledDx)) {
// Gesture is perpendicular, allow all parents to intercept
parent.requestDisallowInterceptTouchEvent(false)
} else {
// Gesture is parallel, query child if movement in that direction is possible
if (canChildScroll(orientation, if (isVpHorizontal) dx else dy)) {
// Child can scroll, disallow all parents to intercept
parent.requestDisallowInterceptTouchEvent(true)
} else {
// Child cannot scroll, allow all parents to intercept
parent.requestDisallowInterceptTouchEvent(false)
}
}
}
}
}
}
}
je...@google.com <je...@google.com> #39
we...@gmail.com <we...@gmail.com> #40
Yes, I'd be happy to! Feel free to email me information on where to start that process.
jq...@gmail.com <jq...@gmail.com> #41
sa...@gmail.com <sa...@gmail.com> #42
li...@gmail.com <li...@gmail.com> #43
Unfortunately, the workaround #18 dose not suitable for the case that a ScrollerView or NestedScrollView that has the same orientation in a ViewPager2.
Any other solution will be appreciated.
li...@gmail.com <li...@gmail.com> #44
Re #43. Finally I found a workaround after some modifications.
It also suitable for the following case: ViewPager2(V) -> NestedScrollView(V) -> RecyclerView(H) (See
Just simply intercept events if child view do not need to scroll.
Also, don't change the touch slop since it seems that the ScrollView will consume the event which result in we can't receive enough move distance.
li...@gmail.com <li...@gmail.com> #45
[Deleted User] <[Deleted User]> #46
ye...@gmail.com <ye...@gmail.com> #47
ly...@gmail.com <ly...@gmail.com> #48
When sliding quickly, NestedScrollableHost.onInterceptTouchEvent will not be called, nested scrollable view cannot scroll to the edge.
If ViewPager2 is nested with an imageview that supports zooming/panning, and then quickly swipe after zooming in, the displayed image is only the width of the screen.
ga...@gmail.com <ga...@gmail.com> #49
Hi
su...@gmail.com <su...@gmail.com> #50
aa...@gmail.com <aa...@gmail.com> #51
aa...@gmail.com <aa...@gmail.com> #52
ra...@oliveboard.in <ra...@oliveboard.in> #53
ra...@gmail.com <ra...@gmail.com> #54
fa...@gmail.com <fa...@gmail.com> #55
pr...@gmail.com <pr...@gmail.com> #56
I have observed sometime the page is changed randomly also, while we are in scrolling recyclerview and suddenly the page is changed.
Any update on this issue? It's leading to some major app functioning issues
ms...@vivino.com <ms...@vivino.com> #57
The attached example works like a charm - and will have to do until Google solves this at a lower level...
pa...@gmail.com <pa...@gmail.com> #58
any update on this?
ry...@gmail.com <ry...@gmail.com> #59
As many have written here, NestedScrollableHost workaround doesn't work for fast scrolling (even on the newest version 1.1.0). This would annoy users, so I went back to ViewPager.
Why fix obvious flaws when you can work on AI, AI, AI...
Description
No description yet.