MotionLayout animate content recyclerview or viewpager - android

I have to implement a transition of either a viewpager o a recyclerview, not sure yet which could fit better. The idea is to expand de ViewPager or Recycler and show more information when this is expanded and less information when is collapsed.
Expand and collapse with MotionLayout is quite easy however I cannot find the approach for animate and change the content shown in each element of the the adapter.
is it posible to animate the content of an adapter simultaneously?

I an ugly solution for you. I use a recycler-view that contains some motion layouts. There is an expanded state and a collapsed state, we only want one expanded at a time.
A method to close positions in the fragment/activity
fun collapseMotionLayout(positions: IntArray){
positions.forEach { position ->
recyclerView.findViewHolderForAdapterPosition(position)?.let { childView ->
//transitionToEnd() or transitionToStart() or toggleUI() or whatever
(childView.itemView.findViewWithTag("motionlayout") as? MotionLayout)?.collapse()
}
}
And then a fragment/activity call back in the motion layout's TransitionListener that you'll define in the viewholder (we use a custom view)
tListener = object: TransitionListener{
override onCompleted(){
callBack.invoke(positionsForAnimation())
}
}
I only see this collapse a single motion layout at a time. I have found that multiple motion layouts running simultaneously will will cause performance issues.

Related

Lazy Column/Row recomposition and perfomance

I have the following structure in the LazyColumn:
item {
// complex layout with long time of recomposition and inside the HorizontalPager
}
items(list) {
// item content
}
The issue is when I scroll down until the first item becomes invisible and scroll back, every time the first item appears on the screen it freezes.
I know that lazy layouts recompose their items unlike the RecyclerView which just binds data to a ViewHolder. But in this case the lazy layout works awful. Recomposing my first item takes too much time to provide a smooth scroll to the top.
Also I've mentioned in the structure that my complex layout is inside the HorizontalPager, which is a lazy layout too. Swiping this pager is not smooth either.
How to handle this? Is there a way to cache or prevent recomposition in a lazy layout? Something like RecyclerView binding?

How to propagate progress from root MotionLayout to its child RecyclerView which contains MotionLayout item?

I have fragment with MotionLayout (1). One of the child layouts is expanded by swiping it down (custom-made Toolbar). This child layout contains RecyclerView with another MotionLayout (2) inside each item.
TransitionListener is connected to (1).
When MotionLayout (1) detects swiping transition (with TransitionListener) it should propagate its progress to currently visible RecyclerView item's MotionLayout (2) so that animation of the custom-made Toolbar expanding AND animation of the visible RecyclerView item are synchronized.
Problem is that although RecyclerView item receives progress value manually sent by TransitionListener.onTransitionChange method, RecyclerView item animation is frozen to one of the start/end states and no animation is visible.
Outside RecyclerView custom-made Toolbar swiping down sends progress to MotionLayout (2) and state changing and animations occur normally.
MotionScene is programmatically added to each RecyclerView item's MotionLayout because data is dynamic and cannot be described in motion_scene.xml of the MotionLayout (2) alone. (I used the https://medium.com/androiddevelopers/working-with-dynamic-data-in-motionlayout-9dbbcfe5ff75 method for programmatically adding MotionScene transitions and states).
How do I have MotionLayout (1) which propagates progress to RecyclerView item's 'MotionLayout' (2)?
Currently there isn't a way to do this with RecyclerView without some Kotlin. Below I'll go into detail about how to do that.
The way I've propagated progress to each ViewHolder is to set a RecyclerView.OnScrollListener and within onScrolled:
Get a reference to the RecyclerView's current LayoutManager
Use findFirstVisibleItemPosition and findLastVisibleItemPosition to get the range of ViewHolders I should be looking for.
Use LayoutManager.findViewByPosition to get a View reference and RecyclerView.getChildViewHolder to get each ViewHolder reference.
Filter out null results
Calculate the progress for each particular ViewHolder for my use-case.
Update the progress of each ViewHolder
In practice (and with some extension functions that handle those bits) the overall flow in Kotlin should look like this:
recyclerView.activeViewHolders(onlyVisible = true)
.mapNotNull { viewHolder -> viewHolder as? WidgetViewHolder }
.forEach { viewHolder ->
val position = viewHolder.adapterPosition
val awayFromCenter = recyclerView.calculateHorizontalOffsetPercentage(position)
viewHolder.resize(awayFromCenter)
}
This solution has worked smoothly for me with both Kotlin synthetics & ViewBinding.
Because you mentioned you have pretty dynamic data it doesn't sound like Carousel can work for you, but you should check it out as its currently in development: https://github.com/androidx/constraintlayout/wiki/Carousel

Is there a way to transition a BottomSheet to an Activity?

Currently, I have a bottomSheetBehavior layout that is reused in several activities, but requirements have changed and logic inside it has increased. My question is, is there a way to keep the functionality of bottom sheet collapsed, but when expanded, transition to an activity?
Specifically, when the bottom sheet is collapsed, allow clicks and scroll on the rest of the view, but when expanded, start a new activity so logic can be handled there.
Any advice is welcome.
What I ended up doing was to set the Bottom Sheet Behaviour inside a View Model, and then, the view model posts whatever data to the view using Live Data, at least that ensures logic is contained in one place.

Pass RecyclerView press to super view

I have a RecyclerView (which scrolls vertically) whose ViewHolders contain RecyclerViews that scroll horizontally. When you tap on an item in horizontal (nested) RecyclerViews, I want the entire row to have the ripple effect.
To do this, I've been trying to override the touch events and get them to get passed up the view stack (by returning false in touch event handlers). This works on views other than RecyclerViews, but it isn't having the desired effect for RecyclerViews.
How do I correctly pass the tap event on a RecyclerView up to the enclosing view?
Haven't found a way to pass the click event up to a recycler view's parent view, but the important thing was getting the ripple effect to happen – which I was able to do. So when a view holder's itemView get's tapped, I do this:
val background = itemView.background
if (background is RippleDrawable) {
background.setState(intArrayOf(android.R.attr.state_pressed, android.R.attr.state_enabled))
}
Which manually triggers the ripple effect.

Why RecyclerView items disappear immediately when using Transition API?

This is a question regarding the use of Android Transition API.
I am trying to animate the height change of a list, just like a dropdown menu.
I tried 2 approaches
Use a RecyclerView and animates its height change
Use a ScrollView > LinearLayout hierarchy and animates ScrollView's height.
The 2nd approach works perfectly.
But the 1st approach has a serious glitch - when the collapse transition starts, items disappear immediately.
By looking at the below GIF you can observe clearly the difference:
To be exact, items' visibility changes at the moment I change RecyclerView's LayoutParams, without waiting for the transition to finish, whatever it is expanding or collapsing
Code
I have created a minimal project on Github.
If you just want to look at the code, here is the MainActivity.
Question
Is it possible to achieve ScrollView's effect with a RecyclerView?
If yes, how?
My Idea is to do the transition of all the recycler view rows individual rather than the whole RecyclerView:
So when collapsing iterate through each ROW of a RecyclerView and do a transition. Remember to check for null if some rows are recycled they may return null. So after that collapse the whole recyclerView.
And like wise for the expanding do the same for the views.
This issue is cause by RecyclerView has many views with it but Scroll View has only one View nested in it.

Categories

Resources