Scroll issues with LazyRow inside traditional android ViewPager - android

When using the jetpack compose interoperability API, using LazyRow inside a prebuilt ViewPager causes scroll issues.
When trying to scroll items inside the LazyRow the ViewPager moves. Is there any way we can prevent this?
ComposeView
<androidx.compose.ui.platform.ComposeView xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:app="http://schemas.android.com/apk/res-auto"
android:id="#+id/compose_view"
android:layout_width="match_parent"
android:layout_height="wrap_content" />
Setting the view in the fragment
binding.composeView.setContent {
HorizontalScrollableView(listOfCards)
}
And the view written in compose
#Composable
fun HorizontalScrollableView(listOfCards: List<Cards>) {
Column(
modifier = Modifier
.background(color = colorResource(R.color.grey10))
) {
Text(
text = "Items",
color = colors.primary,
style = typography.subheadBold,
fontSize = 15.sp,
modifier = Modifier.padding(start = 16.dp, top = 10.dp, end = 16.dp)
)
LazyRow(contentPadding = PaddingValues(end = 16.dp)) {
items(
items = listOfCards,
key = { listOfCards.id }
) {
RenderCard(it)
}
}
}
}

It is a normal behavior you experience.The similar problem is described here.
Link
Alternatively, lazy row gestures can be controlled.
Modifier.pointerInput(Unit) {
detectTapGestures(
onPress = { /* Called when the gesture starts */ },
onDoubleTap = { /* Called on Double Tap */ },
onLongPress = { /* Called on Long Press */ },
onTap = { /* Called on Tap */ }
)
}
In onPress you can cancel the scroll state you want
Apart from composing, we were solving it this way.
Link
I hope this helps. Good job

This is mainly because ComposeView is not implementing canScrollHorizontally correctly. ViewPager uses this method to decide whether it intercepts touch events.
A workaround:
Create a class extending FrameLayout, override canScrollHorizontally to always return true, and wrap ComposeView with this container. In this way, the scrolling in ComposeView works normally. The shortcoming of this workaround is that the ViewPager won't scroll if you are moving your finger on ComposeView, even when ComposeView has scrolled to the end.

Related

The title won't minimize dialog in JetPack Compose BottomSheetDialog with rememberNestedScrollInteropConnection

I have BottomDialog Fragment which consists only of Composable (Row(title) and LazyColumn).
The first issue I faced was when you scroll your list down and then you try to scroll up list won't scroll and the dialog starts to minimize. This is solved with
modifier = Modifier.nestedScroll(rememberNestedScrollInteropConnection())
But now user can't minimize a dialog when he tries to do it by touching a title. And there is my question, How to solve this?
Minimum reproducible code
During creating this example I found that I can maximize dialog when touching the title, also I can start moving the action going to the top with my finger (to start to expand it) and then move the finger to the bottom of the screen, in this way the dialog will be dismissed, but still can't minimize it in a non-tricky way.
ComposeView(requireContext()).apply {
setViewCompositionStrategy(ViewCompositionStrategy.DisposeOnViewTreeLifecycleDestroyed)
setContent {
Theme {
Column(modifier = Modifier.nestedScroll(rememberNestedScrollInteropConnection())) {
Row {
Text(
modifier = Modifier
.padding(16.dp)
.fillMaxWidth(),
text = "Title"
)
}
LazyColumn(
Modifier
.weight(1f)
.fillMaxWidth()
) {
items(100) {
Text(
text = "Item $it",
modifier = Modifier
.padding(16.dp)
.fillMaxWidth(),
)
}
}
}
}
}
}
Please do not propose BottomSheetScaffold. Because I want to create a standardized bottom dialog. And to use it I will need just pass a list of items and a Compose function for one item.
IMO BottomSheetScaffold shouldn't be released at all, cause it was designed "not well". Just imagine earlier before Jetpack Compose you write your code around the bottom dialog, nice layering. No.
As a temp decision. I just think of LazyColumn works properly so I need to wrap my header to the LazyColumn.
So I created this function. And Just pass here any Composable
#Composable
fun TrickyLazyColumn(content: #Composable () -> Unit) {
LazyColumn {
items(
items = listOf(1),
itemContent = {
content.invoke()
})
}
}

Alternate use of View.INVISIBLE in jetpack compose

In xml we use View.INVISIBLE to just don't show the view at all, but it still takes up space for layout purposes. What is the alternative in jetpack compose?
AnimatedVisibility(
// true or false
) {
Button() // button code.
}
You can build a custom modifier to measure the Composable to take up space.
Something like:
fun Modifier.visible(visible: Boolean) = if (visible) this else this.then(Invisible)
private object Invisible : LayoutModifier {
override fun MeasureScope.measure(
measurable: Measurable,
constraints: Constraints
): MeasureResult {
val placeable = measurable.measure(constraints)
return layout(placeable.width, placeable.height) {}
}
}
In Jetpack Compose, you can use the Visibility component to control the visibility of a view. The Visibility component is similar to the View.INVISIBLE property in XML, but it also supports the View.GONE behavior.
As a modifier for a view:
Button(
onClick = { /* Do something */ },
modifier = Modifier.visibility(visible = true)) {Text("Button")}
In this example, the Button view is modified with the visibility modifier. The visible parameter of the modifier determines whether the view should be visible or not.
As a container for a view:
Visibility(visible = true) {
Button(onClick = { /* Do something */ }) {
Text("Button")
}
}
In this example, the Visibility component is used as a container for the Button view. The visible parameter of the Visibility component determines whether the view should be visible or not.
The AnimatedVisibility component is similar to the Visibility component, but it also supports animating the visibility changes. You can use AnimatedVisibility in the same way as Visibility, with the added benefit of animation:
AnimatedVisibility(
visible = true, // true or false
enter = slideInVertically(),
exit = slideOutVertically()
) {
Button(onClick = { /* Do something */ }) {
Text("Button")
}
}
In this example, the Button view is wrapped in an AnimatedVisibility component. The enter and exit parameters of the AnimatedVisibility component define the animation to use when the visibility changes. In this case, the slideInVertically() and slideOutVertically() functions are used to create a slide animation.

How to collapse/expand widget code region/area in Compose Android Studio like in XML

I have many Composables and I want to collapse Composable code inside like in xml. Is there extension for that?
Your post title is a bit misleading, but I think your'e asking how to collapse/expand "code" not the actual widget/ui.
I'm not sure if this is exactly what you want, but you can expand/collapse a specific area of your code if you wrap them within region/endregion without the need of any plugin or configuration, its almost the same behavior that your'e expecting from the xml editor, and you can do this anywhere not only to a function.
expanded code region
collapsed code region
Sample Inner composable expanded
Sample Inner composable collapsed
If you wish to make your Column collapse or expand without animation you simply need to add a if statement and set true to display false to collapse
var visible by remember {
mutableStateOf(true)
}
Column(modifier = Modifier.fillMaxSize()) {
Text("Click to expand or collapse", modifier = Modifier
.fillMaxWidth()
.clickable {
visible = !visible
}
)
if(visible) {
// Content to be collapsed or displayed
}
}
If you wish to collapse or expand with animation you can check out AnimatedVisbility composable
var visible by remember {
mutableStateOf(true)
}
Column(modifier = Modifier.fillMaxSize()) {
Text("Click to expand or collapse", modifier = Modifier
.fillMaxWidth()
.clickable {
visible = !visible
}
)
AnimatedVisibility(visible = visible) {
Column {
// Content to be collapsed or displayed
}
}
}

How to implement 'Snap to center' feature for lists in Jetpack Compose?

In EpoxyRecyclerView with Horizontal LinearLayout there is a Snap to center feature which works like, If i scroll the list with good speed, it keeps on scrolling until it slows down and rests with an item at center. And if I scroll slowly and lift up the finger, then the next item spans/moves to center of screen. One thing you have to understand that, this is not a Pager. Pager automatically snaps the next item only. But I cannot scroll like a free rolling...
You can see this gif as an example
So, I'm looking for such snapping feature in Jetpack Compose. Is this possible yet? If yes, how to achieve this?
You can use this library as well https://github.com/chrisbanes/snapper
https://chrisbanes.github.io/snapper/
val lazyListState = rememberLazyListState()
LazyRow(
state = lazyListState,
flingBehavior = rememberSnapperFlingBehavior(lazyListState),
) {
// content
}
If you use Compose 1.3 you can check the SnapFlingBehavior here
You can try out this library: https://github.com/aakarshrestha/compose-pager-snap-helper
The code will look as follows (using LazyRow for listing the items)
ComposePagerSnapHelper(
width = 320.dp, //required
content = { listState -> //this param is provided by the method itself, add this param below.
LazyRow(
state = listState, //add listState param
) {
items(count = count) { item ->
//Put your Items Composable here
}
}
}
)
Starting with compose 1.3.0 you can use the FlingBehavior that performs snapping of items to a given position:
val state = rememberLazyListState()
LazyRow(
modifier = Modifier.fillMaxSize(),
verticalAlignment = Alignment.CenterVertically,
state = state,
flingBehavior = rememberSnapFlingBehavior(lazyListState = state)
) {
//item content
}

How to dispatch touch events to parent composables in Jetpack Compose

I can't seem to find much information on touch handling in Compose.
In the specific case I'm looking at I have a list like this:
#Composable
fun MyListComposable(items: List<Item>) {
LazyColumn(
contentPadding = paddingValues(listHorizontalMargin, listVerticalMargin),
) {
// Init items emitted for brevity
}
}
This list is contained in a parent which uses the swipeable modifier, something like this.
Card(
modifier = Modifier.swipeable(
state = state,
anchors = mapOf(
0.dp.value to DrawerState.OFFSCREEN,
50.dp.value to DrawerState.PEEKING,
maxHeight.value to DrawerState.EXPANDED,
),
reverseDirection = true,
thresholds = { _, _ -> FractionalThreshold(0f) },
orientation = Orientation.Vertical
) {
MyListComposable(items)
}
My problem is the list swallows all touches, so the swipable is never invoked. So my question is, is there a way to stop lazy column swallowing touches?

Categories

Resources