Gulir RecyclerView untuk menampilkan item yang dipilih di atas

215

Saya mencari cara untuk menggulir RecyclerViewuntuk menampilkan item yang dipilih di atas.

Dalam ListViewsaya bisa melakukannya dengan menggunakan scrollTo(x,y)dan mendapatkan bagian atas elemen yang perlu dipusatkan.

Sesuatu seperti:

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

Masalahnya adalah bahwa RecyclerViewmengembalikan kesalahan saat menggunakan scrollTometode itu mengatakan

RecyclerView tidak mendukung pengguliran ke posisi absolut

Bagaimana saya bisa menggulir RecyclerViewuntuk meletakkan item yang dipilih di bagian atas tampilan?

Distwo
sumber

Jawaban:

405

Jika Anda menggunakan LinearLayoutManageratau terhuyung-huyung GridLayoutManager, mereka masing-masing memiliki metode scrollToPositionWithOffset yang mengambil posisi dan juga offset dari awal item dari awal RecyclerView, yang sepertinya akan memenuhi apa yang Anda butuhkan (mengatur offset ke 0 harus sejajar dengan bagian atas).

Misalnya:

//Scroll item 2 to 20 pixels from the top
linearLayoutManager.scrollToPositionWithOffset(2, 20);
Niraj
sumber
34
Jika ada yang membutuhkan ini untuk memulihkan posisi gulir dari RecyclerView, ini adalah bagaimana cara menyimpan posisi gulir (dua argumen untuk metode scrollToPositionWithOffset): int index = linearLayoutManager.findFirstVisibleItemPosition (); Lihat v = linearLayoutManager.getChildAt (0); int top = (v == null)? 0: (v.getTop () - linearLayoutManager.getPaddingTop ());
DominicM
Yang sepertinya tidak saya dapatkan tentang ini adalah kapan harus memanggil scrollToPosition? jika pendengar seleksi ada pada tampilan individual, bagaimana objek RecyclerView (yang memiliki akses ke manajer tata letaknya yang dapat memanggil scrolToPosition) diberitahu bahwa item dipilih?
Nonos
@Tidak, Anda pertama-tama dapat menyimpan LayoutManager yang Anda gunakan di RecylerView.setLayoutManager () dan kemudian mengaksesnya langsung. Seperti ini: LinearLayoutManager llm = new LinearLayoutManager (this); mRecyclerView.setLayoutManager (llm); ... (nanti dalam kode) ... llm.scrollToPositionWithOffset (2, 20);
Niraj
19
Bagaimana jika saya ingin menggulir ke atas "dengan lancar"?
Tiago
@Tiago, saya belum bermain-main dengannya, tapi seri artikel 3-bagian ini dapat membantu: blog.stylingandroid.com/scrolling-recyclerview-part-1
Niraj
93

Jika Anda mencari LinearLayout Manager vertikal, Anda dapat menggulir dengan lancar menggunakan custom 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;
        }
    }
}

gunakan instance dari layoutmanager dalam tampilan daur ulang dan kemudian panggilan recyclerView.smoothScrollToPosition(pos);akan memuluskan gulir ke posisi yang dipilih ke atas tampilan pendaur ulang

nizammoidu
sumber
1
Jawaban yang sangat berguna! Saya juga mengganti getHorizontalSnapPreference()di TopSnappedSmoothScroller untuk daftar Horizontal.
Juan Saravia
6
Apakah saya benar dalam mengamati bahwa ini hanya berfungsi untuk barang-barang yang masih memiliki cukup setelah / di bawahnya untuk mengisi sisanya RecyclerView? Artinya: Ini tampaknya tidak berfungsi untuk item terakhir - terlepas dari satu SNAP_TO_START- RecyclerViewsatunya gulungan sampai item menjadi terlihat di bagian bawah tetapi tidak bergerak ke atas. Adakah ide bagaimana mencapai ini? (Saya telah mengatasi ini dengan menetapkan bantalan kustom pada RecyclerViewtapi itu sepertinya tidak benar ...)
david.mihola
kadang-kadang dosis item menjadi fokus, saya memiliki fokus yang dapat ditarik untuk tampilan item.
YLS
1
@ david.mihola Sudahkah Anda menemukan jalan yang benar? Saya telah mengatur padding khusus ke recyclerview saya, tetapi saya mengalami beberapa masalah aneh ...
bernardo.g
52

// Gulirkan pos item

linearLayoutManager.scrollToPositionWithOffset(pos, 0);
HBQ
sumber
7
apakah ada cara untuk melakukan ini dengan lancar?
Ashkan
1
@MarkBuikema saya menemukannya sebelumnya. thanx atas perhatian Anda Bung
Ashkan
28

Anda hanya perlu menelepon recyclerview.scrollToPosition(position). Tidak apa-apa!

Jika Anda ingin menyebutnya dalam adaptor, biarkan adaptor Anda memiliki instance dari recyclerview atau aktivitas atau fragmen yang berisi recyclerview, daripada mengimplementasikan metode getRecyclerview()di dalamnya.

Saya harap ini bisa membantu Anda.

Nol
sumber
2
Ini bekerja untuk saya - recyclerView.scrollToPosition (0); menempatkan saya di atas.
Ray Hunter
2
yang tidak berfungsi Anda harus menggunakan linearLayoutManager.scrollToPositionWithOffset (pos, 0);
Anil Ugale
@Anil Ugale Itu omong kosong. Tentu saja berhasil. Anda dapat menggunakan LayoutManager untuk RecyclerView. Mengapa nanti Anda harus memperhatikannya ketika Anda ingin menggulir? Saya pikir Anda melakukan sesuatu yang salah. Recyclerview.scrollToPosition (position) adalah metode kenyamanan yang memanggil LayoutManager.scrollToPosition (position).
Jan
1
@TheincredibleJan tidak berfungsi dalam semua kasus, layoutManager umumnya bertanggung jawab atas pengguliran hanya untuk informasi Anda.
Mohammed
11

sama dengan pengatur kecepatan

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;
    }
}
}
pengguna2212515
sumber
8

Coba apa yang berhasil buatku keren!

Buat variabel private static int displayedposition = 0;

Sekarang untuk posisi RecyclerView Anda di Aktivitas Anda.

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


            }
        });

Tempatkan pernyataan ini di tempat yang Anda inginkan untuk menempatkan situs sebelumnya ditampilkan di tampilan Anda.

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

Baiklah itu saja, itu bekerja dengan baik untuk saya \ o /

pengguna3740244
sumber
7

panggil saja metode ini secara sederhana:

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

dari pada:

recyclerView.scrollToPosition(yourItemPosition);
Amir Hossein Ghasemi
sumber
"dari pada"? Distwo tidak menyebutkan "scrollToPosition". Jika dia menggunakannya, tidak akan ada masalah. scrollToPosition () sebagaimana dimaksud.
Jan
1
anehnya, menggunakan scrollToPositionWithOffset bekerja untuk saya dan recyclerview.scrollToPosition tidak. Meskipun, saya akan meminta Amir untuk mengubah RecyclerView ke recyclerview, karena itu memberi kesan bahwa metode tersebut statis, dan mereka tidak.
Mohit
6

apa yang saya lakukan untuk mengembalikan posisi gulir setelah menyegarkan tombol RecyclerView on diklik:

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

mendapatkan offset item pertama yang terlihat dari atas sebelum membuat objek linearLayoutManager dan setelah instantiating, scrollToPositionWithOffset objek LinearLayoutManager dipanggil.

tsiro
sumber
6

Saya tidak tahu mengapa saya tidak menemukan jawaban terbaik tetapi sangat sederhana.

recyclerView.smoothScrollToPosition(position);

Tidak ada kesalahan

Membuat Animasi

Lucem
sumber
di mana Anda akan menempatkan ini dalam aktivitas atau adaptor?
Arnold Brown
3

gulir pada posisi tertentu
dan ini membantu saya banyak. dengan klik pendengar Anda bisa mendapatkan posisi di adaptor Anda

layoutmanager.scrollToPosition(int position);
Sandeep Singh
sumber
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);**
Paras Santoki
sumber
3

Tidak ada jawaban yang menjelaskan cara menampilkan item terakhir di bagian atas. Jadi, jawabannya hanya berfungsi untuk item yang masih memiliki cukup item di atas atau di bawahnya untuk mengisi sisanya RecyclerView. Misalnya, jika ada 59 elemen dan elemen ke-56 dipilih itu harus di atas seperti pada gambar di bawah ini:

RecyclerView - elemen 56 dipilih Kami dapat menangani kasus-kasus tersebut dengan menggunakan linearLayoutManager.scrollToPositionWithOffset(pos, 0)dan logika tambahan ke Adapterdalam RecyclerView- dengan menambahkan margin khusus di bawah item terakhir (jika item terakhir tidak terlihat maka itu berarti ada cukup ruang untuk mengisi RecyclerView). Margin khusus dapat berupa perbedaan antara tinggi tampilan root dan tinggi item. Jadi, Adapteruntuk Anda RecyclerViewakan terlihat sebagai berikut:

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

    ...
} 
...
Anatolii
sumber
2

Dalam kasus saya, saya RecyclerViewmemiliki padding top seperti ini

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

Kemudian untuk menggulir item ke atas, saya perlu

recyclerViewLinearLayoutManager.scrollToPositionWithOffset(position, -yourRecyclerView.getPaddingTop());
Phan Van Linh
sumber
1

Jika Anda LayoutManageradalah LinearLayoutManagerAnda menggunakan scrollToPositionWithOffset(position,0);di atasnya dan itu akan membuat item Anda item terlihat pertama dalam daftar. Jika tidak, Anda dapat menggunakan smoothScrollToPositiondiRecyclerView langsung.

Saya akhirnya menggunakan kode di bawah ini.

 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);
        }
Mohamed Nageh
sumber
-2

Saya menggunakan kode di bawah ini untuk menggulirkan item (thisView) ke atas.
Ini juga berfungsi untuk GridLayoutManager dengan tampilan ketinggian berbeda:

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

Tampaknya bekerja cukup baik untuk solusi cepat.

feheren.fekete
sumber
1
Apa nilai thisPosition?
pRaNaY
itu posisi yang Anda ingin gulirkan dengan lancar
Jan Rabe
Mengapa tidak menggunakan smoothScrollToPosition (posisi int) tanpa perhitungan? Tidak masalah dari mana Anda memulai, bukan?
Luar biasa