Cuộn RecyclerView để hiển thị mục đã chọn trên đầu trang


215

Tôi đang tìm cách cuộn một RecyclerViewđể hiển thị mục đã chọn lên trên.

Trong một ListViewtôi đã có thể làm điều đó bằng cách sử dụng scrollTo(x,y)và lấy đỉnh của phần tử cần được căn giữa.

Cái gì đó như:

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

Vấn đề là RecyclerViewlỗi trả về khi sử dụng scrollTophương thức của nó

RecyclerView không hỗ trợ cuộn đến vị trí tuyệt đối

Làm cách nào tôi có thể cuộn a RecyclerViewđể đặt mục đã chọn ở đầu chế độ xem?

Câu trả lời:


405

Nếu bạn đang sử dụng LinearLayoutManagerhoặc Staggered GridLayoutManager, mỗi cái đều có một phương thức scrollToPocationWith Offerset có cả vị trí và cả phần bù của phần bắt đầu của mục từ lúc bắt đầu RecyclerView, có vẻ như nó sẽ thực hiện những gì bạn cần (đặt giá trị bù thành 0 nên thẳng hàng với đỉnh).

Ví dụ:

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

34
Nếu bất cứ ai cần điều này để khôi phục vị trí cuộn của RecyclerView, thì đây là cách lưu vị trí cuộn (hai đối số cho phương thức scrollToPocationWith Offerset): int index = linearLayoutManager.findFirstVisibleItemPP (); Xem v = linearLayoutManager.getChildAt (0); int top = (v == null)? 0: (v.getTop () - linearLayoutManager.getPaddingTop ());
Đaminh 17/2/2015 lúc

Những gì tôi dường như không nhận được về điều này là khi gọi scrollToPocation? nếu trình nghe lựa chọn nằm trên các khung nhìn riêng lẻ, thì đối tượng RecyclerView (có quyền truy cập vào trình quản lý bố cục của nó có thể gọi scrolToPocation) sẽ được thông báo rằng một mục được chọn như thế nào?
Nonos

@Nonos trước tiên bạn có thể lưu trữ Trình quản lý Bố cục mà bạn sử dụng trong RecylerView.setLayoutManager () và sau đó truy cập trực tiếp vào nó. Giống như thế này: linearLayoutManager llm = new linearLayoutManager (cái này); mRecyclerView.setLayoutManager (llm); ... (sau này là mã) ... llm.scrollToPocationWith Offerset (2, 20);
Niraj

19
Nếu tôi muốn cuộn lên trên cùng một cách "trơn tru" thì sao?
Tiago

@Tiago, tôi chưa chơi xung quanh nó, nhưng loạt bài viết gồm 3 phần này có thể giúp ích: blog.stommeandroid.com/scrolling-recyclerview-part-1
Niraj

93

Nếu bạn đang tìm kiếm Trình quản lý tuyến tính dọc, bạn có thể cuộn dễ dàng bằng cách sử dụng một tùy chỉnh 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;
        }
    }
}

sử dụng một thể hiện của layoutmanager trong chế độ xem tái chế và sau đó gọi recyclerView.smoothScrollToPosition(pos);sẽ di chuyển trơn tru đến vị trí đã chọn lên trên cùng của chế độ xem tái chế


1
Câu trả lời thực sự hữu ích! Tôi cũng ghi đè getHorizontalSnapPreference()trong TopSnappedSmoothScroller cho danh sách Ngang.
Juan Saravia

6
Tôi có đúng không khi nhận thấy rằng điều này chỉ hoạt động đối với các vật phẩm vẫn còn đủ sau / dưới chúng để lấp đầy phần còn lại RecyclerView? Điều này có nghĩa là: Điều này dường như không hoạt động cho mục cuối cùng - bất kể SNAP_TO_STARTcác RecyclerViewcuộn duy nhất cho đến khi mục đó hiển thị ở phía dưới nhưng không di chuyển hết lên. Bất kỳ ý tưởng làm thế nào để đạt được điều này? (Tôi đã giải quyết vấn đề này bằng cách đặt phần đệm tùy chỉnh trên RecyclerViewnhưng điều đó có vẻ không thực sự đúng ...)
david.mihola


đôi khi các mục vật phẩm được tập trung, tôi đang có một tiêu điểm có thể rút ra để xem mục.
YLS

1
@ david.mihola Bạn đã tìm được cách 'đúng' chưa? Tôi đã đặt phần đệm tùy chỉnh cho phần tái chế của mình nhưng tôi gặp một số vấn đề kỳ lạ ...
bernardo.g


28

Bạn chỉ cần gọi recyclerview.scrollToPosition(position). Tốt rồi!

Nếu bạn muốn gọi nó trong bộ điều hợp, chỉ cần để bộ điều hợp của bạn có phiên bản recyclerview hoặc hoạt động hoặc đoạn có chứa recyclerview, hơn là thực hiện phương thức getRecyclerview()trong chúng.

Tôi hy vọng nó có thể giúp bạn.


2
Điều này làm việc cho tôi - recyclerView.scrollToP vị trí (0); đặt tôi lên hàng đầu
Ray Hunter

2
rằng không hoạt động, bạn phải sử dụng linearLayoutManager.scrollToPocationWith Offerset (pos, 0);
Anil Ugale

@Anil Ugale Điều đó thật vớ vẩn. Tất nhiên nó hoạt động. Bạn có thể sử dụng bất kỳ Trình quản lý bố cục nào cho RecyclerView. Tại sao sau này bạn nên quan tâm đến nó khi bạn muốn cuộn? Tôi nghĩ rằng bạn đã làm điều gì đó sai. Recyclerview.scrollToP vị trí (vị trí) là một phương thức tiện lợi, gọi LayoutManager.scrollToP vị trí (vị trí).
Không thể tin được vào ngày

1
@TheincredibleJan nó không hoạt động trong mọi trường hợp, layoutManager thường chịu trách nhiệm cho việc cuộn dù sao chỉ là thông tin của bạn.
Mohammed

11

tương tự với bộ điều tốc

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

Hãy thử những gì làm việc cho tôi mát mẻ!

Tạo một biến private static int displayedposition = 0;

Bây giờ cho vị trí của RecyclerView trong Hoạt động của bạn.

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();


            }
        });

Đặt tuyên bố này nơi bạn muốn nó đặt trang web cũ được hiển thị trong chế độ xem của bạn.

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

Vâng, nó hoạt động tốt với tôi \ o /


7

chỉ cần gọi phương thức này đơn giản:

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

thay vì:

recyclerView.scrollToPosition(yourItemPosition);

"thay vì"? Distwo đã không đề cập đến "scrollToPocation". Nếu anh ta đã sử dụng nó thì sẽ không có vấn đề gì. scrollToP vị trí () như dự định.
Không thể tin được vào ngày

1
thật kỳ lạ, sử dụng scrollToPocationWith Offerset hoạt động với tôi và recyclerview.scrollToP vị trí không. Mặc dù, tôi sẽ yêu cầu Amir thay đổi RecyclerView thành recyclerview, bởi vì nó mang lại cảm giác rằng các phương thức là tĩnh, còn chúng thì không.
Mohit

6

những gì tôi đã làm để khôi phục vị trí cuộn sau khi làm mới nút RecyclerView trên đã nhấp:

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

lấy phần bù của mục hiển thị đầu tiên từ trên xuống trước khi tạo đối tượng linearLayoutManager và sau khi khởi tạo nó, scrollToPocationWith Offerset của đối tượng linearLayoutManager được gọi.


6

Tôi không biết tại sao tôi không tìm thấy câu trả lời hay nhất nhưng nó thực sự đơn giản.

recyclerView.smoothScrollToPosition(position);

Không có lỗi

Tạo ảnh động


bạn sẽ đặt cái này ở đâu trong hoạt động hay bộ chuyển đổi?
Arnold Brown

3

cuộn ở vị trí cụ thể
và điều này đã giúp tôi rất nhiều. bằng cách nhấp vào người nghe, bạn có thể có được vị trí trong bộ chuyển đổi của bạn

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

Không có câu trả lời nào giải thích cách hiển thị (các) mục cuối cùng ở trên cùng. Vì vậy, câu trả lời chỉ hoạt động đối với các mục vẫn có đủ các mục bên trên hoặc bên dưới chúng để điền vào phần còn lại RecyclerView. Chẳng hạn, nếu có 59 phần tử và phần tử thứ 56 được chọn, nó sẽ ở trên cùng như trong hình dưới đây:

RecyclerView - phần tử 56 được chọn Chúng ta có thể xử lý những trường hợp này bằng cách sử dụng linearLayoutManager.scrollToPositionWithOffset(pos, 0)và một logic bổ sung vào Adaptercác RecyclerView- bằng cách thêm một biên độ tùy chỉnh bên dưới mục cuối cùng (nếu mục cuối cùng là không nhìn thấy được thì nó có nghĩa là có đủ không gian lấp đầy các RecyclerView). Lề tùy chỉnh có thể là một sự khác biệt giữa chiều cao xem gốc và chiều cao mục. Vì vậy, bạn Adaptercho RecyclerViewsẽ trông như sau:

...
@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

Trong trường hợp của tôi, tôi RecyclerViewcó một đầu đệm như thế này

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

Sau đó, để cuộn một mục lên trên cùng, tôi cần phải

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

1

Nếu bạn LayoutManagerLinearLayoutManagerbạn sử dụng scrollToPositionWithOffset(position,0);nó và nó sẽ làm cho mục của bạn trở thành mục hiển thị đầu tiên trong danh sách. Nếu không, bạn có thể sử dụng smoothScrollToPositiontrên RecyclerViewtrực tiếp.

Tôi đã kết thúc bằng cách sử dụng mã dưới đây.

 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

Tôi sử dụng mã dưới đây để cuộn trơn tru một mục (thisView) lên trên cùng.
Nó cũng hoạt động cho GridLayoutManager với các chế độ xem khác nhau:

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

Có vẻ để làm việc đủ tốt cho một giải pháp nhanh chóng.


1
Giá trị của là thisPositiongì?
pRaNaY

đó là vị trí bạn muốn di chuyển trơn tru đến
Jan Rabe

Tại sao không chỉ đơn giản sử dụng smoothScrollToP vị trí (vị trí int) mà không có bất kỳ tính toán nào? Không quan trọng từ nơi bạn bắt đầu, phải không?
Không thể tin được vào ngày
Khi sử dụng trang web của chúng tôi, bạn xác nhận rằng bạn đã đọc và hiểu Chính sách cookieChính sách bảo mật của chúng tôi.
Licensed under cc by-sa 3.0 with attribution required.