Status Update
Comments
si...@gmail.com <si...@gmail.com> #2
Branch: androidx-main
commit 392d5beb3bb6f5b82439fe53986c3c818aef5075
Author: Nader Jawad <njawad@google.com>
Date: Thu Apr 11 14:05:41 2024
Updated GraphicsLayer outline APIs to consume floats.
Relnote: "Updated GraphicsLayer outline APIs to consume float
parameters instead of int.
Removed UnsetOffset/UnsetSize IntSize sentinel values in favor
of already exising Unspecified constants on float based Offset
and Size inline classes"
Fixes: 333863462
Test: Re-ran graphicsLayer tests
Change-Id: I2fb03296a009ad4957a59905b97b6f21355cb8ba
M compose/ui/ui-graphics/api/current.txt
M compose/ui/ui-graphics/api/restricted_current.txt
M compose/ui/ui-graphics/samples/src/main/java/androidx/compose/ui/graphics/samples/GraphicsLayerSamples.kt
M compose/ui/ui-graphics/src/androidInstrumentedTest/kotlin/androidx/compose/ui/graphics/layer/AndroidGraphicsLayerTest.kt
M compose/ui/ui-graphics/src/androidMain/kotlin/androidx/compose/ui/graphics/layer/AndroidGraphicsLayer.android.kt
M compose/ui/ui-graphics/src/commonMain/kotlin/androidx/compose/ui/graphics/layer/GraphicsLayer.kt
M compose/ui/ui-graphics/src/desktopMain/kotlin/androidx/compose/ui/graphics/layer/GraphicsLayer.desktop.kt
M compose/ui/ui-graphics/src/desktopTest/kotlin/androidx/compose/ui/graphics/layer/DesktopGraphicsLayerTest.kt
ma...@google.com <ma...@google.com>
vi...@dowjones.com <vi...@dowjones.com> #3
I see that this is a P4 and was behind nested scrolling support in a Compose-only architecture, so it seems likely that I'll need a solution before this is officially supported. Do you have any ideas on a possible custom workaround other than requiring a fixed-position toolbar? Thanks!
ma...@google.com <ma...@google.com> #4
Hello.
Thanks for calling this out, it should be a p2 and I bumped the priority. This doesn't mean that it there's a known ETA or anything like this yet.
To accommodate your usecases, you can insert a top level Modifier.nestedScroll()
and proxy properly callbacks from the NestedScrollConnection
to the CoordinatorLayout or any other wrapper View you are using around your compose hierarchy.
Basically, the hierarchy you are looking for is:
CoordinatorLayout (you control it)
AndroidComposeViews (Internal compose view)
Compose hierarchy (you insert your
Modifier.nestedScroll
there and proxy calls to the CoordinatorLayout)
Something like this should work, but I haven't tried it, just an idea from the top of my head.
Let me know how it went if you try it out :)
vi...@dowjones.com <vi...@dowjones.com> #5
jr...@gmail.com <jr...@gmail.com> #6
[Deleted User] <[Deleted User]> #7
Any ETA on when this is going to be fixed?
ma...@google.com <ma...@google.com> #8
No ETA on this feature, apologies for the inconvenience.
This feature is in our top priority backlog for scrolling. Stay tuned.
vi...@dowjones.com <vi...@dowjones.com> #10
v....@gmail.com <v....@gmail.com> #11
[Deleted User] <[Deleted User]> #12
The proposed workaround does not work when we need to show content below the scrollable view like WebView. Similarly, it doesn't work at all when WebView
or NestedScrollView
is used in HorizontalPager
from the Accompanist library.
ub...@gmail.com <ub...@gmail.com> #13
I guess the workaround addresses only one half of this issue, compose as a child to Views. In my case, the View is the child (MapView) to a scrolling Compose parent (HorizontalPager). Horizontal map scrolling does not work in this scenario, not sure how to make it work. MapView is essentially unusable.
I would not want to disable HorizontalPager scrolling entirely, there is plenty of non-MapView screen space to drag on, should be pretty intuitive for the user. Just have the MapView consume its own scroll events.
ch...@gmail.com <ch...@gmail.com> #14
Yes, the workaround in #9 only handles the (more common) case of Compose nested scrolling events propagating up to views.
As a hint: the opposite case could be handled via a wrapper function around AndroidView()
, which wraps the created view in a view group to observe any view nested scrolling events, and then forward them to a NestedScrollDispatcher
.
ub...@gmail.com <ub...@gmail.com> #15
Thanks requestDisallowInterceptTouchEvent()
, just like in ViewPager2.
es...@gmail.com <es...@gmail.com> #16
di...@zillowgroup.com <di...@zillowgroup.com> #17
ya...@gmail.com <ya...@gmail.com> #18
ya...@gmail.com <ya...@gmail.com> #19
<androidx.core.widget.NestedScrollView
android:id="@+id/scroll_view"
android:layout_width="match_parent"
android:layout_height="match_parent"
android:background="@color/white">
<FrameLayout
android:layout_width="match_parent"
android:layout_height="wrap_content">
<LinearLayout
android:id="@+id/exploreDynamic"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:orientation="vertical"
tools:context="com.goldengekko.o2pm.presentation.landing.LandingFragment">
<com.goldengekko.o2pm.feature.views.TallCampaignView
android:id="@+id/tallCampaignView"
android:layout_width="match_parent"
android:layout_height="wrap_content"
app:layout_constraintLeft_toLeftOf="parent"
app:layout_constraintRight_toRightOf="parent"
app:layout_constraintTop_toTopOf="parent" />
<!-- Just for you module -->
<LinearLayout
android:id="@+id/just_for_you_cell"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:orientation="vertical"
android:visibility="gone">
<com.goldengekko.o2pm.presentation.landing.justforyoumodule.JustForYouModuleView
android:id="@+id/just_for_you_module"
android:layout_width="match_parent"
android:layout_height="244dp" />
</LinearLayout>
<LinearLayout
android:id="@+id/thank_you_cell"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:orientation="vertical">
<com.goldengekko.o2pm.presentation.landing.thankyourewardsmodule.ThankYouModuleView
android:id="@+id/thank_you_module"
android:layout_width="match_parent"
android:layout_height="wrap_content" />
<include layout="@layout/landing_page_list_divider" />
</LinearLayout>
<LinearLayout
android:id="@+id/nearby_vertical_offer_cell"
android:nestedScrollingEnabled = "true"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:orientation="vertical">
<com.goldengekko.o2pm.presentation.landing.composeviews.VerticalComposeOffersModule
android:id="@+id/nearby_vertical_offer_module"
android:layout_width="match_parent"
android:layout_height="wrap_content" />
<include layout="@layout/landing_page_list_divider" />
</LinearLayout>
<LinearLayout
android:id="@+id/prizedraws_horizontal_group"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:orientation="vertical">
<com.goldengekko.o2pm.presentation.util.TextView
android:id="@+id/prizedrawtitle"
style="@style/H3"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:paddingStart="24dp"
android:paddingTop="48dp"
android:paddingEnd="24dp" />
<com.goldengekko.o2pm.composecard.carousal.CarousalCardView
android:id="@+id/prizedraws_horizontal_cell"
android:layout_width="match_parent"
android:layout_height="wrap_content" />
</LinearLayout>
<LinearLayout
android:id="@+id/banner_group"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:orientation="vertical">
<include
android:id="@+id/banner_cell"
layout="@layout/layout_landing_banner_cell" />
</LinearLayout>
<LinearLayout
android:id="@+id/ourpicks_horizontal_cell"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:orientation="vertical">
<com.goldengekko.o2pm.presentation.util.TextView
android:id="@+id/ourtitle"
style="@style/H3"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:paddingStart="24dp"
android:paddingTop="48dp"
android:paddingEnd="24dp" />
<com.goldengekko.o2pm.composecard.carousal.CarousalCardView
android:id="@+id/ourpicks_horizontal_module"
android:layout_width="match_parent"
android:layout_height="wrap_content" />
<include layout="@layout/landing_page_list_divider" />
</LinearLayout>
<LinearLayout
android:id="@+id/categories_cell"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:orientation="vertical">
<com.goldengekko.o2pm.presentation.landing.categoriesmodule.CategoriesModuleView
android:id="@+id/categories_module"
android:layout_width="match_parent"
android:layout_height="wrap_content" />
<include layout="@layout/landing_page_list_divider" />
</LinearLayout>
<LinearLayout
android:id="@+id/tickets_cell"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:orientation="vertical">
<com.goldengekko.o2pm.presentation.landing.verticalmodule.VerticalModuleView
android:id="@+id/tickets_module"
android:layout_width="match_parent"
android:layout_height="wrap_content" />
<include layout="@layout/landing_page_list_divider" />
</LinearLayout>
<LinearLayout
android:id="@+id/o2deals_horizontal_cell"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:orientation="vertical">
<com.goldengekko.o2pm.presentation.util.TextView
android:id="@+id/o2title"
style="@style/H3"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:paddingStart="24dp"
android:paddingTop="48dp"
android:paddingEnd="24dp" />
<com.goldengekko.o2pm.composecard.carousal.CarousalCardView
android:id="@+id/o2deals_horizontal_module"
android:layout_width="match_parent"
android:layout_height="wrap_content" />
<include layout="@layout/landing_page_list_divider" />
</LinearLayout>
<LinearLayout
android:id="@+id/newest_group"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:orientation="vertical">
<com.goldengekko.o2pm.presentation.landing.verticalmodule.VerticalModuleView
android:id="@+id/newest_cell"
android:layout_width="match_parent"
android:layout_height="wrap_content" />
</LinearLayout>
</LinearLayout>
<View
android:id="@+id/landing_fragment_snow_flakes_tint"
android:layout_width="match_parent"
android:layout_height="match_parent"
android:background="@drawable/ic_tint_overlay_snow_flake" />
<com.goldengekko.o2pm.presentation.landing.SnowView
android:id="@+id/landing_fragment_snow_flakes"
android:layout_width="match_parent"
android:layout_height="match_parent" />
</FrameLayout>
</androidx.core.widget.NestedScrollView>
ya...@gmail.com <ya...@gmail.com> #20
ya...@gmail.com <ya...@gmail.com> #21
context: Context,
attrs: AttributeSet? = null,
defStyle: Int = 0
) : AbstractComposeView(context, attrs, defStyle) {
private var selectedSortTab = mutableStateOf(Tab.NEARBY)
var list by mutableStateOf<List<ListModel>>(emptyList())
var onClick by mutableStateOf({ index: Int -> })
@Composable
override fun Content() {
CardsMaterialTheme(content = { VerticalOffersList() })
}
@ExperimentalCoilApi
@Composable
private fun VerticalOffersList() {
val listState = rememberLazyListState()
Column(
modifier = Modifier
.background(colorResource(id = R.color.light_grey))
.fillMaxWidth()
.fillMaxHeight()
.nestedScroll(rememberViewInteropNestedScrollConnection())
) {
LazyColumn(
modifier = Modifier
.background(colorResource(id = com.goldengekko.o2pm.common.R.color.light_grey))
.fillMaxWidth(),
state = listState
) {
item {
TextTab(mapSelectedTabIndex(selectedSortTab.value)) {
tabClicked()
}
}
items(0, itemContent = { index ->
})
}
}
}
me...@gmail.com <me...@gmail.com> #22
[Deleted User] <[Deleted User]> #23
I have a simple hierarchy:
Coordinator
- AppBarLayout with height > ?actionBarSize (scrollFlags = scroll)
- ComposeView
- LazyColumn(rememberViewInteropNestedScrollConnection)
The regular scroll works fine, but when I do fling, coordinator intercepts only part of a velocity, doesn't matter how "hard" I swipe.
Don't you guys have this issue?
Are there any workaround for fling behavior?
ma...@google.com <ma...@google.com> #24
Is there any planned release for this fix? Should we hope it for 1.2.0 alpha 3?
No updates yet in this regard, we will let you know when there's a known release this will be provided in.
ni...@google.com <ni...@google.com>
ma...@google.com <ma...@google.com>
di...@swiggy.in <di...@swiggy.in> #25
NestedScrollView
ComposeView
LazyColumn
RecyclerView
The following error is thrown:
java.lang.IllegalStateException: Nesting scrollable in the same direction layouts like LazyColumn and Column(Modifier.verticalScroll()) is not allowed. If you want to add a header before the list of items please take a look on LazyColumn component which has a DSL api which allows to first add a header via item() function and then the list of items via items().
ma...@google.com <ma...@google.com> #26
This is an unrelated problem to the nested scroll in this ticket. In your case error message indicates that you are on the old compose version, newer error message explain how to fix it. Right now it says
Vertically scrollable component was measured with an infinity maximum height " +
"constraints, which is disallowed. One of the common reasons is nesting layouts " +
"like LazyColumn and Column(Modifier.verticalScroll()). If you want to add a " +
"header before the list of items please add a header as a separate item() before " +
"the main items() inside the LazyColumn scope. There are could be other reasons " +
"for this to happen: your ComposeView was added into a LinearLayout with some " +
"weight, you applied Modifier.wrapContentSize(unbounded = true) or wrote a " +
"custom layout. Please try to remove the source of infinite constraints in the " +
"hierarchy above the scrolling container.
If you observe any bug in the toolkit - please file another issue, otherwise there's stackoverflow and kotlinlang slack where community can help with the code and error.
ap...@google.com <ap...@google.com> #27
Branch: androidx-main
commit fc31a066f4f02a0b9e7a22de99a4255602200a06
Author: Levi Albuquerque <levima@google.com>
Date: Tue Mar 01 09:50:06 2022
Nested Scrolling Interop Phase 1: Scrolling compose in cooperating view
In this first implementation of the nested scrolling interop we're adding support
for the case where a Compose scrolling container is placed within a cooperating
view. This means the View can receive scroll deltas from a scrolling child (it already
implements NestedScrollingParent3). This is the case of a CoordinatorLayout.
Test: Added sample and tests to check the behavior.
Relnote: "Enable Nested Scroll interop between View and Compose for
cooperating View classes. This means compose is now able to dispatch scroll deltas to a (cooperating) View parent."
Fixes: 174348612
Change-Id: I5d1ac5fdeced612ac07f0c26ce14284a43369673
A compose/ui/ui/samples/src/main/java/androidx/compose/ui/samples/NestedScrollInteropSamples.kt
M compose/ui/ui/src/androidMain/kotlin/androidx/compose/ui/platform/Wrapper.android.kt
M compose/ui/ui/api/current.txt
A compose/ui/ui/src/androidMain/kotlin/androidx/compose/ui/platform/NestedScrollInteropAdapter.kt
M compose/ui/ui/build.gradle
M compose/ui/ui-tooling/src/androidAndroidTest/kotlin/androidx/compose/ui/tooling/ComposeViewAdapterTest.kt
M compose/ui/ui/integration-tests/ui-demos/src/main/java/androidx/compose/ui/demos/viewinterop/PointerInputInteropComposeInAndroid.kt
M compose/ui/ui/src/androidMain/kotlin/androidx/compose/ui/platform/AndroidComposeView.android.kt
M compose/ui/ui/api/restricted_current.txt
A compose/ui/ui/src/androidAndroidTest/res/layout/test_nested_scroll_coordinator_layout.xml
A compose/ui/ui/integration-tests/ui-demos/src/main/res/layout/compose_in_android_coordinator_layout.xml
M compose/ui/ui/integration-tests/ui-demos/build.gradle
A compose/ui/ui/src/androidAndroidTest/kotlin/androidx/compose/ui/viewinterop/NestedScrollInteropTest.kt
M compose/ui/ui/integration-tests/ui-demos/src/main/AndroidManifest.xml
M compose/ui/ui/api/public_plus_experimental_current.txt
le...@google.com <le...@google.com>
ap...@google.com <ap...@google.com> #28
Branch: androidx-main
commit 697235331591da8efbc20a8f4a9896b2e17b0c1e
Author: Levi Albuquerque <levima@google.com>
Date: Tue Mar 22 16:31:44 2022
Nested Scrolling Interop Phase 2: A scrolling view child inside a compose parent
In this phase I made AndroidViewHolder to implement NestedScrollingParent3.
That way it will act as a nested scroll parent for a containing view.
At the same time, it uses an internal NestedScrollDispatcher to forward
deltas to a Compose Parent, so it also can act as a nested scroll child
for a compose parent.
Test: Added sample and tests to check the behavior.
Relnote: "Enable Nested Scroll interop between Compose and View in the
direction Compose > View. This means that a compose parent will be able to
receive nested scroll deltas from a nested scroll view."
Fixes: 174348612
Change-Id: If7949ccb50ba7897bc740b489fd3f4d615a5369b
M compose/ui/ui/samples/src/main/java/androidx/compose/ui/samples/NestedScrollInteropSamples.kt
M compose/ui/ui/src/androidAndroidTest/kotlin/androidx/compose/ui/viewinterop/NestedScrollInteropConnectionTest.kt
A compose/ui/ui/src/androidAndroidTest/res/layout/android_in_compose_nested_scroll_interop_list_item.xml
A compose/ui/ui/src/androidAndroidTest/res/layout/test_nested_scroll_coordinator_layout_without_toolbar.xml
M compose/ui/ui/api/current.txt
M compose/ui/ui/samples/build.gradle
M compose/ui/ui/src/androidMain/kotlin/androidx/compose/ui/viewinterop/AndroidViewHolder.android.kt
M compose/ui/ui/src/androidMain/kotlin/androidx/compose/ui/viewinterop/AndroidView.android.kt
M compose/ui/ui/build.gradle
M compose/ui/ui/integration-tests/ui-demos/src/main/java/androidx/compose/ui/demos/viewinterop/PointerInputInteropAndroidInCompose.kt
A compose/ui/ui/integration-tests/ui-demos/src/main/res/layout/android_in_compose_nested_scroll_interop_list_item.xml
M compose/ui/ui/api/restricted_current.txt
A compose/ui/ui/integration-tests/ui-demos/src/main/res/layout/android_in_compose_nested_scroll_interop.xml
A compose/ui/ui/src/androidAndroidTest/res/layout/android_in_compose_nested_scroll_interop.xml
M compose/ui/ui/src/androidMain/kotlin/androidx/compose/ui/platform/NestedScrollInteropConnection.kt
A compose/ui/ui/src/androidAndroidTest/kotlin/androidx/compose/ui/viewinterop/NestedScrollInteropViewHolderTest.kt
M compose/ui/ui/api/public_plus_experimental_current.txt
ub...@gmail.com <ub...@gmail.com> #29
The change from requestDisallowInterceptTouchEvent()
setup from setNestedScrollingEnabled()
on top of the MapView
and a scrollable()
Modifier on a parent Box
. Am I missing something, or does this not work with MapView for some reason? My MapView is inside a horizontal accompanist pager; I tried both vertical and horizontal orientations for scrollable()
, but either one just gets swallowed, and I cannot drag the map in the specified orientation, or drag the pager element.
le...@google.com <le...@google.com> #30
Hi, thanks for your comment. The Modifier.scrollable in the example is just an illustration so one can see the scroll deltas being propagated up the nested scrolling chain across view and compose. If I understand you example correctly, you have a compose scrollable parent (HorizontalPager) + a View (MapView) child. In this case, just setNestedScrollingEnabled() should suffice.
ub...@gmail.com <ub...@gmail.com> #31
Gotcha, thanks for the quick response. Alas, it did not fix my problem. What I want is to have my MapView be scrollable horizontally inside a horizontal accompanist pager. (There is more screen real estate besides the MapView that the user can drag the pager on.) With or without setNestedScrollingEnabled()
, only the pager ever scrolls.
Here's the rough Compose tree:
HorizontalPager {
Column {
AndroidView({
MapView().also { ViewCompat.setNestedScrollingEnabled(it, true) }
})
Text("Drag me to flip the page")
}
}
le...@google.com <le...@google.com> #32
Hey, so from the sample you added above, if you swipe on top of the text "Drag me to flip the page" the pager goes to the next page, but if you do the same on top of the map, you want the map to scroll instead of the pager? I can see how this would be the desired behavior, but ff this is the case we'll need to re-check. MapView is a "special" type of View and I don't think it dispatches NestedScrolling the same way other scrollable containers would (using the nested scrolling system).
ub...@gmail.com <ub...@gmail.com> #33
Yes, that is the desired behavior. Short of Compose support, I am able to make it happen by wrapping the MapView inside this custom view; it's a pattern from androidx ViewPager2:
class TouchWrappingLayout(context: Context) : FrameLayout(context) {
override fun onInterceptTouchEvent(e: MotionEvent): Boolean {
parent.requestDisallowInterceptTouchEvent(true)
return super.onInterceptTouchEvent(e)
}
}
mr...@gmail.com <mr...@gmail.com> #34
On non-compose version a fling event propagates to top appbar, so it collapses smothly
But compose behaviour is different - appbar wont collapse (or expand) when I do a fling gesture
Am I doing something wrong?
n....@gmail.com <n....@gmail.com> #35
le...@google.com <le...@google.com> #36
Hi, we are aware that nestedScroll has some issues with fling movements (see:
wo...@ubique.ch <wo...@ubique.ch> #37
I'm having trouble using Compose nested scrolling inside a BottomSheet. Scrolling upwards (so further down in a list) works fine, meaning it first expands the BottomSheet before scrolling in the Compose LazyColumn. However scrolling downwards (so further up in a list) immediately starts collapsing the BottomSheet instead of first scrolling to the top of the LazyColumn and then collapsing the BottomSheet. Is there a fix or workaround for this issue?
Here's an example:
XML:
<?xml version="1.0" encoding="utf-8"?>
<androidx.coordinatorlayout.widget.CoordinatorLayout xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:app="http://schemas.android.com/apk/res-auto"
android:layout_width="match_parent"
android:layout_height="match_parent"
android:background="@android:color/white">
<LinearLayout
android:id="@+id/bottomSheet"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:background="#e9e9e9"
android:orientation="vertical"
android:padding="5dp"
app:layout_behavior="@string/bottom_sheet_behavior">
<androidx.compose.ui.platform.ComposeView
android:id="@+id/composeContent"
android:layout_width="match_parent"
android:layout_height="match_parent" />
</LinearLayout>
</androidx.coordinatorlayout.widget.CoordinatorLayout>
Activity:
class ComposeActivity : AppCompatActivity() {
@OptIn(ExperimentalComposeUiApi::class)
override fun onCreate(savedInstanceState: Bundle?) {
super.onCreate(savedInstanceState)
val binding = ActivityComposeBinding.inflate(layoutInflater)
setContentView(binding.root)
binding.composeContent.setContent {
LazyColumn(
Modifier
.nestedScroll(rememberNestedScrollInteropConnection(binding.composeContent))
.fillMaxSize()
) {
items(100) {
Text("Item $it", Modifier.fillMaxWidth(), Color.Black)
}
}
}
}
}
[Deleted User] <[Deleted User]> #38
wo...@ubique.ch <wo...@ubique.ch> #39
Neither the official Compose rememberNestedScrollInteropConnection()
nor the ViewInteropNestedScrollConnection
from #9 fixes the problem I described in #37. I've attached a video showing the issue. When the bottom sheet is fully expanded and the nested composable is not scrolled to the top, I would expect to first scroll the nested composable and not start dragging the bottom sheet down until its collapsed state is reached.
le...@google.com <le...@google.com> #40
Hi, I have tested #37 locally using rememberNestedScrollInteropConnection() and I see the correct behavior. Are you using the latest version of compose? I noticed your rememberNestedScrollInteropConnection still has binding.composeContent, whilst in the latest version we don't need to pass the compose view anymore.
le...@gmail.com <le...@gmail.com> #41
This rememberNestedScrollInteropConnection() is not working for me. Can you please explain what is going wrong here?
My situation is that I'm building a bottom sheet with using BottomSheetDialogFragment. Here is my layout
<androidx.coordinatorlayout.widget.CoordinatorLayout
xmlns:android="http://schemas.android.com/apk/res/android"
android:layout_width="match_parent"
android:layout_height="match_parent" >
<androidx.compose.ui.platform.ComposeView
android:id="@+id/composeView"
android:layout_width="match_parent"
android:layout_height="match_parent" />
</androidx.coordinatorlayout.widget.CoordinatorLayout>
Here is how I render my ComposeView, note that the top node of the ComposeView is not LazyColumn:
composeView.setContent {
Column // Set nestedScroll(rememberNestedScrollInteropConnection()) here
- Composable 1 (Top bar)
- Columm(modifier = Modifier.weight(1f))
- LazyColumn // Doesn't work with this LazyColumn here
- Composable 2 (Bottom bar)
}
What I'm doing wrong here? Note that using the workaround from #9 works for me. I use #9 directly to the LazyColumn as it does not require binding with composeView.
wo...@ubique.ch <wo...@ubique.ch> #42
#40: I am using Compose 1.2.0-alpha08, which at the time of writing is the latest version in the Google Maven. When checking the 1.2.0-alpha08 sources jar from rememberNestedScrollInteropConnection
function in NestedScrollInteropConnection.kt
still requires passing a ComposeView as parameter. I can see
I have attached a sample project which shows the behavior I reported in #37 using Compose 1.2.0-alpha08 and using either the official NestedScrollInteropConnection
or the ViewInteropNestedScrollConnection
from #9.
le...@google.com <le...@google.com> #43
Hi #41, could you provide a full code sample so I can better understand and test it? From your sample alone I don't see anything apart the ComposeView inside your xml so I'm not sure why you're using the interop connection here. Thank you!
le...@google.com <le...@google.com> #44
Hi #42, thanks for the sample. Yes, I'm looking into the schedule and it seems that it hasn't been released yet. You can copy and paste it to test it out, but I'm attaching a video I made with the latest version and this seems to be working. Once you're able to test it (either by copying and pasting or waiting for the next release) let me know if the issue is really solved. I'll keep an eye out for this, thanks for reporting!
fl...@polarsteps.com <fl...@polarsteps.com> #45
wo...@ubique.ch <wo...@ubique.ch> #46
Hi #44: I've updated to the new Compose 1.2.0-beta01 where the new parameter-less rememberNestedScrollInteropConnection()
function is available. However, this update made it worse in my sample project, as the bottom sheet now no longer expands/shrinks at all when scrolling in the compose content. Only grabbing the bottom sheet itself changes the expanded state. I've attached a video of the new behavior.
le...@google.com <le...@google.com> #47
Hi #46, this is the version of your sample I have used to generate the video above. I only made minimal changes to update the version and remove unnecessary code. I have just tried it again and it seems to be working.
If the sample you're using is different than this one, could you file a bug in another ticket so we can follow up from there?
Thank you.
wo...@ubique.ch <wo...@ubique.ch> #48
Hi #47: You're right, it is working with the new 1.2.0-beta01. I still had a reference to the rememberViewInteropNestedScrollConnection(binding.composeContent)
from #9 in the code, which interfered with the NestedScrollingChildHelper of the official rememberNestedScrollInteropConnection()
. Thanks a lot for the support and the fix!
kr...@codequest.com <kr...@codequest.com> #49
#47, I modified your BottomSheetCompose2.zip to provide a minimal reproducible example. My main issue is that some places of bottom sheet don't react at all to drag&dismiss gesture (apart from `LazyColumn`). Please see `BottomSheetFragment` class for reference.
ch...@gmail.com <ch...@gmail.com> #50
One of the issues here is that BottomSheetBehavior
calls view.canScrollVertically()
on the nested scrolling view. ComposeView
doesn't implement that, so it's a bit broken atm. I'm surprised this wasn't picked up in testing?
We previously had a workaround of manually creating a 'proxy' nested scrolling view, attaching it via AndroidView
and then instrumenting its canScrollVertically
function based on the LazyListState
. That no longer works in Compose 1.2.0 as rememberNestedScrollInteropConnection()
uses the LocalView
internally rather than allowing us to pass that view through.
qu...@gmail.com <qu...@gmail.com> #51
le...@google.com <le...@google.com> #52
Hi #49, thank you for reporting this. I looked into your sample and I think some clarifications are needed, we're working on adding more documentation around the nested scroll interop in order to avoid confusions in the future, but for now here's the explanation:
- rememberNestedScrollInteropConnection will install a NestedScrollConnection in the element you attached it to, in this case the Box element. The NestedScrollConnection is responsible for transmitting the deltas from the Compose level to the View level. Note that it enables the element to participate in nested scrolling, but it doesn't scroll elements automatically.
- Box is not scrollable automatically, so when you touch the non-scrollable area it, Compose won't propagate deltas in the nested scroll system and the deltas won't reach the NestedScrollConnection provided by rememberNestedScrollInteropConnection, therefore those deltas won't reach the view world.
- When you touch the scrollable portion of compose, the LazyList, the deltas are propagated in the nested scroll system and that's why it works when you scroll it.
- To have the scrolling deltas reach the view world you'd need to either include your text in the Lazy list as a one-off element (using the item{} DSL or to make your outter box scrollable using the .scrollable modifier.
Hopefully the explanation makes sense, let us know what you think. Thank you!
le...@google.com <le...@google.com> #53
Hi #51, the issues with fling is on our roadmap for upcoming releases, stay tuned to these two tickets (
fj...@gmail.com <fj...@gmail.com> #54
I want to scroll pager when i have scrolled to the bottom of lazy column, but its not working.
I tried using rememberNestedScrollInteropConnection, but it did not work.
I Have ViewPager with vertical orientation, Which has fragment with structure like this
<NestedScrollableHost>
<ComposeView>
</NestedScrollHost>
ComposeView has lazy column with vertical orientation.
Seems thats because ComposeView return canScrollVertically as false. I can't use requestDisallowInterceptTouchEvent() to decide to whom give scroll permission. Is there any workaround that i could use to make it work?
Here is the code for NestedScrollableHost:
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 parentViewPager: ViewPager2?
get() {
var v: View? = parent as? View
while (v != null && !(v is ViewPager2 && (v as? ViewPager2)?.orientation == ORIENTATION_VERTICAL)) {
v = v.parent as? View
}
return v as? ViewPager2
}
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) {
val orientation = parentViewPager?.orientation ?: return
// Early return if child can't scroll in same direction as parent, except if child is also viewpager
if (child !is ViewPager2
&& !canChildScroll(orientation, -1f)
&& !canChildScroll(orientation, 1f)
) {
return
}
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)) {
if (child is ViewPager2) {
// In case child and parent are both ViewPagers - calculate on which angle,
// which widget will be swiped
val angle = getAngle(initialX, initialY, e.x, e.y)
// Right
if (inRange(angle, 0f, 55f) || inRange(angle, 305f, 360f)) {
parent.requestDisallowInterceptTouchEvent(true)
// Left
} else if (inRange(angle, 125f, 235f)) {
parent.requestDisallowInterceptTouchEvent(true)
} else {
parent.requestDisallowInterceptTouchEvent(false)
}
} else {
// 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)
}
}
}
}
}
private fun getAngle(x1: Float, y1: Float, x2: Float, y2: Float): Double {
val rad = atan2((y1 - y2).toDouble(), (x2 - x1).toDouble()) + Math.PI
return (rad * 180 / Math.PI + 180) % 360
}
private fun inRange(angle: Double, init: Float, end: Float): Boolean {
return angle >= init && angle < end
}
}
[Deleted User] <[Deleted User]> #55
lu...@bertelsmann.de <lu...@bertelsmann.de> #57
The header can be dragged as expected while the list is scrolled fully to the top, however as soon as this is not the case dragging on the header does no longer work as expected, I can only drag the header a few pixels, if at all, and then it instantly dismisses the BottomSheetDialogFragment.
This is what my code looks like:
Box(
Modifier
.nestedScroll(rememberNestedScrollInteropConnection())
) {
Column({
Box(Modifier
.height(50.dp)
.fillMaxWidth()
.verticalScroll(rememberScrollState()),
contentAlignment = Alignment.Center
) {
// Header
}
Column(
Modifier
.fillMaxWidth()
.wrapContentHeight()
.verticalScroll(rememberScrollState()), content = content
)
}
}
Can someone maybe tell me what I am doing wrong, or how to fix this?
Description
when nested scrolling in compose is done, we need to consider supporting cases for view interop when compose is either a parent or a child.