Прокрутіть RecyclerView, щоб показати вибраний елемент зверху


216

Я шукаю спосіб прокрутки до, RecyclerViewщоб показати вибраний елемент зверху.

У a ListViewя міг це зробити, використовуючи scrollTo(x,y)та отримуючи верхню частину елемента, який потрібно зосереджувати.

Щось на зразок:

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

Проблема полягає в тому, що RecyclerViewповертає помилку при використанні scrollToметоду висловлювання

RecyclerView не підтримує прокручування до абсолютного положення

Як я можу прокручувати a, RecyclerViewщоб поставити вибраний елемент у верхній частині подання?

Відповіді:


406

Якщо ви використовуєте LinearLayoutManagerабо Поетапно GridLayoutManager, у кожному з них є метод scrollToPositionWithOffset, який займає як позицію, так і зсув початку елемента з початку RecyclerView, що здається, що воно виконає те, що вам потрібно (встановивши зміщення на 0 слід вирівняти зверху).

Наприклад:

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

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

Що я, мабуть, не розумію з цього приводу, коли викликати scrollToPosition? якщо слухач вибору знаходиться на окремих представленнях, то як об’єкт RecyclerView (який має доступ до свого менеджера макетів, який може викликати scrolToPosition), буде повідомлено, що вибраний елемент?
Нонос

@Nonos ви можете спочатку зберігати LayoutManager, який ви використовуєте в RecylerView.setLayoutManager (), а потім отримувати прямий доступ до нього. Ось так: LinearLayoutManager llm = новий LinearLayoutManager (це); 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;
        }
    }
}

використовувати екземпляр менеджера макета в режимі перегляду кошика, і тоді виклик recyclerView.smoothScrollToPosition(pos);згладить прокрутку до обраної позиції до вершини подання рециркулятора


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

6
Я маю рацію, зауваживши, що це працює лише для предметів, яких ще достатньо після / під ними, щоб заповнити решту RecyclerView? Який повинен сказати: Це не схоже на роботу для останнього пункту - незалежно від SNAP_TO_STARTтих RecyclerViewтільки сувої , поки елемент не стає видимим в нижній частині, але не рухається весь шлях вгору. Будь-які ідеї, як цього досягти? (Я працював над цим, встановлюючи власні RecyclerView
накладки,


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

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


28

Вам просто потрібно зателефонувати recyclerview.scrollToPosition(position). Добре!

Якщо ви хочете викликати його в адаптері, просто дозвольте вашому адаптеру мати примірник перегляду або активність або фрагмент, який містить recilerview, ніж реалізує метод getRecyclerview()в них.

Я сподіваюся, що це може вам допомогти.


2
Це працювало для мене - recilerView.scrollToPosition (0); поставити мене на вершину.
Рей Хантер

2
що не працює, ви повинні використовувати linearLayoutManager.scrollToPositionWithOffset (pos, 0);
Аніл Угале

@Anil Ugale Це нісенітниця. Звичайно, це працює. Ви можете використовувати будь-який LayoutManager для RecyclerView. Чому пізніше вам це буде турбувати, коли ви хочете прокрутити? Я думаю, що ти робив щось не так. Recyclerview.scrollToPosition (позиція) - це зручний метод, який викликає LayoutManager.scrollToPosition (позиція).
Неймовірний

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 працює для мене, а recilerview.scrollToPosition не став. Хоча я б попросив Аміра змінити RecyclerView на recilerview, оскільки це дає сенс, що методи є статичними, а їх немає.
Мохіт

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?
pRaNaY

це положення, на яке ви хочете згладити прокрутку
Ян Рабе

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