PagingLibrary with recyclerView can not stop loadAfter() - android

loadAfter() can't stop even though I didn't swipe the RecyclerView.
It keeps calling onBindView->loadAfter()
I tried to debug, find these:
method loadAroundInternal in class ContiguousPagedList
int appendItems = getAppendItemsRequested(mConfig.prefetchDistance, index, mStorage.getLeadingNullCount() + mStorage.getStorageCount());
index keep ++ while getBindView in recyclerView is being bigger
the appendItem is always >0
I'm not sure, but I think maybe
network->appendPage->notifyInserted->bindView->loadAfter->network
then the recyclerView adapter execute onBindView.But the bindViewHolder which position should't call called. So it can't stop.
I don't know whether I'm right or not.And I don't know how to fix it.
below are some relevant code I wrote:
#Override
public void onBindViewHolder(#NonNull BaseViewHolder holder, int position) {
VB binding = DataBindingUtil.getBinding(holder.itemView);
setData(binding, getItem(position));
binding.executePendingBindings();
}
In my own PagingDataSource which extends PageKeyedDataSource
#Override
public void loadAfter(#NonNull LoadParams<Integer> params, #NonNull LoadCallback<Integer, T> callback) {
singleCreator.createSingle(params.key, params.requestedLoadSize)
.subscribe(new SingleObserver<Page<T>>() {
#Override
public void onSubscribe(Disposable d) {
}
#Override
public void onSuccess(Page<T> tPage) {
callback.onResult(tPage.list, tPage.pageNumber + 1);
}
#Override
public void onError(Throwable e) {
}
});
}
public interface SingleCreator<T> {
SingleSubscribeProxy<Page<T>> createSingle(int page, int pageSize);
}
If other things need, I will edit the question
=====================================
Add message:I found if I set an absolute value for recyclerView's height, such as 100dp, It works fine. Butwork with problem when wrap_content and match_parent.
So the problem now is:when 2 recyclerViews in nestedScrollView and with a wrap_content or match_parent, the bindView can't stop loadAfter() which is a method of paging library.

So the reason why It happened
When a network result backs, the recycleView refresh.
Since it's in a nestedScrollView, it has no exact height.
Then onSizeChanged->getItem->network.
For me, the solution is
write a custom linearLayout which implements NestedScrollingParent2;

Related

RecyclerView ViewHolder creation inside NestedScrollView

I have two problems caused by the same source.
I'm working on a social media app, I have a profile view containing a CollapsingToolbarLayout for the profile picture and RecyclerView inside a NestedScrollView for the posts feed.
The behaviour I didn't expect is that let's say I have 20 posts in the RecyclerView and the screen can only display 3, the recycler adapter creates 20 view holders and they are all considered as visible.
This causes two problems for me :
1 - The posts may contain videos and I want the video to be stopped if the post is not visible on the screen. I used to do this on my other RecyclerViews.
#Override
public void onViewDetachedFromWindow(#NonNull RecyclerView.ViewHolder holder) {
if (holder instanceof PostViewHolder) {
PostViewHolder postViewHolder = (PostViewHolder) holder;
pauseVideo(postViewHolder.videoPlayer);
}
}
This method is never called because the RecyclerView or adapter or whatever considers all the view holders to be visible on the screen. To make sure my assumption is correct I did a log on onViewAttachedToWindow and if the list contains 20 posts, it gets called 20 times when I add the list to the RecyclerView.
2 - I want posts to be loaded dynamically (load small batches on scroll). This was achieved using this method :
WrapContentLinearLayoutManager llManager = new WrapContentLinearLayoutManager(this, LinearLayoutManager.VERTICAL, false);
recyclerView.setLayoutManager(llManager);
recyclerView.addOnScrollListener(new RecyclerView.OnScrollListener() {
#Override
public void onScrolled(RecyclerView recyclerView, int dx, int dy) {
if(dy > 0) { //check for scroll down
if (llManager.findFirstVisibleItemPosition() + 10 > llManager.getItemCount() && !loadingMorePosts) {
loadingMorePosts = true;
dbListeners.getMoreUserPosts();
}
}
}
});
This doesn't work too because llManager.findFirstVisibleItemPosition() always returns 0.
Am I doing something wrong or is this the expected behaviour from a RecyclerView inside a nested ScrollView?
And is there a solution or a workaround for the second problem because loading all the posts at once is not acceptable.
And Thanks.
You have to handle pagination at scroll listener of nesterscroll view
nestedScrollView.setOnScrollChangeListener(new NestedScrollView.OnScrollChangeListener() {
#Override
public void onScrollChange(NestedScrollView view, int scrollX, int scrollY, int oldScrollX, int oldScrollY) {
if (scrollY == (view.getChildAt(0).getMeasuredHeight() - view.getMeasuredHeight())&& !loadingMorePosts) {
loadingMorePosts = true;
dbListeners.getMoreUserPosts();
}
}
});

Android RecyclerView with gridlayout smoothScrollTo() speed

I have a recyclerview with gridlayoutmanager.
If I run the code
recycler.smoothScrollTo(adapter.getItemCount())
the recycler scrolls really fast to the last element. I tried some solutions on Stackoverflow to make the scrolling slower, but all apply to Linearlayoutmanager not Gridlayoutmanager.
Any help?
I cannot say for sure what your problem is. But I am lucky enough to have a very simple GridLayoutManager recyclerview demo out there, very small sample project. I created a so branch and added a button that does the same you do.
Look it up: https://github.com/Gryzor/GridToShowAds/compare/so?expand=1
.setOnClickListener { mainRecyclerView.smoothScrollToPosition(data.size) }
And that alone just works.
Check the source code, it's a very simple sample for something unrelated, but happens to have a RV with a Grid Layout :)
UPDATE
What you actual want is to control the Speed at which the recyclerView scrolls. Ok.
It's not the RecyclerView that drives the scroll, it's actually the LayoutManager that does. How so?
If you look at RV's source code...
public void smoothScrollToPosition(int position) {
...
mLayout.smoothScrollToPosition(this, mState, position);
}
So it ends up calling mLayout. What is this?
#VisibleForTesting LayoutManager mLayout;
So, your LayoutManager#smoothScroll... method is used.
Decompiling now GridLayoutManager for science:
#Override
public void smoothScrollToPosition(RecyclerView recyclerView, RecyclerView.State state,
int position) {
LinearSmoothScroller linearSmoothScroller =
new LinearSmoothScroller(recyclerView.getContext());
linearSmoothScroller.setTargetPosition(position);
startSmoothScroll(linearSmoothScroller);
}
note: this method is actually in LinearLayoutManager because GridLayoutManager is a subclass and it doesn't override the method
A LinearSmoothScroller!; no parameter to specify the speed though...
Look at it:
public class LinearSmoothScroller extends RecyclerView.SmoothScroller {
private static final boolean DEBUG = false;
private static final float MILLISECONDS_PER_INCH = 25f;
private static final int TARGET_SEEK_SCROLL_DISTANCE_PX = 10000;
...
}
This class has a start() method described as:
* Starts a smooth scroll for the given target position.
So who calls this?
The mLayout.smoothScrollToPosition method does at the end in the startSmoothScroll(...) call.
public void startSmoothScroll(SmoothScroller smoothScroller) {
Starts a smooth scroll using the provided {#link SmoothScroller}.
mSmoothScroller.start(mRecyclerView, this);
So... in lieu of all this, the answer to your question is:
You need to create your extension of GridLayoutManager by subclassing it, and in it, override the smoothScrollToPosition method, to provide your own Scroller logic.
Thread carefully though, LayoutManagers are not the "simplest" classes of all time and they can be quite complicated to master.
Good luck! :)
My simple working solution currently is still implementing a timer then working with it.
final CountDownTimer scrollUp_timer = new CountDownTimer(50000, 30) {
#Override
public void onTick(long millisUntilFinished) {
if (layoutManager != null && layoutManager.findFirstVisibleItemPosition() != 0) searchRecyclerView.smoothScrollToPosition(layoutManager.findFirstVisibleItemPosition()-1);
}
#Override
public void onFinish() {
try{
}catch(Exception e){
// log
}
}
};
scrollUp.setOnDragListener(new View.OnDragListener() {
#Override
public boolean onDrag(View view, DragEvent dragEvent) {
layoutManager = ((GridLayoutManager)searchRecyclerView.getLayoutManager());
int action = dragEvent.getAction();
if (action == DragEvent.ACTION_DRAG_ENTERED) {
scrollUp_timer.start();
} else if (action == DragEvent.ACTION_DRAG_EXITED) {
searchRecyclerView.scrollBy(0,0);
scrollUp_timer.cancel();
}
return true;
}
});
You can extend:
class CSCustomRecyclerSmoothScroller(context: Context, speed: Float = 0.2f)
: LinearSmoothScroller(context) {
override fun calculateSpeedPerPixel(displayMetrics: DisplayMetrics): Float = speed
}
And use it like:
val shortAnimationDuration =
view.resources.getInteger(android.R.integer.config_shortAnimTime)
val scroller = CSCustomRecyclerSmoothScroller(this, speed = 0.15)
scroller.targetPosition = position
view.postDelayed({
layoutManager.startSmoothScroll(scroller)
}, shortAnimationDuration.toLong())
postDelayed can be necessary in some cases but maybe not in all.
I use similar code with GridLayoutManager I just tried to extract relevant parts from my way of writing things.

getHeight returns 0 inside onPreDrawListenter

I have a layout that has several cardView, that have recyclerViews in them
in the beginning the first recyclerview is empty, so the card doesn't show.
later on, the recyclerview gets populated, so I want it to have a nice animation to it.
I'm trying to move the other cards down, and then fade in the card.
to do that I set the database of the recyclerView, and then I attach a OnPreDrawListener to the cardview around it, so I can get the height of the view, then I set the view to GONE and run a transationY animation on the card below it.
Thing is that when I call getMeasuredHeight I get 0.
It's almost as the notifyDatasetChanged() only happens in the next frame, so the view didn't get it's new height yet.
here is my code:
private void runSearchAnimation(List<Route> searchResult) {
if(rvSearchResults.getMeasuredHeight() == 0) {
Log.i(TAG, "height is 0");
resultsAdapter.setDatabase(searchResult);
cvSearchResults.getViewTreeObserver().addOnPreDrawListener(new ViewTreeObserver.OnPreDrawListener() {
#Override
public boolean onPreDraw() {
cvSearchResults.getViewTreeObserver().removeOnPreDrawListener(this);
Log.i(TAG, "cvSearchResults.getMeasuredHeight() = " + cvSearchResults.getMeasuredHeight());
cvSearchResults.setVisibility(View.GONE);
ObjectAnimator contentAnim = ObjectAnimator.ofFloat(cvRecentSearches,
"translationY", cvRecentSearches.getTranslationY(), cvSearchResults.getMeasuredHeight());
contentAnim.addListener(new AnimatorListenerAdapter() {
#Override
public void onAnimationStart(Animator animation) {
Log.i(TAG, "animation started");
}
#Override
public void onAnimationEnd(Animator animation) {
Log.i(TAG, "animation ended");
cvSearchResults.setVisibility(View.VISIBLE);
}
});
contentAnim.setDuration(300);
contentAnim.start();
return true;
}
});
}
}
Does anyone have an idea on how to solve this?
I believe that RecyclerView supports such animations via RecyclerView.ItemAnimator.
See the docs.
so after some thinking I came up with this easy solution:
cvSearchResults.getViewTreeObserver().addOnPreDrawListener(new ViewTreeObserver.OnPreDrawListener() {
#Override
public boolean onPreDraw() {
if(cvSearchResults.getMeasuredHeight() != 0) {
cvSearchResults.getViewTreeObserver().removeOnPreDrawListener(this);
Since I will keep getting my onPreDraw method called until I will remove the listener, I remove it only when I know the view has been measured with the recyclerview data I gave it

Overdraw-optimize RecyclerView layouts

So I have a RecyclerView that has multiple view types that all have a different rendering background-wise. Naturally I want to avoid overdraw for all these components, so I give my RecyclerView and all views up in the hierarchy no background at all.
This works fine as is - until I start animating items in and out. The DefaultItemAnimator of course nicely blends items in and out and therefor opens a "hole" in the RecyclerView where the background of it shortly becomes visible.
Ok, I thought, lets try something - let's give the RecyclerView only a background when animations are actually running, but otherwise remove the background, so scrolling works smoothly at high FPS rates. However, this is actually harder than I originally thought, since there is no specific "animations will start" and corresponding "animations will end" signal in RecyclerView nor the ItemAnimator or related classes.
What I recently tried was to combine an AdapterDataObserver with an ItemAnimatorFinishedListener like this, but without success:
RecyclerView.ItemAnimator.ItemAnimatorFinishedListener finishListener =
new RecyclerView.ItemAnimator.ItemAnimatorFinishedListener() {
#Override
public void onAnimationsFinished() {
recycler.setBackgroundResource(0);
}
};
recycler.getAdapter().registerAdapterDataObserver(
new RecyclerView.AdapterDataObserver() {
#Override
public void onItemRangeInserted(int positionStart, int itemCount) {
start();
}
#Override
public void onItemRangeRemoved(int positionStart, int itemCount) {
start();
}
#Override
public void onItemRangeMoved(int fromPosition, int toPosition, int itemCount) {
start();
}
private void start() {
recycler.setBackgroundResource(R.color.white);
if (!recycler.getItemAnimator().isRunning()) {
return;
}
recycler.getItemAnimator().isRunning(finishListener);
}
}
);
The issue here is that the adapter's range callbacks are ran way earlier than the actual animations run, because the animations will not be scheduled before the next requestLayout() happens internally in the RecyclerView, i.e. recycler.getItemAnimator().isRunning() in my start() method always returns false, so the white background is never removed.
So before I start experimenting with an additional ViewTreeObserver.OnGlobalLayoutListener and bring that into the mix - has anybody found a proper, working (easier?!) solution to this problem?
Ok, I went further down the road and included a ViewTreeObserver.OnGlobalLayoutListener - this seems to be working:
/**
* This is a utility class that monitors a {#link RecyclerView} for changes and temporarily
* gives the view a background so we do not see any artifacts while items are animated in or
* out of the view, and, at the same time prevent the overdraw that would occur when we'd
* give the {#link RecyclerView} a permanent opaque background color.
* <p>
* Created by Thomas Keller <me#thomaskeller.biz> on 12.05.16.
*/
public class RecyclerBackgroundSaver {
private RecyclerView mRecyclerView;
#ColorRes
private int mBackgroundColor;
private boolean mAdapterChanged = false;
private ViewTreeObserver.OnGlobalLayoutListener mGlobalLayoutListener
= new ViewTreeObserver.OnGlobalLayoutListener() {
#Override
public void onGlobalLayout() {
// ignore layout changes until something actually changed in the adapter
if (!mAdapterChanged) {
return;
}
mRecyclerView.setBackgroundResource(mBackgroundColor);
// if no animation is running (which should actually only be the case if
// we change the adapter without animating anything, like complete dataset changes),
// do not do anything either
if (!mRecyclerView.getItemAnimator().isRunning()) {
return;
}
// remove this view tree observer, i.e. do not react on further layout changes for
// one and the same dataset change and give control to the ItemAnimatorFinishedListener
mRecyclerView.getViewTreeObserver().removeOnGlobalLayoutListener(this);
mRecyclerView.getItemAnimator().isRunning(finishListener);
}
};
RecyclerView.ItemAnimator.ItemAnimatorFinishedListener finishListener
= new RecyclerView.ItemAnimator.ItemAnimatorFinishedListener() {
#Override
public void onAnimationsFinished() {
// the animation ended, reset the adapter changed flag so the next change kicks off
// the cycle again and add the layout change listener back
mRecyclerView.setBackgroundResource(0);
mAdapterChanged = false;
mRecyclerView.getViewTreeObserver().addOnGlobalLayoutListener(mGlobalLayoutListener);
}
};
RecyclerView.AdapterDataObserver mAdapterDataObserver = new RecyclerView.AdapterDataObserver() {
#Override
public void onItemRangeInserted(int positionStart, int itemCount) {
mAdapterChanged = true;
}
#Override
public void onItemRangeRemoved(int positionStart, int itemCount) {
mAdapterChanged = true;
}
#Override
public void onItemRangeMoved(int fromPosition, int toPosition, int itemCount) {
mAdapterChanged = true;
}
};
public RecyclerBackgroundSaver(RecyclerView recyclerView, #ColorRes int backgroundColor) {
mRecyclerView = recyclerView;
mBackgroundColor = backgroundColor;
}
/**
* Enables the background saver, i.e for the next item change, the RecyclerView's background
* will be temporarily set to the configured background color.
*/
public void enable() {
checkNotNull(mRecyclerView.getAdapter(), "RecyclerView has no adapter set, yet");
mRecyclerView.getAdapter().registerAdapterDataObserver(mAdapterDataObserver);
mRecyclerView.getViewTreeObserver().addOnGlobalLayoutListener(mGlobalLayoutListener);
}
/**
* Disables the background saver, i.e. for the next animation,
* the RecyclerView's parent background will again shine through.
*/
public void disable() {
mRecyclerView.getViewTreeObserver().removeOnGlobalLayoutListener(mGlobalLayoutListener);
if (mRecyclerView.getAdapter() != null) {
mRecyclerView.getAdapter().unregisterAdapterDataObserver(mAdapterDataObserver);
}
}
}

(Smooth)ScrollToPosition doesn't work properly with RecyclerView

I'm using basic RecyclerView with GridLayoutManager. I observed that nor smoothScrollToPosition nor scrollToPosition works properly.
a) when using smoothScrollToPosition I often receive error from RecyclerView
"RecyclerView﹕ Passed over target position while smooth scrolling."
and RecyclerView is not scrolled properly (often it misses the targeted row). This is observed mostly when I'm trying to scroll to the 1st item of some row
b) when using scrollToPosition it seems to work quite ok but most of the time I can see only the 1st item of the row and the rest are not displayed.
Can you give me some hints how to make work properly at least one of the methods?
Thanks a lot!
Finally I was able to make it work! LinearLayoutManager.scrollToPositionWithOffset(int, int) did the trick.
I also have same issue, but managed to fix the issue by Customizing SmoothScroller
let Custom LayoutManager as below
public class CustomLayoutManager extends LinearLayoutManager {
private static final float MILLISECONDS_PER_INCH = 50f;
private Context mContext;
public CustomLayoutManager(Context context) {
super(context);
mContext = context;
}
#Override
public void smoothScrollToPosition(RecyclerView recyclerView,
RecyclerView.State state, final int position) {
LinearSmoothScroller smoothScroller =
new LinearSmoothScroller(mContext) {
//This controls the direction in which smoothScroll looks
//for your view
#Override
public PointF computeScrollVectorForPosition
(int targetPosition) {
return CustomLayoutManager.this
.computeScrollVectorForPosition(targetPosition);
}
//This returns the milliseconds it takes to
//scroll one pixel.
#Override
protected float calculateSpeedPerPixel
(DisplayMetrics displayMetrics) {
return MILLISECONDS_PER_INCH/displayMetrics.densityDpi;
}
};
smoothScroller.setTargetPosition(position);
startSmoothScroll(smoothScroller);
}
}
(documentation commented inside the code given above)Please set the above LayoutManager
to the recyerview
CustomLayoutManagerlayoutManager = new CustomLayoutManager(getActivity());
recyclerView.setLayoutManager(layoutManager);
recyclerView.smoothScrollToPosition(position);
by using the custom Layout manager
scrollToPosition
also working well in my case u can use
recyclerView.scrollToPosition(position)
also if you want to adjust the speed of smoothScrollToPosition please override the
private static final float MILLISECONDS_PER_INCH = 50f;
in CustomLayoutManager.
So if we put the value as 1f the smoothScrollToPosition will be faster like scrollToPosition.increasing value make delay and decreasing will make the speed of scroll.
Hope this will useful.
In My case,
mRecyclerView.scrollToPosition(10);
also did not work.
But
mRecyclerView.smoothScrollToPosition(10);
works fine for me...
To scroll down smoothly to bottom from any position in the RecyclerView on clicking EditText.
edittext.setOnClickListener(new View.OnClickListener() {
#Override
public void onClick(View view) {
rv_commentList.postDelayed(new Runnable() {
#Override
public void run() {
rv_commentList.scrollToPosition(rv_commentList.getAdapter().getItemCount() - 1);
}
}, 1000);
}
});
Another reason why any of the before mentioned solutions may not work is if your RecyclerView is embedded in a NestedScrollView. In this case you have to call the scroll action on the NestedScrollView.
for example:
nestedScrollview.smoothScrollTo(0,0)
This extension is so useful, try please.
fun RecyclerView.smoothSnapToPosition(position: Int, snapMode: Int = LinearSmoothScroller.SNAP_TO_START) {
val smoothScroller = object : LinearSmoothScroller(this.context) {
override fun getVerticalSnapPreference(): Int = snapMode
override fun getHorizontalSnapPreference(): Int = snapMode
}
smoothScroller.targetPosition = position
layoutManager?.startSmoothScroll(smoothScroller)
}
I was facing a weird issue wherein smoothScrollToPosition only worked occasionally.
After putting the smoothScrollToPosition inside Handler Post
Delayed with 1 second delay, it worked fine.
Refer to the following Kotlin example:
Handler().postDelayed({
recyclerViewObject.smoothScrollToPosition(0) // mention the position in place of 0
}, 1000) // 1000 indicates the 1 second delay.
recyclerView.getLayoutManager().smoothScrollToPosition(recyclerView,new RecyclerView.State(),currentPosition);
Try measuring item width or height and call smoothScrollBy(int dx, int dy).
How to perform smooth scrolling and save RecyclerView vertical position after device rotating:
This is the method that works for my case,
public class MainFragment extends Fragment { //OR activity it's //fragment in my case
....
#Override
public void onLoadFinished(#NonNull Loader<List<Report>> loader, List<Report> objects) { // or other method of your choice, in my case it's a Loader
RecyclerView recyclerViewRv = findViewById(........;
.....
recyclerViewRv.setAdapter(.....Your adapter);
recyclerViewRv.addOnScrollListener(new RecyclerView.OnScrollListener() {
#Override
public void onScrollStateChanged(#NonNull RecyclerView recyclerView, int newState) {
super.onScrollStateChanged(recyclerView, newState);
}
#Override
public void onScrolled(#NonNull RecyclerView recyclerView, int dx, int dy) {
super.onScrolled(recyclerView, dx, dy);
recyclerScrollY = recyclerViewRv. computeVerticalScrollOffset();
}
});
//Apply smooth vertical scroll
recyclerViewRv.smoothScrollBy(0,recyclerScrollY);
}
//Save vertical scroll position before rotating devices
#Override
public void onSaveInstanceState(#NonNull Bundle outState) {
super.onSaveInstanceState(outState);
outState.putInt("recyclerScrollY",recyclerScrollY);
}
//BackUp vertical scroll position after rotating devices
#Override
public void onCreate(#Nullable Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
if(savedInstanceState != null) {
recyclerScrollY = savedInstanceState.getInt("recyclerScrollY");
}
}
//If you want to perform the same operation for horizontal scrolling just add a variable called recyclerScrollX = recyclerScrollY = recyclerViewRv. computeHorizontalScrollOffset(); then save in bundle
Calling the recyclerView smoothScroll isn't effective, as the recyclerView itself doesn't handle its layout.
What you should do is calling the layout manager scroll method instead.
This should look something like this
mRecyclerView.getLayoutManager().scrollToPosition(desiredPosition);
If you are trying to do a quick scroll to a position at the top of the RecyclerView, just use LinearLayoutManager.scrollToPositionWithOffset with 0 as the offset.
Example:
mLinearLayoutManager = new LinearLayoutManager(this);
recyclerView.setLayoutManager(mLinearLayoutManager);
mLinearLayoutManager.scrollToPositionWithOffset(myPosition, 0);
smoothScrollToPosition is very slow. If you want something fast go with scrollToPositionWithOffset.
when you use scrollToPosition it will show it on top of the recycler view.
But if you use smoothScrollToPosition it will scroll till it come in to Window Visible. that's why while smoothScrool to item below, it will show it on bottom
Actually, if you have a RecyclerView inside a NestedScrollView, you must use both of these lines every time you want to go to the beginning of the RecyclerView:
nestedScrollView.smoothScrollTo(0, 0);
layoutManager.scrollToPositionWithOffset(0, 0);
This completely works for me.
this worked for me
Handler().postDelayed({
(recyclerView.getLayoutManager() as LinearLayoutManager).scrollToPositionWithOffset( 0, 0)
}, 100)
None of these answers worked for me. I needed to smoothScrollToPosition but #Ramz answer didn't work. I was finding it would consistently overscroll but only in one direction. I discovered that it seemed to be the item decorators throwing it off. I had a horizontal layout and I wanted to add a space after every item except the last and it didn't like that asymmetry. As soon as I included a space after every item, it worked!
nestedScroll.smoothScrollTo(0, recycler.top)
So i was looking for a solution to get back to the top with a recyclerview inside another layout that has a view on top of it (in my case I had a LinearLayout with a TextView and my recyclerview inside). Because of that the smoothscroll would go only to half the way to the first item.
Here's what I did which works really well (Kotlin solution):
back_to_top.setOnClickListener {
GlobalScope.launch(Dispatchers.Main) {
GlobalScope.launch(Dispatchers.Main) {
recyclerview.layoutManager?.smoothScrollToPosition(recyclerview, RecyclerView.State(), 0)
delay((recyclerview.layoutManager as LinearLayoutManager).findLastVisibleItemPosition() * 100L)
}.join()
recyclerview.scrollToPosition(0)
}
back_to_top.visibility = View.GONE
}
}
Here what I do is I smoothscroll to the first element and delay the scroll by 100ms times the last item visible and then call the scrollToPosition(0) (which goes to the top.correctly)

Categories

Resources