Быстрая прокрутка в RecyclerView
Я пытаюсь использовать новый класс RecyclerView
для сценария, где я хочу, чтобы компонент привязывался к определенному элементу при прокрутке (старый Android Gallery
приходит на ум в качестве примера такого списка с центром -блокированный элемент).
Это подход, который я принимаю до сих пор:
У меня есть интерфейс ISnappyLayoutManager
, который содержит метод getPositionForVelocity
, который вычисляет, в какой позиции представление должно заканчивать прокрутку с учетом начальной скорости движения.
public interface ISnappyLayoutManager {
int getPositionForVelocity(int velocityX, int velocityY);
}
Тогда у меня есть класс, SnappyRecyclerView
, который подклассы RecyclerView
и переопределяет его метод fling() таким образом, чтобы отображать точное точное количество:
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;
}
}
Я не очень доволен этим подходом по нескольким причинам. Прежде всего, похоже, что философия "RecyclerView" должна подклассифицировать ее для реализации определенного типа прокрутки. Во-вторых, если я хочу просто использовать значение по умолчанию LinearLayoutManager
, это становится несколько сложным, поскольку я должен возиться со своими внутренностями, чтобы понять его текущее состояние прокрутки и рассчитать, где именно это происходит. Наконец, это даже не позаботится обо всех возможных сценариях прокрутки, как если бы вы переместили список, а затем приостановили, а затем подняли палец, не произошло никакого события перехода (скорость слишком низкая), и поэтому список остается на полпути должность. Об этом можно позаботиться, добавив слушателя состояния прокрутки в RecyclerView
, но это также очень хладнокровно.
Я чувствую, что мне что-то не хватает. Есть ли лучший способ сделать это?
Ответы
Ответ 1
В итоге я придумал что-то немного отличное от вышеизложенного. Это не идеально, но он работает хорошо для меня и может быть полезен кому-то другому. Я не буду принимать этот ответ в надежде, что кто-то еще придет с чем-то лучшим и менее взломанным (и, возможно, я не понимаю реализацию RecyclerView
и пропустил какой-то простой способ сделать это, но тем временем это достаточно хорош для работы правительства!)
Основы реализации таковы: прокрутка в RecyclerView
является разновидностью разделения между RecyclerView
и LinearLayoutManager
. Мне нужно обрабатывать два случая:
- Пользователь бросает вид. Поведение по умолчанию состоит в том, что
RecyclerView
передает fling во внутреннюю Scroller
, которая затем выполняет маска прокрутки. Это проблематично, потому что тогда RecyclerView
обычно располагается в незанятой позиции. Я решаю это, переопределяя реализацию RecyclerView
fling()
и вместо того, чтобы бросать, плавно переведите LinearLayoutManager
в позицию.
- Пользователь поднимает палец с недостаточной скоростью, чтобы инициировать свиток. В этом случае не происходит перелета. Я хочу обнаружить этот случай в том случае, если представление не находится в позиции привязки. Я делаю это, переопределяя метод
onTouchEvent
.
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 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 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;
}
}
Интерфейс для быстрого управления макетами:
/**
* 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();
}
И вот пример LayoutManager
, который подклассифицирует LinearLayoutManager
, чтобы получить LayoutManager
с плавной прокруткой:
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;
}
}
Ответ 2
С LinearSnapHelper
это сейчас очень просто.
Все, что вам нужно сделать, это следующее:
SnapHelper helper = new LinearSnapHelper();
helper.attachToRecyclerView(recyclerView);
Это так просто! Обратите внимание, что LinearSnapHelper
был добавлен в библиотеку поддержки, начиная с версии 24.2.0.
Значение вам нужно добавить в модуль приложения build.gradle
compile "com.android.support:recyclerview-v7:24.2.0"
Ответ 3
Мне удалось найти более чистый способ сделать это. @Catherine (OP) сообщите мне, если это может быть улучшено или вы чувствуете улучшение по сравнению с вашим:)
Здесь используется прослушиватель прокрутки.
https://github.com/humblerookie/centerlockrecyclerview/
Я опустил некоторые незначительные предположения здесь, например, например.
1) Начальная и заключительная прокладки. Первый и последний элементы в горизонтальном прокрутке должны иметь начальную и конечную прокладки соответственно, чтобы начальный и конечный виды находились в центре при прокрутке до первого и последнего соответственно. Например, в onBindViewHolder вы можете сделать что-то вроде этого.
@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);
}
}
Логика довольно условна, и ее можно использовать для многих других случаев. В моем случае просмотр ресайклера является горизонтальным и растягивает всю горизонтальную ширину без полей (в основном центр координат X - центр экрана) или неровные прокладки.
Если кто-то сталкивается с любезным комментарием о проблеме.
Ответ 4
Мне также нужен был быстрый просмотр recycler. Я хочу, чтобы элемент просмотра ресайклера привязывался слева от столбца. Это привело к внедрению SnapScrollListener, который я установил в представлении recycler. Это мой код:
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);
}
}
}
}
Вычисление привязки:
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;
}
Если первое видимое изображение прокручивается больше, чем половина ширины экрана, следующий видимый столбец привязывается влево.
Установка слушателя:
mRecyclerView.addOnScrollListener(new SnapScrollListener());
Ответ 5
Здесь более простой взлом для плавной прокрутки к определенной позиции в событии сбрасывания:
@Override
public boolean fling(int velocityX, int velocityY) {
smoothScrollToPosition(position);
return super.fling(0, 0);
}
Переопределите метод fling с вызовом smoothScrollToPosition (int position), где "int position" - позиция вашего представления в адаптере. Вам нужно каким-то образом получить значение позиции, но это зависит от ваших потребностей и реализации.
Ответ 6
После того, как я немного поработал с RecyclerView, это то, к чему я пришел, и что я сейчас использую. У него есть один незначительный недостаток, но я не буду проливать beans (пока), так как вы, вероятно, не заметите.
https://gist.github.com/lauw/fc84f7d04f8c54e56d56
Он поддерживает только горизонтальные обратные вызовы и привязки к центру, а также может масштабировать представления в зависимости от того, насколько они далеко от центра. Используйте в качестве замены RecyclerView.
Изменить: 08/2016
Сделано в хранилище:
https://github.com/lauw/Android-SnappingRecyclerView
Я просто продолжу это, работая над лучшей реализацией.
Ответ 7
Очень простой подход для достижения привязки к положению -
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 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;
}
}
});
Единственным недостатком является то, что он не будет привязываться назад, когда вы прокручиваете менее половины пути частично видимой ячейки, но если это вас не беспокоит, это чистое и простое решение.
Ответ 8
Snap Scroll теперь является частью библиотеки поддержки 24.2.0
https://developer.android.com/topic/libraries/support-library/revisions.html
Связанные классы:
https://developer.android.com/reference/android/support/v7/widget/SnapHelper.html
https://developer.android.com/reference/android/support/v7/widget/LinearSnapHelper.html
Ниже приведен хороший пример:
https://rubensousa.github.io/2016/08/recyclerviewsnap
Ответ 9
Я реализовал рабочее решение для горизонтальной ориентации RecyclerView, которое просто считывает координаты onTouchEvent, сначала MOVE и UP. На UP вычислите позицию, в которой нам нужно перейти.
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;
}}
Прекрасно работает для меня, дайте мне знать, если что-то не так.
Ответ 10
Чтобы обновить ответ humblerookie:
Этот прослушиватель прокрутки действительно эффективен для централизованного блокирования
https://github.com/humblerookie/centerlockrecyclerview/
Но вот более простой способ добавить дополнение в начале и конце recyclerview для центрирования его элементов:
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);
}
}
});
Ответ 11
Если вам нужна поддержка привязки для начала, сверху, конца или снизу, используйте GravitySnapHelper (https://github.com/rubensousa/RecyclerViewSnap/blob/master/app/src/main/java/com/github/rubensousa/recyclerviewsnap/GravitySnapHelper.java).
Центр привязки:
SnapHelper snapHelper = new LinearSnapHelper();
snapHelper.attachToRecyclerView(recyclerView);
Запуск с помощью GravitySnapHelper:
startRecyclerView.setLayoutManager(new LinearLayoutManager(this,
LinearLayoutManager.HORIZONTAL, false));
SnapHelper snapHelperStart = new GravitySnapHelper(Gravity.START);
snapHelperStart.attachToRecyclerView(startRecyclerView);
Привязка сверху с помощью GravitySnapHelper:
topRecyclerView.setLayoutManager(new LinearLayoutManager(this));
SnapHelper snapHelperTop = new GravitySnapHelper(Gravity.TOP);
snapHelperTop.attachToRecyclerView(topRecyclerView);
Ответ 12
И еще один вариант очистки - использовать пользовательский LayoutManager
,
вы можете проверить
https://github.com/apptik/multiview/tree/master/layoutmanagers
Он разрабатывается, но работает достаточно хорошо.
Доступен снимок:
https://oss.sonatype.org/content/repositories/snapshots/io/apptik/multiview/layoutmanagers/
Пример:
recyclerView.setLayoutManager(new SnapperLinearLayoutManager(getActivity()));
Ответ 13
Мне нужно что-то немного отличающееся от всех вышеперечисленных ответов.
Основные требования заключались в следующем:
- Он работает так же, когда пользователь бросает или просто отпускает палец.
- Использует собственный механизм прокрутки, чтобы иметь такое же "чувство", как обычный
RecyclerView
.
- Когда он останавливается, он начинает плавно прокручиваться до ближайшей точки привязки.
- Не нужно использовать пользовательские
LayoutManager
или RecyclerView
. Просто a RecyclerView.OnScrollListener
, который затем привязан recyclerView.addOnScrollListener(snapScrollListener)
. Таким образом, код намного чище.
И два очень специфических требования, которые должны быть легко изменены в приведенном ниже примере, чтобы соответствовать вашему случаю:
- Работает по горизонтали.
- Привязывает левый край элемента к определенной точке в
RecyclerView
.
В этом решении используется собственный LinearSmoothScroller
. Разница в том, что на последнем шаге, когда найден "целевой вид", он меняет расчет смещения так, что он привязывается к определенной позиции.
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);
}
}