Прокрутите RecyclerView, чтобы показать выбранный элемент сверху


216

Я ищу способ прокрутки, RecyclerViewчтобы показать выбранный элемент сверху.

В ListViewя был в состоянии сделать это с помощью scrollTo(x,y)и получить в верхней части элемента , который нужно быть по центру.

Что-то вроде:

@Override
public void onItemClick(View v, int pos){
    mylistView.scrollTo(0, v.getTop());
}

Проблема в том, что RecyclerViewвозвращает scrollToметод при использовании этого метода, говоря

RecyclerView не поддерживает прокрутку до абсолютной позиции

Как прокрутить a, RecyclerViewчтобы поместить выбранный элемент в верхнюю часть представления?

Ответы:


406

Если вы используете LinearLayoutManagerили Staggered GridLayoutManager, у каждого из них есть метод scrollToPositionWithOffset, который принимает как позицию, так и смещение начала элемента от начала элемента RecyclerView, что, по-видимому, будет выполнением того, что вам нужно (установка смещения на 0 следует выровнять с верхом).

Например:

//Scroll item 2 to 20 pixels from the top
linearLayoutManager.scrollToPositionWithOffset(2, 20);

34
Если кому-то это нужно для восстановления позиции прокрутки в RecyclerView, это как сохранить позиции прокрутки (два аргумента для метода scrollToPositionWithOffset): int index = linearLayoutManager.findFirstVisibleItemPosition (); View v = linearLayoutManager.getChildAt (0); int top = (v == null)? 0: (v.getTop () - linearLayoutManager.getPaddingTop ());
DominicM

Кажется, я не понимаю, когда вызывать scrollToPosition? если прослушиватель выбора находится в отдельных представлениях, как объект RecyclerView (который имеет доступ к своему диспетчеру компоновки, который может вызывать scrolToPosition) будет уведомлен о том, что элемент выбран?
Нонос

@ Нет, вы можете сначала сохранить LayoutManager, который вы используете, в RecylerView.setLayoutManager (), а затем получить к нему прямой доступ. Вот так: LinearLayoutManager llm = new LinearLayoutManager (this); mRecyclerView.setLayoutManager (LLM); ... (далее в коде) ... llm.scrollToPositionWithOffset (2, 20);
Нирадж

19
Что делать, если я хочу «плавно» прокрутить вверх?
Тиаго

@Tiago, я не играл с ним, но это 3-часть серии статей может помочь: blog.stylingandroid.com/scrolling-recyclerview-part-1
Niraj

93

Если вы ищете вертикальный менеджер LinearLayout, вы можете добиться плавной прокрутки с помощью пользовательского LinearSmoothScroller:

import android.content.Context;
import android.graphics.PointF;
import android.support.v7.widget.LinearLayoutManager;
import android.support.v7.widget.LinearSmoothScroller;
import android.support.v7.widget.RecyclerView;

public class SnappingLinearLayoutManager extends LinearLayoutManager {

    public SnappingLinearLayoutManager(Context context, int orientation, boolean reverseLayout) {
        super(context, orientation, reverseLayout);
    }

    @Override
    public void smoothScrollToPosition(RecyclerView recyclerView, RecyclerView.State state,
                                       int position) {
        RecyclerView.SmoothScroller smoothScroller = new TopSnappedSmoothScroller(recyclerView.getContext());
        smoothScroller.setTargetPosition(position);
        startSmoothScroll(smoothScroller);
    }

    private class TopSnappedSmoothScroller extends LinearSmoothScroller {
        public TopSnappedSmoothScroller(Context context) {
            super(context);

        }

        @Override
        public PointF computeScrollVectorForPosition(int targetPosition) {
            return SnappingLinearLayoutManager.this
                    .computeScrollVectorForPosition(targetPosition);
        }

        @Override
        protected int getVerticalSnapPreference() {
            return SNAP_TO_START;
        }
    }
}

используйте экземпляр layoutmanager в представлении recycle, а затем вызов сгладит recyclerView.smoothScrollToPosition(pos);прокрутку до выбранной позиции в верхней части представления переработчика


1
Действительно полезный ответ! Я также переопределяю getHorizontalSnapPreference()в TopSnappedSmoothScroller для горизонтальных списков.
Хуан Саравиа

6
Прав ли я, заметив, что это работает только для тех предметов, которых после / под них достаточно, чтобы заполнить оставшиеся RecyclerView? Который должен сказать: Это не похоже на работу для последнего пункта - независимо от SNAP_TO_STARTтех RecyclerViewтолько свитки , пока элемент не становится видимым в нижней части, но не двигается весь путь вверх. Есть идеи как этого добиться? (Я работал над этим, устанавливая пользовательские отступы, RecyclerViewно это не кажется правильным ...)
david.mihola


когда-нибудь предмет становится сфокусированным, у меня есть фокус, который можно нарисовать для просмотра предмета.
YLS

1
@ david.mihola Вы нашли «правильный» путь? Я установил пользовательские отступы для своего рециркулятора, но у меня есть некоторые странные проблемы ...
bernardo.g


27

Вам просто нужно позвонить recyclerview.scrollToPosition(position). Это хорошо!

Если вы хотите вызвать его в адаптере, просто позвольте вашему адаптеру иметь экземпляр recyclerview или действие или фрагмент, который содержит recyclerview, чем реализует метод getRecyclerview()в них.

Я надеюсь, что это может помочь вам.


2
Это сработало для меня - recyclerView.scrollToPosition (0); поставь меня на вершину.
Рэй Хантер

2
что не работает, вы должны использовать linearLayoutManager.scrollToPositionWithOffset (pos, 0);
Анил Угале

@Anil Ugale Это чепуха. Конечно, это работает. Вы можете использовать любой LayoutManager для RecyclerView. Почему вы должны заботиться об этом, когда хотите прокрутить? Я думаю, что вы делали что-то не так. Recyclerview.scrollToPosition (position) - это вспомогательный метод, который вызывает LayoutManager.scrollToPosition (position).
Невероятное

1
@TheincredibleJan это работает не во всех случаях, layoutManager, как правило, отвечает за прокрутку в любом случае только для вашей информации.
Мохаммед

Это не всегда помещает запрошенный элемент сверху, как заданный вопрос.
Джеймс Риордан

11

то же самое с регулятором скорости

public class SmoothScrollLinearLayoutManager extends LinearLayoutManager {
private static final float MILLISECONDS_PER_INCH = 110f;
private Context mContext;

public SmoothScrollLinearLayoutManager(Context context,int orientation, boolean reverseLayout) {
    super(context,orientation,reverseLayout);
    mContext = context;
}

@Override
public void smoothScrollToPosition(RecyclerView recyclerView, RecyclerView.State state,
                                   int position) {
    RecyclerView.SmoothScroller smoothScroller = new TopSnappedSmoothScroller(recyclerView.getContext()){
        //This controls the direction in which smoothScroll looks for your view
        @Override
        public PointF computeScrollVectorForPosition(int targetPosition) {
            return new PointF(0, 1);
        }

        //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);
}


private class TopSnappedSmoothScroller extends LinearSmoothScroller {
    public TopSnappedSmoothScroller(Context context) {
        super(context);

    }

    @Override
    public PointF computeScrollVectorForPosition(int targetPosition) {
        return SmoothScrollLinearLayoutManager.this
                .computeScrollVectorForPosition(targetPosition);
    }

    @Override
    protected int getVerticalSnapPreference() {
        return SNAP_TO_START;
    }
}
}

8

Попробуйте то, что сработало для меня круто!

Создать переменную private static int displayedposition = 0;

Теперь о позиции вашего RecyclerView в вашей деятельности.

myRecyclerView.setOnScrollListener(new RecyclerView.OnScrollListener() {
            @Override
            public void onScrollStateChanged(RecyclerView recyclerView, int newState) {
                super.onScrollStateChanged(recyclerView, newState);
            }

            @Override
            public void onScrolled(RecyclerView recyclerView, int dx, int dy) {
                super.onScrolled(recyclerView, dx, dy);

                LinearLayoutManager llm = (LinearLayoutManager) myRecyclerView.getLayoutManager();


                displayedposition = llm.findFirstVisibleItemPosition();


            }
        });

Поместите это утверждение туда, где вы хотите разместить прежний сайт, отображаемый в вашем представлении.

LinearLayoutManager llm = (LinearLayoutManager) mRecyclerView.getLayoutManager();
        llm.scrollToPositionWithOffset(displayedposition , youList.size());

Ну вот и все, у меня все работало нормально \ о /


7

просто вызовите этот метод просто:

((LinearLayoutManager)recyclerView.getLayoutManager()).scrollToPositionWithOffset(yourItemPosition,0);

вместо того:

recyclerView.scrollToPosition(yourItemPosition);

"вместо того"? Distwo не упомянул "scrollToPosition". Если бы он использовал его, проблем бы не было. scrollToPosition () как задумано.
Невероятный

1
как ни странно, использование scrollToPositionWithOffset работает для меня, а recyclerview.scrollToPosition не работает. Хотя я бы попросил Амира изменить RecyclerView на recyclerview, потому что это дает ощущение, что методы являются статическими, а они - нет.
Мохит

6

что я сделал, чтобы восстановить положение прокрутки после обновления RecyclerView на нажатой кнопке:

if (linearLayoutManager != null) {

    index = linearLayoutManager.findFirstVisibleItemPosition();
    View v = linearLayoutManager.getChildAt(0);
    top = (v == null) ? 0 : (v.getTop() - linearLayoutManager.getPaddingTop());
    Log.d("TAG", "visible position " + " " + index);
}

else{
    index = 0;
}

linearLayoutManager = new LinearLayoutManager(getApplicationContext());
linearLayoutManager.scrollToPositionWithOffset(index, top);

получение смещения первого видимого элемента сверху до создания объекта linearLayoutManager и после его создания вызывается scrollToPositionWithOffset объекта LinearLayoutManager.


6

Я не знаю, почему я не нашел лучший ответ, но это действительно просто.

recyclerView.smoothScrollToPosition(position);

Нет ошибок

Создает анимацию


где вы положите это в деятельности или адаптер?
Арнольд Браун

3

прокрутка в определенной позиции,
и это мне очень помогло. щелкнув слушатель вы можете получить позицию в вашем адаптере

layoutmanager.scrollToPosition(int position);

3
If you want to scroll automatic without show scroll motion then you need to write following code:

**=> mRecyclerView.getLayoutManager().scrollToPosition(position);**

If you want to display scroll motion then you need to add following code.
=>Step 1: You need to declare SmoothScroller.

RecyclerView.SmoothScroller smoothScroller = new
                LinearSmoothScroller(this.getApplicationContext()) {
                    @Override
                    protected int getVerticalSnapPreference() {
                        return LinearSmoothScroller.SNAP_TO_START;
                    }
                };

=>step 2: You need to add this code any event you want to perform scroll to specific position.
=>First you need to set target position to SmoothScroller.

**smoothScroller.setTargetPosition(position);**

=>Then you need to set SmoothScroller to LayoutManager.
                        **mRecyclerView.getLayoutManager().startSmoothScroll(smoothScroller);**

3

Ни один из ответов не объясняет, как показать последний элемент (ы) вверху. Таким образом, ответы работают только для элементов, у которых еще есть элементы над или под ними, чтобы заполнить оставшиеся RecyclerView. Например, если есть 59 элементов и выбран 56-й элемент, он должен быть вверху, как на рисунке ниже:

RecyclerView - выбран элемент 56 Мы могли бы обрабатывать эти случаи, используя linearLayoutManager.scrollToPositionWithOffset(pos, 0)и дополнительную логику в Adapterиз RecyclerView- путем добавления пользовательской маржи ниже последнего элемента (если последний элемент не виден , то это означает , что есть достаточно места Наполните RecyclerView). Пользовательское поле может быть разницей между высотой корневого вида и высотой элемента. Итак, ваш Adapterзапрос RecyclerViewбудет выглядеть следующим образом:

...
@Override
public void onBindViewHolder(ViewHolder holder, final int position) {
    ...

    int bottomHeight = 0;
    int itemHeight = holder.itemView.getMeasuredHeight();
    // if it's the last item then add a bottom margin that is enough to bring it to the top
    if (position == mDataSet.length - 1) {
        bottomHeight = Math.max(0, mRootView.getMeasuredHeight() - itemHeight);
    }
    RecyclerView.LayoutParams params = (RecyclerView.LayoutParams)holder.itemView.getLayoutParams();
    params.setMargins(0, 0, params.rightMargin, bottomHeight);
    holder.itemView.setLayoutParams(params);

    ...
} 
...

2

В моем случае у меня RecyclerViewесть такой верх

<android.support.v7.widget.RecyclerView
     ...
     android:paddingTop="100dp"
     android:clipToPadding="false"
/>

Затем для прокрутки элемента вверх, мне нужно

recyclerViewLinearLayoutManager.scrollToPositionWithOffset(position, -yourRecyclerView.getPaddingTop());

1

Если ваше LayoutManagerэто LinearLayoutManagerвы используете scrollToPositionWithOffset(position,0);на нем , и это сделает ваш пункт первого видимый элемент в списке. В противном случае, вы можете использовать smoothScrollToPositionна RecyclerViewнепосредственно.

В итоге я использовал приведенный ниже код.

 RecyclerView.LayoutManager layoutManager = mainList.getLayoutManager();
        if (layoutManager instanceof LinearLayoutManager) {
            // Scroll to item and make it the first visible item of the list.
            ((LinearLayoutManager) layoutManager).scrollToPositionWithOffset(position, 0);
        } else {
            mainList.smoothScrollToPosition(position);
        }

-2

Я использую код ниже для плавной прокрутки элемента (thisView) к вершине.
Это работает также для GridLayoutManager с видами различной высоты:

View firstView = mRecyclerView.getChildAt(0);
int toY = firstView.getTop();
int firstPosition = mRecyclerView.getChildAdapterPosition(firstView);
View thisView = mRecyclerView.getChildAt(thisPosition - firstPosition);
int fromY = thisView.getTop();

mRecyclerView.smoothScrollBy(0, fromY - toY);

Кажется, работает достаточно хорошо для быстрого решения.


1
Какова стоимость thisPosition?
ПРАНАЙ

это позиция, в которую вы хотите сгладить прокрутку
Jan Rabe

Почему бы просто не использовать smoothScrollToPosition (int position) без каких-либо вычислений? Неважно, откуда вы начинаете, не так ли?
Невероятное
Используя наш сайт, вы подтверждаете, что прочитали и поняли нашу Политику в отношении файлов cookie и Политику конфиденциальности.
Licensed under cc by-sa 3.0 with attribution required.