Saya mencoba membuat tampilan seperti korsel di sini menggunakan RecyclerView, saya ingin item dipasang di tengah layar saat menggulir, satu item dalam satu waktu. Saya sudah mencoba menggunakanrecyclerView.setScrollingTouchSlop(RecyclerView.TOUCH_SLOP_PAGING);
tetapi tampilan masih bergulir dengan lancar, saya juga mencoba menerapkan logika saya sendiri menggunakan pendengar gulir seperti:
recyclerView.setOnScrollListener(new OnScrollListener() {
@Override
public void onScrollStateChanged(RecyclerView recyclerView, int newState) {
super.onScrollStateChanged(recyclerView, newState);
Log.v("Offset ", recyclerView.getWidth() + "");
if (newState == 0) {
try {
recyclerView.smoothScrollToPosition(layoutManager.findLastVisibleItemPosition());
recyclerView.scrollBy(20,0);
if (layoutManager.findLastVisibleItemPosition() >= recyclerView.getAdapter().getItemCount() - 1) {
Beam refresh = new Beam();
refresh.execute(createUrl());
}
} catch (Exception e) {
e.printStackTrace();
}
}
Gesekan dari kanan ke kiri berfungsi dengan baik sekarang, tetapi tidak sebaliknya, apa yang saya lewatkan di sini?
sumber
LinearLayoutManager
mana semua tampilan berukuran biasa. Tidak ada yang lain selain cuplikan di atas.LinearSnapHelper
.Pembaruan Google I / O 2019
ViewPager2 ada di sini!
Google baru saja mengumumkan pada pembicaraan 'What's New in Android' (alias 'The Android keynote') bahwa mereka sedang mengerjakan ViewPager baru berdasarkan RecyclerView!
Dari slide:
Anda dapat memeriksa versi terbaru di sini dan catatan rilisnya di sini . Ada juga sampel resmi .
Pendapat pribadi: Saya pikir ini adalah tambahan yang sangat dibutuhkan. Saya baru-baru ini mengalami banyak masalah dengan
PagerSnapHelper
kiri kanan yang berosilasi tanpa batas waktu - lihat tiket yang telah saya buka.Jawaban baru (2016)
Anda sekarang dapat menggunakan SnapHelper .
Jika Anda menginginkan perilaku gertakan rata tengah yang mirip dengan ViewPager, gunakan PagerSnapHelper :
SnapHelper snapHelper = new PagerSnapHelper(); snapHelper.attachToRecyclerView(recyclerView);
Ada juga LinearSnapHelper . Saya sudah mencobanya dan jika Anda fling dengan energi maka itu menggulung 2 item dengan 1 lemparan. Secara pribadi saya tidak menyukainya, tetapi memutuskan sendiri - mencobanya hanya membutuhkan beberapa detik.
Jawaban asli (2016)
Setelah berjam - jam mencoba 3 solusi berbeda yang ditemukan di SO, saya akhirnya membuat solusi yang sangat mirip dengan perilaku yang ditemukan di file
ViewPager
.Solusinya didasarkan pada solusi @eDizzle , yang saya yakin telah cukup saya tingkatkan untuk mengatakan bahwa itu bekerja hampir seperti
ViewPager
.Penting:
RecyclerView
lebar item saya persis sama dengan layar. Saya belum mencoba dengan ukuran lain. Saya juga menggunakannya dengan horizontalLinearLayoutManager
. Saya pikir Anda perlu menyesuaikan kode jika Anda ingin scroll vertikal.Di sini Anda memiliki kodenya:
public class SnappyRecyclerView extends RecyclerView { // Use it with a horizontal LinearLayoutManager // Based on https://stackoverflow.com/a/29171652/4034572 public SnappyRecyclerView(Context context) { super(context); } public SnappyRecyclerView(Context context, @Nullable AttributeSet attrs) { super(context, attrs); } public SnappyRecyclerView(Context context, @Nullable AttributeSet attrs, int defStyle) { super(context, attrs, defStyle); } @Override public boolean fling(int velocityX, int velocityY) { LinearLayoutManager linearLayoutManager = (LinearLayoutManager) getLayoutManager(); int screenWidth = Resources.getSystem().getDisplayMetrics().widthPixels; // views on the screen int lastVisibleItemPosition = linearLayoutManager.findLastVisibleItemPosition(); View lastView = linearLayoutManager.findViewByPosition(lastVisibleItemPosition); int firstVisibleItemPosition = linearLayoutManager.findFirstVisibleItemPosition(); View firstView = linearLayoutManager.findViewByPosition(firstVisibleItemPosition); // distance we need to scroll int leftMargin = (screenWidth - lastView.getWidth()) / 2; int rightMargin = (screenWidth - firstView.getWidth()) / 2 + firstView.getWidth(); int leftEdge = lastView.getLeft(); int rightEdge = firstView.getRight(); int scrollDistanceLeft = leftEdge - leftMargin; int scrollDistanceRight = rightMargin - rightEdge; if (Math.abs(velocityX) < 1000) { // The fling is slow -> stay at the current page if we are less than half through, // or go to the next page if more than half through if (leftEdge > screenWidth / 2) { // go to next page smoothScrollBy(-scrollDistanceRight, 0); } else if (rightEdge < screenWidth / 2) { // go to next page smoothScrollBy(scrollDistanceLeft, 0); } else { // stay at current page if (velocityX > 0) { smoothScrollBy(-scrollDistanceRight, 0); } else { smoothScrollBy(scrollDistanceLeft, 0); } } return true; } else { // The fling is fast -> go to next page if (velocityX > 0) { smoothScrollBy(scrollDistanceLeft, 0); } else { smoothScrollBy(-scrollDistanceRight, 0); } return true; } } @Override public void onScrollStateChanged(int state) { super.onScrollStateChanged(state); // If you tap on the phone while the RecyclerView is scrolling it will stop in the middle. // This code fixes this. This code is not strictly necessary but it improves the behaviour. if (state == SCROLL_STATE_IDLE) { LinearLayoutManager linearLayoutManager = (LinearLayoutManager) getLayoutManager(); int screenWidth = Resources.getSystem().getDisplayMetrics().widthPixels; // views on the screen int lastVisibleItemPosition = linearLayoutManager.findLastVisibleItemPosition(); View lastView = linearLayoutManager.findViewByPosition(lastVisibleItemPosition); int firstVisibleItemPosition = linearLayoutManager.findFirstVisibleItemPosition(); View firstView = linearLayoutManager.findViewByPosition(firstVisibleItemPosition); // distance we need to scroll int leftMargin = (screenWidth - lastView.getWidth()) / 2; int rightMargin = (screenWidth - firstView.getWidth()) / 2 + firstView.getWidth(); int leftEdge = lastView.getLeft(); int rightEdge = firstView.getRight(); int scrollDistanceLeft = leftEdge - leftMargin; int scrollDistanceRight = rightMargin - rightEdge; if (leftEdge > screenWidth / 2) { smoothScrollBy(-scrollDistanceRight, 0); } else if (rightEdge < screenWidth / 2) { smoothScrollBy(scrollDistanceLeft, 0); } } } }
Nikmati!
sumber
Jika tujuannya adalah untuk
RecyclerView
meniru perilaku,ViewPager
ada pendekatan yang cukup mudahRecyclerView recyclerView = (RecyclerView) view.findViewById(R.id.recycler_view); LinearLayoutManager layoutManager = new LinearLayoutManager(context, LinearLayoutManager.HORIZONTAL, false); SnapHelper snapHelper = new PagerSnapHelper(); recyclerView.setLayoutManager(layoutManager); snapHelper.attachToRecyclerView(mRecyclerView);
Dengan menggunakan
PagerSnapHelper
Anda bisa mendapatkan perilaku sepertiViewPager
sumber
LinearSnapHelper
alih-alihPagerSnapHelper
dan berfungsi untuk sayaLinearSnapHelper
LinearSnapHelper
, dan itu masuk ke item tengah.PagerSnapHelper
penghalang untuk menggulir dengan mudah (setidaknya, daftar gambar).Anda perlu menggunakan findFirstVisibleItemPosition untuk pergi ke arah yang berlawanan. Dan untuk mendeteksi ke arah mana gesekan itu masuk, Anda harus mendapatkan kecepatan lemparan atau perubahan x. Saya mendekati masalah ini dari sudut yang sedikit berbeda dari yang Anda miliki.
Buat kelas baru yang memperluas kelas RecyclerView lalu ganti metode melempar RecyclerView seperti ini:
@Override public boolean fling(int velocityX, int velocityY) { LinearLayoutManager linearLayoutManager = (LinearLayoutManager) getLayoutManager(); //these four variables identify the views you see on screen. int lastVisibleView = linearLayoutManager.findLastVisibleItemPosition(); int firstVisibleView = linearLayoutManager.findFirstVisibleItemPosition(); View firstView = linearLayoutManager.findViewByPosition(firstVisibleView); View lastView = linearLayoutManager.findViewByPosition(lastVisibleView); //these variables get the distance you need to scroll in order to center your views. //my views have variable sizes, so I need to calculate side margins separately. //note the subtle difference in how right and left margins are calculated, as well as //the resulting scroll distances. int leftMargin = (screenWidth - lastView.getWidth()) / 2; int rightMargin = (screenWidth - firstView.getWidth()) / 2 + firstView.getWidth(); int leftEdge = lastView.getLeft(); int rightEdge = firstView.getRight(); int scrollDistanceLeft = leftEdge - leftMargin; int scrollDistanceRight = rightMargin - rightEdge; //if(user swipes to the left) if(velocityX > 0) smoothScrollBy(scrollDistanceLeft, 0); else smoothScrollBy(-scrollDistanceRight, 0); return true; }
sumber
Cukup tambahkan
padding
danmargin
kerecyclerView
danrecyclerView item
:recyclerView item:
<RelativeLayout xmlns:android="http://schemas.android.com/apk/res/android" android:id="@+id/parentLayout" xmlns:app="http://schemas.android.com/apk/res-auto" xmlns:tools="http://schemas.android.com/tools" android:layout_marginLeft="8dp" <!-- here --> android:layout_marginRight="8dp" <!-- here --> android:layout_width="match_parent" android:layout_height="200dp"> <!-- child views --> </RelativeLayout>
recyclerView:
<androidx.recyclerview.widget.RecyclerView android:layout_width="match_parent" android:layout_height="wrap_content" android:paddingLeft="8dp" <!-- here --> android:paddingRight="8dp" <!-- here --> android:clipToPadding="false" <!-- important!--> android:scrollbars="none" />
dan setel
PagerSnapHelper
:int displayWidth = Resources.getSystem().getDisplayMetrics().widthPixels; parentLayout.getLayoutParams().width = displayWidth - Utils.dpToPx(16) * 4; SnapHelper snapHelper = new PagerSnapHelper(); snapHelper.attachToRecyclerView(recyclerView);
dp ke px:
public static int dpToPx(int dp) { return (int) TypedValue.applyDimension(TypedValue.COMPLEX_UNIT_DIP, dp, Resources.getSystem().getDisplayMetrics()); }
hasil:
sumber
Solusi saya:
/** * Horizontal linear layout manager whose smoothScrollToPosition() centers * on the target item */ class ItemLayoutManager extends LinearLayoutManager { private int centeredItemOffset; public ItemLayoutManager(Context context) { super(context, LinearLayoutManager.HORIZONTAL, false); } @Override public void smoothScrollToPosition(RecyclerView recyclerView, RecyclerView.State state, int position) { LinearSmoothScroller linearSmoothScroller = new Scroller(recyclerView.getContext()); linearSmoothScroller.setTargetPosition(position); startSmoothScroll(linearSmoothScroller); } public void setCenteredItemOffset(int centeredItemOffset) { this.centeredItemOffset = centeredItemOffset; } /** * ********** Inner Classes ********** */ private class Scroller extends LinearSmoothScroller { public Scroller(Context context) { super(context); } @Override public PointF computeScrollVectorForPosition(int targetPosition) { return ItemLayoutManager.this.computeScrollVectorForPosition(targetPosition); } @Override public int calculateDxToMakeVisible(View view, int snapPreference) { return super.calculateDxToMakeVisible(view, SNAP_TO_START) + centeredItemOffset; } } }
Saya meneruskan manajer tata letak ini ke RecycledView dan mengatur offset yang diperlukan ke item tengah. Semua item saya memiliki lebar yang sama sehingga offset konstan tidak masalah
sumber
PagerSnapHelper
tidak berfungsi denganGridLayoutManager
spanCount> 1, jadi solusi saya dalam keadaan ini adalah:class GridPagerSnapHelper : PagerSnapHelper() { override fun findTargetSnapPosition(layoutManager: RecyclerView.LayoutManager?, velocityX: Int, velocityY: Int): Int { val forwardDirection = if (layoutManager?.canScrollHorizontally() == true) { velocityX > 0 } else { velocityY > 0 } val centerPosition = super.findTargetSnapPosition(layoutManager, velocityX, velocityY) return centerPosition + if (forwardDirection) (layoutManager as GridLayoutManager).spanCount - 1 else 0 } }
sumber