Ich versuche, die neue RecyclerView
Klasse für ein Szenario zu verwenden, in dem die Komponente beim Scrollen an einem bestimmten Element ausgerichtet werden soll (das alte Android ist Gallery
ein Beispiel für eine solche Liste mit einem zentrierten Element).
Dies ist der Ansatz, den ich bisher verfolge:
Ich habe eine Schnittstelle, ISnappyLayoutManager
die eine Methode enthält getPositionForVelocity
, die berechnet, an welcher Position die Ansicht das Scrollen beenden soll, wenn die anfängliche Schleudergeschwindigkeit gegeben ist.
public interface ISnappyLayoutManager {
int getPositionForVelocity(int velocityX, int velocityY);
}
Dann habe ich eine Klasse, SnappyRecyclerView
die RecyclerView
ihre fling () -Methode so unterordnet und überschreibt, dass die Ansicht genau um den richtigen Betrag verschoben wird:
public final class SnappyRecyclerView extends RecyclerView {
/** other methods deleted **/
@Override
public boolean fling(int velocityX, int velocityY) {
LayoutManager lm = getLayoutManager();
if (lm instanceof ISnappyLayoutManager) {
super.smoothScrollToPosition(((ISnappyLayoutManager) getLayoutManager())
.getPositionForVelocity(velocityX, velocityY));
}
return true;
}
}
Ich bin aus mehreren Gründen mit diesem Ansatz nicht sehr zufrieden. Zunächst scheint es der Philosophie von 'RecyclerView' zu widersprechen, es in Unterklassen unterteilen zu müssen, um eine bestimmte Art des Bildlaufs zu implementieren. Zweitens, wenn ich nur die Standardeinstellung verwenden möchte LinearLayoutManager
, wird dies etwas komplex, da ich mit den Interna herumspielen muss, um den aktuellen Bildlaufstatus zu verstehen und genau zu berechnen, wohin dieser Bildlauf führt. Schließlich werden nicht einmal alle möglichen Bildlaufszenarien berücksichtigt. Wenn Sie die Liste verschieben und dann pausieren und dann einen Finger heben, tritt kein Schleuderereignis auf (die Geschwindigkeit ist zu niedrig), sodass die Liste auf halber Strecke bleibt Position. Dies kann möglicherweise durch Hinzufügen eines On-Scroll-Status-Listeners zum RecyclerView
behoben werden, aber das fühlt sich auch sehr hackig an.
Ich habe das Gefühl, dass mir etwas fehlen muss. Gibt es einen besseren Weg, dies zu tun?
quelle
Am Ende habe ich mir etwas anderes ausgedacht als oben. Es ist nicht ideal, aber es funktioniert akzeptabel gut für mich und kann für jemand anderen hilfreich sein. Ich werde diese Antwort nicht akzeptieren, in der Hoffnung, dass jemand anderes etwas Besseres und weniger Hackiges mitbringt (und es ist möglich, dass ich die
RecyclerView
Implementierung falsch verstehe und eine einfache Methode dazu vermisse, aber in der Zwischenzeit ist dies gut genug für die Regierungsarbeit!)Die Grundlagen der Implementierung sind folgende: Das Scrollen in a
RecyclerView
ist eine Art Aufteilung zwischen demRecyclerView
und demLinearLayoutManager
. Es gibt zwei Fälle, die ich behandeln muss:RecyclerView
Fling an einen internen übergeben wird,Scroller
der dann die Bildlaufmagie ausführt. Dies ist problematisch, da sich die dannRecyclerView
normalerweise in einer nicht eingerasteten Position niederlassen. Ich löse diesesRecyclerView
fling()
Problem, indem ich die Implementierung überschreibe und anstatt zu schleudern, dieLinearLayoutManager
Position glatt streiche .onTouchEvent
Methode überschreibe .Die
SnappyRecyclerView
:public final class SnappyRecyclerView extends RecyclerView { public SnappyRecyclerView(Context context) { super(context); } public SnappyRecyclerView(Context context, AttributeSet attrs) { super(context, attrs); } public SnappyRecyclerView(Context context, AttributeSet attrs, int defStyle) { super(context, attrs, defStyle); } @Override public boolean fling(int velocityX, int velocityY) { final LayoutManager lm = getLayoutManager(); if (lm instanceof ISnappyLayoutManager) { super.smoothScrollToPosition(((ISnappyLayoutManager) getLayoutManager()) .getPositionForVelocity(velocityX, velocityY)); return true; } return super.fling(velocityX, velocityY); } @Override public boolean onTouchEvent(MotionEvent e) { // We want the parent to handle all touch events--there's a lot going on there, // and there is no reason to overwrite that functionality--bad things will happen. final boolean ret = super.onTouchEvent(e); final LayoutManager lm = getLayoutManager(); if (lm instanceof ISnappyLayoutManager && (e.getAction() == MotionEvent.ACTION_UP || e.getAction() == MotionEvent.ACTION_CANCEL) && getScrollState() == SCROLL_STATE_IDLE) { // The layout manager is a SnappyLayoutManager, which means that the // children should be snapped to a grid at the end of a drag or // fling. The motion event is either a user lifting their finger or // the cancellation of a motion events, so this is the time to take // over the scrolling to perform our own functionality. // Finally, the scroll state is idle--meaning that the resultant // velocity after the user's gesture was below the threshold, and // no fling was performed, so the view may be in an unaligned state // and will not be flung to a proper state. smoothScrollToPosition(((ISnappyLayoutManager) lm).getFixScrollPos()); } return ret; } }
Eine Schnittstelle für bissige Layout-Manager:
/** * An interface that LayoutManagers that should snap to grid should implement. */ public interface ISnappyLayoutManager { /** * @param velocityX * @param velocityY * @return the resultant position from a fling of the given velocity. */ int getPositionForVelocity(int velocityX, int velocityY); /** * @return the position this list must scroll to to fix a state where the * views are not snapped to grid. */ int getFixScrollPos(); }
Und hier ist ein Beispiel für eine
LayoutManager
Unterklasse, dieLinearLayoutManager
zu einemLayoutManager
reibungslosen Bildlauf führt:public class SnappyLinearLayoutManager extends LinearLayoutManager implements ISnappyLayoutManager { // These variables are from android.widget.Scroller, which is used, via ScrollerCompat, by // Recycler View. The scrolling distance calculation logic originates from the same place. Want // to use their variables so as to approximate the look of normal Android scrolling. // Find the Scroller fling implementation in android.widget.Scroller.fling(). private static final float INFLEXION = 0.35f; // Tension lines cross at (INFLEXION, 1) private static float DECELERATION_RATE = (float) (Math.log(0.78) / Math.log(0.9)); private static double FRICTION = 0.84; private double deceleration; public SnappyLinearLayoutManager(Context context) { super(context); calculateDeceleration(context); } public SnappyLinearLayoutManager(Context context, int orientation, boolean reverseLayout) { super(context, orientation, reverseLayout); calculateDeceleration(context); } private void calculateDeceleration(Context context) { deceleration = SensorManager.GRAVITY_EARTH // g (m/s^2) * 39.3700787 // inches per meter // pixels per inch. 160 is the "default" dpi, i.e. one dip is one pixel on a 160 dpi // screen * context.getResources().getDisplayMetrics().density * 160.0f * FRICTION; } @Override public int getPositionForVelocity(int velocityX, int velocityY) { if (getChildCount() == 0) { return 0; } if (getOrientation() == HORIZONTAL) { return calcPosForVelocity(velocityX, getChildAt(0).getLeft(), getChildAt(0).getWidth(), getPosition(getChildAt(0))); } else { return calcPosForVelocity(velocityY, getChildAt(0).getTop(), getChildAt(0).getHeight(), getPosition(getChildAt(0))); } } private int calcPosForVelocity(int velocity, int scrollPos, int childSize, int currPos) { final double dist = getSplineFlingDistance(velocity); final double tempScroll = scrollPos + (velocity > 0 ? dist : -dist); if (velocity < 0) { // Not sure if I need to lower bound this here. return (int) Math.max(currPos + tempScroll / childSize, 0); } else { return (int) (currPos + (tempScroll / childSize) + 1); } } @Override public void smoothScrollToPosition(RecyclerView recyclerView, State state, int position) { final LinearSmoothScroller linearSmoothScroller = new LinearSmoothScroller(recyclerView.getContext()) { // I want a behavior where the scrolling always snaps to the beginning of // the list. Snapping to end is also trivial given the default implementation. // If you need a different behavior, you may need to override more // of the LinearSmoothScrolling methods. protected int getHorizontalSnapPreference() { return SNAP_TO_START; } protected int getVerticalSnapPreference() { return SNAP_TO_START; } @Override public PointF computeScrollVectorForPosition(int targetPosition) { return SnappyLinearLayoutManager.this .computeScrollVectorForPosition(targetPosition); } }; linearSmoothScroller.setTargetPosition(position); startSmoothScroll(linearSmoothScroller); } private double getSplineFlingDistance(double velocity) { final double l = getSplineDeceleration(velocity); final double decelMinusOne = DECELERATION_RATE - 1.0; return ViewConfiguration.getScrollFriction() * deceleration * Math.exp(DECELERATION_RATE / decelMinusOne * l); } private double getSplineDeceleration(double velocity) { return Math.log(INFLEXION * Math.abs(velocity) / (ViewConfiguration.getScrollFriction() * deceleration)); } /** * This implementation obviously doesn't take into account the direction of the * that preceded it, but there is no easy way to get that information without more * hacking than I was willing to put into it. */ @Override public int getFixScrollPos() { if (this.getChildCount() == 0) { return 0; } final View child = getChildAt(0); final int childPos = getPosition(child); if (getOrientation() == HORIZONTAL && Math.abs(child.getLeft()) > child.getMeasuredWidth() / 2) { // Scrolled first view more than halfway offscreen return childPos + 1; } else if (getOrientation() == VERTICAL && Math.abs(child.getTop()) > child.getMeasuredWidth() / 2) { // Scrolled first view more than halfway offscreen return childPos + 1; } return childPos; } }
quelle
Constant.INCHES_PER_METER
nicht existiert, also habe ich es39.3700787
mir selbst gesetzt.+ 1
s durch ersetzt wird+ getSpanCount()
. Die Berechnung für dynamische Rasterlayouts mit unterschiedlichen Zellengrößen erfordert etwas mehr Arbeit. Danke @Catherine!return (int) Math.max(currPos + tempScroll / childSize, 0);
toreturn (int) Math.max(currPos + tempScroll / childSize + 2 , 0);
Ich habe es geschafft, einen saubereren Weg zu finden, dies zu tun. @Catherine (OP) lass es mich wissen, wenn dies verbessert werden kann oder du denkst, dass es eine Verbesserung gegenüber deiner ist :)
Hier ist der Scroll-Listener, den ich benutze.
https://github.com/humblerookie/centerlockrecyclerview/
Ich habe hier einige kleinere Annahmen wie z.
1) Anfangs- und Endauffüllung : Für das erste und letzte Element in der horizontalen Bildlaufleiste müssen die Anfangs- und Endauffüllung so eingestellt sein, dass die Anfangs- und Endansicht in der Mitte liegen, wenn zum ersten bzw. letzten Bildlauf gescrollt wird. Zum Beispiel im onBindViewHolder können Sie dies tun etwas wie das.
@Override public void onBindViewHolder(ReviewHolder holder, int position) { holder.container.setPadding(0,0,0,0);//Resetpadding if(position==0){ //Only one element if(mData.size()==1){ holder.container.setPadding(totalpaddinginit/2,0,totalpaddinginit/2,0); } else{ //>1 elements assign only initpadding holder.container.setPadding(totalpaddinginit,0,0,0); } } else if(position==mData.size()-1){ holder.container.setPadding(0,0,totalpaddingfinal,0); } } public class ReviewHolder extends RecyclerView.ViewHolder { protected TextView tvName; View container; public ReviewHolder(View itemView) { super(itemView); container=itemView; tvName= (TextView) itemView.findViewById(R.id.text); } }
Die Logik ist ziemlich allgemein und man kann sie für viele andere Fälle verwenden. In meinem Fall ist die Recycler-Ansicht horizontal und erstreckt sich über die gesamte horizontale Breite ohne Ränder (im Grunde ist die mittlere X-Koordinate der Recyclerview die Mitte des Bildschirms) oder ungleichmäßige Polsterungen.
Falls jemand vor einem Problem steht, kommentieren Sie dies bitte.
quelle
Ich brauchte auch eine bissige Recycler-Ansicht. Ich möchte, dass das Element "Recycler-Ansicht" links von einer Spalte einrastet. Am Ende wurde ein SnapScrollListener implementiert, den ich in der Recycler-Ansicht festgelegt habe. Das ist mein Code:
SnapScrollListener:
class SnapScrollListener extends RecyclerView.OnScrollListener { @Override public void onScrollStateChanged(RecyclerView recyclerView, int newState) { if (RecyclerView.SCROLL_STATE_IDLE == newState) { final int scrollDistance = getScrollDistanceOfColumnClosestToLeft(mRecyclerView); if (scrollDistance != 0) { mRecyclerView.smoothScrollBy(scrollDistance, 0); } } } }
Berechnung des Schnappschusses:
private int getScrollDistanceOfColumnClosestToLeft(final RecyclerView recyclerView) { final LinearLayoutManager manager = (LinearLayoutManager) recyclerView.getLayoutManager(); final RecyclerView.ViewHolder firstVisibleColumnViewHolder = recyclerView.findViewHolderForAdapterPosition(manager.findFirstVisibleItemPosition()); if (firstVisibleColumnViewHolder == null) { return 0; } final int columnWidth = firstVisibleColumnViewHolder.itemView.getMeasuredWidth(); final int left = firstVisibleColumnViewHolder.itemView.getLeft(); final int absoluteLeft = Math.abs(left); return absoluteLeft <= (columnWidth / 2) ? left : columnWidth - absoluteLeft; }
Wenn die erste sichtbare Ansicht mehr als die halbe Breite des Bildschirms entfernt ist, wird die nächste sichtbare Spalte nach links ausgerichtet.
Hörer einstellen:
mRecyclerView.addOnScrollListener(new SnapScrollListener());
quelle
Hier ist ein einfacherer Hack für das reibungslose Scrollen zu einer bestimmten Position bei einem Fling-Ereignis:
@Override public boolean fling(int velocityX, int velocityY) { smoothScrollToPosition(position); return super.fling(0, 0); }
Überschreiben Sie die Fling-Methode mit einem Aufruf vonoothScrollToPosition (int position), wobei "int position" die Position der gewünschten Ansicht im Adapter ist. Sie müssen den Wert der Position irgendwie ermitteln, dies hängt jedoch von Ihren Anforderungen und Ihrer Implementierung ab.
quelle
Nachdem ich ein bisschen mit RecyclerView herumgespielt habe, habe ich mir das bisher ausgedacht und was ich gerade benutze. Es hat einen kleinen Fehler, aber ich werde die Bohnen (noch) nicht verschütten, da Sie es wahrscheinlich nicht bemerken werden.
https://gist.github.com/lauw/fc84f7d04f8c54e56d56
Es unterstützt nur horizontale Recycling-Ansichten und rastet in der Mitte ein. Außerdem können Ansichten verkleinert werden, je nachdem, wie weit sie vom Zentrum entfernt sind. Verwendung als Ersatz für RecyclerView.
Bearbeiten: 08/2016 In ein Repository aufgenommen:
https://github.com/lauw/Android-SnappingRecyclerView
Ich werde dies einfach fortsetzen, während ich an einer besseren Implementierung arbeite.
quelle
RecyclerView
Snap Scroll ist jetzt Teil der Support-Bibliothek 24.2.0
https://developer.android.com/topic/libraries/support-library/revisions.html
Verwandte Klassen:
https://developer.android.com/reference/android/support/v7/widget/SnapHelper.html
https://developer.android.com/reference/android/support/v7/widget/LinearSnapHelper.html
Das Folgende ist ein gutes Beispiel:
https://rubensousa.github.io/2016/08/recyclerviewsnap
quelle
Ein sehr einfacher Ansatz, um ein Snap-to-Position-Verhalten zu erzielen -
recyclerView.setOnScrollListener(new OnScrollListener() { private boolean scrollingUp; @Override public void onScrolled(RecyclerView recyclerView, int dx, int dy) { // Or use dx for horizontal scrolling scrollingUp = dy < 0; } @Override public void onScrollStateChanged(RecyclerView recyclerView, int newState) { // Make sure scrolling has stopped before snapping if (newState == RecyclerView.SCROLL_STATE_IDLE) { // layoutManager is the recyclerview's layout manager which you need to have reference in advance int visiblePosition = scrollingUp ? layoutManager.findFirstVisibleItemPosition() : layoutManager.findLastVisibleItemPosition(); int completelyVisiblePosition = scrollingUp ? layoutManager .findFirstCompletelyVisibleItemPosition() : layoutManager .findLastCompletelyVisibleItemPosition(); // Check if we need to snap if (visiblePosition != completelyVisiblePosition) { recyclerView.smoothScrollToPosition(visiblePosition); return; } } });
Der einzige kleine Nachteil ist, dass es nicht rückwärts schnappt, wenn Sie weniger als die Hälfte der teilweise sichtbaren Zelle scrollen - aber wenn Sie dies nicht stört, ist es eine saubere und einfache Lösung.
quelle
Verwenden Sie GravitySnapHelper ( https://github.com/rubensousa/RecyclerViewSnap/blob/master/app/src/main/java/com/github/rubensousa/recyclerviewsnap/GravitySnapel .java ).
Schnappzentrum:
SnapHelper snapHelper = new LinearSnapHelper(); snapHelper.attachToRecyclerView(recyclerView);
Schnappstart mit GravitySnapHelper:
startRecyclerView.setLayoutManager(new LinearLayoutManager(this, LinearLayoutManager.HORIZONTAL, false)); SnapHelper snapHelperStart = new GravitySnapHelper(Gravity.START); snapHelperStart.attachToRecyclerView(startRecyclerView);
Schnappverschluss mit GravitySnapHelper:
topRecyclerView.setLayoutManager(new LinearLayoutManager(this)); SnapHelper snapHelperTop = new GravitySnapHelper(Gravity.TOP); snapHelperTop.attachToRecyclerView(topRecyclerView);
quelle
Ich habe eine funktionierende Lösung für die horizontale Ausrichtung von RecyclerView implementiert, die nur die Koordinaten von TouchEvent, First MOVE und UP liest. Berechnen Sie auf UP die Position, zu der wir gehen müssen.
public final class SnappyRecyclerView extends RecyclerView { private Point mStartMovePoint = new Point( 0, 0 ); private int mStartMovePositionFirst = 0; private int mStartMovePositionSecond = 0; public SnappyRecyclerView( Context context ) { super( context ); } public SnappyRecyclerView( Context context, AttributeSet attrs ) { super( context, attrs ); } public SnappyRecyclerView( Context context, AttributeSet attrs, int defStyle ) { super( context, attrs, defStyle ); } @Override public boolean onTouchEvent( MotionEvent e ) { final boolean ret = super.onTouchEvent( e ); final LayoutManager lm = getLayoutManager(); View childView = lm.getChildAt( 0 ); View childViewSecond = lm.getChildAt( 1 ); if( ( e.getAction() & MotionEvent.ACTION_MASK ) == MotionEvent.ACTION_MOVE && mStartMovePoint.x == 0) { mStartMovePoint.x = (int)e.getX(); mStartMovePoint.y = (int)e.getY(); mStartMovePositionFirst = lm.getPosition( childView ); if( childViewSecond != null ) mStartMovePositionSecond = lm.getPosition( childViewSecond ); }// if MotionEvent.ACTION_MOVE if( ( e.getAction() & MotionEvent.ACTION_MASK ) == MotionEvent.ACTION_UP ){ int currentX = (int)e.getX(); int width = childView.getWidth(); int xMovement = currentX - mStartMovePoint.x; // move back will be positive value final boolean moveBack = xMovement > 0; int calculatedPosition = mStartMovePositionFirst; if( moveBack && mStartMovePositionSecond > 0 ) calculatedPosition = mStartMovePositionSecond; if( Math.abs( xMovement ) > ( width / 3 ) ) calculatedPosition += moveBack ? -1 : 1; if( calculatedPosition >= getAdapter().getItemCount() ) calculatedPosition = getAdapter().getItemCount() -1; if( calculatedPosition < 0 || getAdapter().getItemCount() == 0 ) calculatedPosition = 0; mStartMovePoint.x = 0; mStartMovePoint.y = 0; mStartMovePositionFirst = 0; mStartMovePositionSecond = 0; smoothScrollToPosition( calculatedPosition ); }// if MotionEvent.ACTION_UP return ret; }}
Funktioniert gut für mich, lassen Sie mich wissen, wenn etwas nicht stimmt.
quelle
So aktualisieren Sie die Antwort von humblerookie:
Dieser Scroll-Listener ist in der Tat für das Centerlocking von https://github.com/humblerookie/centerlockrecyclerview/ wirksam.
Es gibt jedoch eine einfachere Möglichkeit, am Anfang und am Ende der Recyclingansicht Polster hinzuzufügen, um die Elemente zu zentrieren:
mRecycler.getViewTreeObserver().addOnGlobalLayoutListener(new ViewTreeObserver.OnGlobalLayoutListener() { @Override public void onGlobalLayout() { int childWidth = (int) TypedValue.applyDimension(TypedValue.COMPLEX_UNIT_DIP, CHILD_WIDTH_IN_DP, getResources().getDisplayMetrics()); int offset = (mRecycler.getWidth() - childWidth) / 2; mRecycler.setPadding(offset, mRecycler.getPaddingTop(), offset, mRecycler.getPaddingBottom()); if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.JELLY_BEAN) { mRecycler.getViewTreeObserver().removeOnGlobalLayoutListener(this); } else { mRecycler.getViewTreeObserver().removeGlobalOnLayoutListener(this); } } });
quelle
android:clipToPadding="false"
undandroid:clipChildren="false"
zur Recycling-Ansicht in der XML-Layout-DateiEine weitere saubere Option ist die Verwendung von benutzerdefinierten Optionen.
LayoutManager
Sie können https://github.com/apptik/multiview/tree/master/layoutmanagers überprüfenEs befindet sich in der Entwicklung, funktioniert aber recht gut. Ein Schnappschuss ist verfügbar: https://oss.sonatype.org/content/repositories/snapshots/io/apptik/multiview/layoutmanagers/
Beispiel:
recyclerView.setLayoutManager(new SnapperLinearLayoutManager(getActivity()));
quelle
Ich brauchte etwas anderes als alle obigen Antworten.
Die Hauptanforderungen waren:
RecyclerView
.LayoutManager
oder zu verwendenRecyclerView
. Nur einRecyclerView.OnScrollListener
, der dann von angehängt wirdrecyclerView.addOnScrollListener(snapScrollListener)
. Auf diese Weise ist der Code viel sauberer.Und zwei sehr spezifische Anforderungen, die im folgenden Beispiel leicht zu ändern sein sollten, um zu Ihrem Fall zu passen:
RecyclerView
.Diese Lösung verwendet die native
LinearSmoothScroller
. Der Unterschied besteht darin, dass im letzten Schritt, wenn die "Zielansicht" gefunden wird, die Berechnung des Versatzes so geändert wird, dass sie an einer bestimmten Position einrastet.public class SnapScrollListener extends RecyclerView.OnScrollListener { private static final float MILLIS_PER_PIXEL = 200f; /** The x coordinate of recycler view to which the items should be scrolled */ private final int snapX; int prevState = RecyclerView.SCROLL_STATE_IDLE; int currentState = RecyclerView.SCROLL_STATE_IDLE; public SnapScrollListener(int snapX) { this.snapX = snapX; } @Override public void onScrollStateChanged(RecyclerView recyclerView, int newState) { super.onScrollStateChanged(recyclerView, newState); currentState = newState; if(prevState != RecyclerView.SCROLL_STATE_IDLE && currentState == RecyclerView.SCROLL_STATE_IDLE ){ performSnap(recyclerView); } prevState = currentState; } private void performSnap(RecyclerView recyclerView) { for( int i = 0 ;i < recyclerView.getChildCount() ; i ++ ){ View child = recyclerView.getChildAt(i); final int left = child.getLeft(); int right = child.getRight(); int halfWidth = (right - left) / 2; if (left == snapX) return; if (left - halfWidth <= snapX && left + halfWidth >= snapX) { //check if child is over the snapX position int adapterPosition = recyclerView.getChildAdapterPosition(child); int dx = snapX - left; smoothScrollToPositionWithOffset(recyclerView, adapterPosition, dx); return; } } } private void smoothScrollToPositionWithOffset(RecyclerView recyclerView, int adapterPosition, final int dx) { final RecyclerView.LayoutManager layoutManager = recyclerView.getLayoutManager(); if( layoutManager instanceof LinearLayoutManager) { LinearSmoothScroller scroller = new LinearSmoothScroller(recyclerView.getContext()) { @Override public PointF computeScrollVectorForPosition(int targetPosition) { return ((LinearLayoutManager) layoutManager).computeScrollVectorForPosition(targetPosition); } @Override protected void onTargetFound(View targetView, RecyclerView.State state, Action action) { 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); } } @Override protected float calculateSpeedPerPixel(DisplayMetrics displayMetrics) { return MILLIS_PER_PIXEL / displayMetrics.densityDpi; } }; scroller.setTargetPosition(adapterPosition); layoutManager.startSmoothScroll(scroller); } }
quelle