Подтвердить что ты не робот

Быстрая прокрутка в 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, но это также очень хладнокровно.

Я чувствую, что мне что-то не хватает. Есть ли лучший способ сделать это?

4b9b3361

Ответ 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). Таким образом, код намного чище.

И два очень специфических требования, которые должны быть легко изменены в приведенном ниже примере, чтобы соответствовать вашему случаю:

  1. Работает по горизонтали.
  2. Привязывает левый край элемента к определенной точке в 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);

    }
}