When I used smoothScrollToPosition with RecyclerView, it stopped only at a halfway position, so it is a memo when I checked it.
Use LinearLayoutManager The scroll direction is vertical
I want to make sure that the scroll stop position is at the top.
Depending on the scrolling direction, it may go up or down ...
smoothScrollToPosition The implementation in the commonly used LinearLayoutManager looks like this.
LinearLayoutManager.java
@Override
public void smoothScrollToPosition(RecyclerView recyclerView, RecyclerView.State state, int position) {
LinearSmoothScroller linearSmoothScroller = new LinearSmoothScroller(recyclerView.getContext());
//Set scroll targetPosition in scroller
linearSmoothScroller.setTargetPosition(position);
//Scroll using scroller
startSmoothScroll(linearSmoothScroller);
}
LinearSmoothScroller
inherits from SmoothScroller
.
SmoothScroller
SmoothScroller
is an abstract class with a method called ʻonTargetFound` in it.
SmoothScroller.java
public abstract static class SmoothScroller {
...(Abbreviation)
/**
* Called when the target position is laid out. This is the last callback SmoothScroller
* will receive and it should update the provided {@link Action} to define the scroll
* details towards the target view.
* @param targetView The view element which render the target position.
* @param state Transient state of RecyclerView
* @param action Action instance that you should update to define final scroll action
* towards the targetView
*/
protected abstract void onTargetFound(View targetView, State state, Action action);
...(Abbreviation)
}
SmoothScroller
has some callbacks, but this callback is the last one to be notified and will be called when the target View comes into the layout. As for what to do here, there is a comment to the effect that update the ʻactionpassed as an argument and fine-tune the position. Now, let's check what kind of implementation is specifically in
LinearSmoothScroller`.
LinearSmoothScroller
LinearSmoothScroller.java
@Override
protected void onTargetFound(View targetView, RecyclerView.State state, Action action) {
final int dx = calculateDxToMakeVisible(targetView, getHorizontalSnapPreference());
final int dy = calculateDyToMakeVisible(targetView, getVerticalSnapPreference());
final int distance = (int) Math.sqrt(dx * dx + dy * dy);
final int time = calculateTimeForDeceleration(distance);
if (time > 0) {
action.update(-dx, -dy, time, mDecelerateInterpolator);
}
}
It calculates the required scroll amount in the x and y directions and executes ʻaction.update. The content of the implementation of
calculateDx / DyToMakeVisible` to be calculated is muddy, so if you look only at the comments, it looks like this.
LinearSmoothScroller.java
/**
* Calculates the vertical scroll amount necessary to make the given view fully visible
* inside the RecyclerView.
*
* @param view The view which we want to make fully visible
* @param snapPreference The edge which the view should snap to when entering the visible
* area. One of {@link #SNAP_TO_START}, {@link #SNAP_TO_END} or
* {@link #SNAP_TO_ANY}.
* @return The vertical scroll amount necessary to make the view visible with the given
* snap preference.
*/
public int calculateDyToMakeVisible(View view, int snapPreference) {
It says that it will calculate the amount of scrolling required before the entire view of the first argument can be seen. The second argument indicates the position where you want the view to be finally displayed, and select from the following three.
Looking at the getVerticalSnapPreference
that determines the second argument, it looks like this.
LinearSmoothScroller.java
protected int getVerticalSnapPreference() {
return mTargetVector == null || mTargetVector.y == 0 ? SNAP_TO_ANY :
mTargetVector.y > 0 ? SNAP_TO_END : SNAP_TO_START;
}
It switches between SNAP_TO_START
and SNAP_TO_END
depending on the direction of sucrose. In other words, for this purpose, it can be achieved by modifying (override) this snapPreference.
The description has become long, but I've found that the code that should actually be implemented needs only a slight extension of the default LinearSmoothScroller
.
CustomLayoutManager.kt
class CustomLayoutManager(context: Context) : LinearLayoutManager(context) {
override fun smoothScrollToPosition(recyclerView: RecyclerView, state: RecyclerView.State?, position: Int) {
val linearSmoothScroller = object : LinearSmoothScroller(recyclerView.context) {
override fun getVerticalSnapPreference(): Int = if (reverseLayout) SNAP_TO_END else SNAP_TO_START
}
linearSmoothScroller.targetPosition = position
startSmoothScroll(linearSmoothScroller)
}
}
By the way, in this example, only reverseLayout
is considered, but I think that it is general-purpose to implement it considering the scroll direction (vertical / horizontal) as well.
Recommended Posts