PagedListAdapter: Weird run time behaviours - android

I am using PageListAdapter in a fragment dedicated for chatting(just like in any modern messaging application). I initially used RecyclerView only, but it's very jittery. After that I searched further and came across PagedListAdapter, but, It's here with its unique limitations and bugs. it's more jittery and weird scrolling behaviour. Whenever the data is changed in DB which is loaded using RxJava Flowable, most of the views are recreated as all the messages/views in RecyclerView starts flickering, the scroll position automatically changes and scrolls to somewhere in middle.
So, basically I have these questions:
How to start the view with last page of PagedListAdapter?
How to prevent PagedListAdapter from auto-scrolling?

I had a similar issue when using RecyclerView inside the MotionLayout (from beta3/beta4).
I was using MotionLayout to achieve a swipeable overlay (from bottom in portrait and from left in horizontal mode).
In horizontal mode, updating the RecyclerView worked OK because in horizontal mode, the overlay was taking up the entire screen, and so did the RecyclerView.
But in portrait mode, I needed to have the overlay swiped only up to a half of screen, hence, the RecyclerView was not full screen.
In this case, the entire view was changing its size when the RecyclerView was updated, creating a jumpy list (when new chat message inserted, the list would jump to another position).
Still haven't figured it out why exactly, and I can't confirm it's because of the MotionLayout, but its the main suspect.
My solution to it was to fixate the height so the list wouldn't change size on update. I've overriden the getHeight function of the LayoutManager and set a fixed size that I got in onViewCreated() of the fragment.
Also, you need to keep this layout listener to have the size changed if the keyboard appears.
requireView().viewTreeObserver.addOnGlobalLayoutListener {
if (initialHeight != view.height) {
initialHeight = view.height
chatLayoutManager.requestLayout()
}
}
chatLayoutManager = object : LinearLayoutManager(requireContext(), VERTICAL, true) {
override fun getHeight() = initialHeight
}

Related

RecyclerView peek behaviour

I'm using a horizontal RecyclerView with PagerSnapHelper to make it look like a ViewPager. It's displaying child views.
How can I make it show a small peek of the next entry? The user needs to see close to 20% of the next entry to understand intuitively that they need to swipe horizontally. The earlier entry needs to align back.
There could be other indications of dots at the bottom but this is the expected behaviour. The child view occupies the entire recycler view, so adjusting the width of the child so as to make other siblings come onto the screen isn't an option.
Right now I'm using a RecyclerView that is set up with a PagerSnapHelper.
I'm able to scroll to 20% of the next entry by using this:
val nextPosition = (currentPosition + 1) % numOfEntries
val xscrollDistance = carouselRecyclerView.width * 0.80
linearLayoutManager.scrollToPositionWithOffset(nextPosition, xscrollDistance.toInt())
With this, I see the next entry come onto the screen like a peek but here's what I'm unhappy with:
This scroll isn't smooth, I'm looking for a smooth scroll to the 20% peek of the next entry
Snap doesn't work. I was hoping when I do step 1., the snap behaviour will kick in and align the earlier entry onto the screen. It doesn't happen that way.
The peeking behaviour also needs to be cyclic. Last entry's peek should be the 0th entry.
I'm also looking for a less complex solution if any.
I checked an older solution - How to peek RecyclerView + PagerSnapHelper but I'm hoping to find a cleaner solution instead.

Nested Scrolling inside a Viewpager2 inside a BottomSheet

It's 2022, and I'm having the same issue with ViewPager2 that folks had with ViewPager (see NestedScrolling inside a Viewpager inside a BottomSheetDialog ) - 5 years ago.
Although I'm not using a BottomSheetDialogFragment, just a regular old bottom sheet (with a FragmentContainer).
ViewPager2 is a bit different in that it itself uses a horizontal RecyclerView. BottomSheetView.findScollingChild() sees this as the scrolling child!
So, the approach I took to solving this is:
added a page change listener to my ViewPager2 that gets the hosting
CoordinatorLayout, and calls requestLayout() on it after the page change.
copied the BottomSheetBehavior class that matches my current material components version (1.5) into my project, renamed it, and made findScrollingChild public.
subclassed that copy, and set the sub-class as the behavior on my sheet.
Why sub-class and not just change the findScrollingChild method directly? Well, this way it's relatively easy to update the copy of BottomSheetBehavior when we update our material components.
My implementation of findScrollingChild() in the sub-class checks specifically for a ViewPager2.
If it is, it gets child 0 of the ViewPager2 (the horizontal RecyclerView), and then uses recycler?.layoutManager?.findViewByPosition(pager.currentItem) as the view to then search for the scrolling child.
If the view is not a ViewPager2, it uses the same algorithm from the original findScrollingChild()
This basically works. I have a pager with 2 tabs, one containing a ScollView that has nested scrolling enabled, and one containing a RecyclerView. The bottom sheet expands as its scrolled, and then the contents of the nested child scroll down properly once the sheet is open.
The problem is if after the sheet has expanded if the finger gets lifted, then any attempt to scroll up causes the bottom sheet to close rather then scrolling up -- no matter how far it's been scrolled down. At least this is the case for the RecyclerView in the second tab, I don't have enough content in the first tab at the moment that it actually needs to scroll.
The sheet gets closed with the list still scrolled down several pages (or wherever you stopped scrolling). If however you scroll down -- even just a little bit -- scrolling up works again! And you can swipe a couple times and it will work - until it doesn't, and the sheet moves to the half expanded state.
I'm not sure where this behavior is coming from or how to resolve it. It doesn't happen when the bottom sheet has a direct RecyclerView (no ViewPager2 I'm the way).
I tried disabling swiping in the ViewPager2 thinking it might be interfering with touch events, but to no avail.

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.

Android: RecyclerView, scroll along a path

I'm trying to make the items within a RecyclerView scroll along a path, specifically a curve. At the left and right edge of the screen the offset is 0, and in the middle of the screen the offset is 100% for example. I am attempting to achieve this by setting the Y translation of each view. Currently I am doing this in an OnScrollListener attached to the RecyclerView. This mostly works OK, but there are occasions where the OnScrollListener isn't invoked so the translation resets to 0. These occasions include when adding or removing an item from the RecyclerViews adapter.
Is there a better place to attempt to achieve this effect? I have also looked into using my own LayoutManager and overriding OnLayoutChildren, but again this doesn't work in all cases and isn't called when scrolling.

Animate RecyclerView height

Is it possible to animate RecyclerView height, or otherwise change it programmatically? For example, if I have a large header view that is sometimes visible, and other times not - I would want to animate the RecyclerView height to fill the screen when the header is animated out.
Changing LayoutParams.height does not seem to work. LinearLayout animateLayoutChanges causes a crash.
<LinearLayout>
<RelativeLayout (header)>
<RecyclerView>
</LinearLayout>
I want to make the RelativeLayout animate out the top (translationY) and then at the same time make the recyclerview animate to be taller to fit.
There are possible options to tackle this:
Follow suggestion from #Ari to start animation and on every animation tick update layout params. This will make an effect of recyclerview changing its size. However, this is a horrible idea from performance stand point. Layout and Measure process is quite expensive, so generally you want to minimize calls which trigger layout. Call to setLayoutParams will trigger layout & measure process for RecyclerView + all its children which means that on every single frame you will do really expensive work which most likely will lead to framedrop and bad user experience
There is another way though. It might not work in all cases - it all depends on your final layout, but still that's what I would recommend doing. The idea - is to make your recyclerView taller before you start animation. It requires some advanced Android skills though. Basically you need to override onMeasure & layout methods in your RecyclerView (you actually need to extend RecyclerView class to do that).
You can introduce some flag to your recyclerView to measure itself a bit taller than normal (how much taller - the exact height of your header view)
when you need to animate header out - set your flag to true and request new layout. This will re-layout recyclerView with some invisible part at the bottom.
Now you can just animate y translations of both RecyclerView & Header so header moves out of the screen and recyclerview goes higher. This will make user feel like recyclerview "expands"
Once animation is done - set your custom flag to false and change visibility of your header to GONE since it is off screen now
Here is some information about implementing custom onMeasure logic:
https://medium.com/android-news/perfmatters-introduction-to-custom-viewgroups-to-improve-performance-part-2-f14fbcd47c

Categories

Resources