J'essaie de créer une vue de type carrousel ici à l'aide de RecyclerView, je veux que l'élément s'emboîte au milieu de l'écran lors du défilement, un élément à la fois. J'ai essayé d'utiliserrecyclerView.setScrollingTouchSlop(RecyclerView.TOUCH_SLOP_PAGING);
mais la vue défile toujours en douceur, j'ai également essayé d'implémenter ma propre logique en utilisant un écouteur de défilement comme ceci:
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();
}
}
Le balayage de droite à gauche fonctionne correctement maintenant, mais pas l'inverse, que me manque-t-il ici?
la source
LinearLayoutManager
où les vues étaient toutes de taille régulière. Rien d'autre que l'extrait ci-dessus n'est nécessaire.LinearSnapHelper
.Mise à jour Google I / O 2019
ViewPager2 est là!
Google vient d'annoncer lors de la conférence «Quoi de neuf dans Android» (alias «La keynote Android») qu'ils travaillaient sur un nouveau ViewPager basé sur RecyclerView!
À partir des diapositives:
Vous pouvez consulter la dernière version ici et les notes de version ici . Il existe également un échantillon officiel .
Opinion personnelle: je pense que c'est un ajout vraiment nécessaire. J'ai récemment eu beaucoup de problèmes avec l'
PagerSnapHelper
oscillation gauche droite indéfiniment - voir le ticket que j'ai ouvert.Nouvelle réponse (2016)
Vous pouvez maintenant simplement utiliser un SnapHelper .
Si vous voulez un comportement d' accrochage centré similaire à ViewPager, utilisez PagerSnapHelper :
SnapHelper snapHelper = new PagerSnapHelper(); snapHelper.attachToRecyclerView(recyclerView);
Il existe également un LinearSnapHelper . Je l'ai essayé et si vous vous lancez avec de l'énergie, il fait défiler 2 éléments avec 1 jet. Personnellement, je n'ai pas aimé, mais décidez par vous-même - l'essayer ne prend que quelques secondes.
Réponse originale (2016)
Après de nombreuses heures à essayer 3 solutions différentes trouvées ici dans SO, j'ai finalement construit une solution qui imite de très près le comportement trouvé dans un fichier
ViewPager
.La solution est basée sur la solution @eDizzle , que je pense avoir suffisamment améliorée pour dire qu'elle fonctionne presque comme un
ViewPager
.Important: la
RecyclerView
largeur de mes éléments est exactement la même que celle de l'écran. Je n'ai pas essayé avec d'autres tailles. Aussi je l'utilise avec un horizontalLinearLayoutManager
. Je pense que vous devrez adapter le code si vous voulez un défilement vertical.Ici vous avez le code:
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); } } } }
Prendre plaisir!
la source
Si le but est de faire
RecyclerView
mimer le comportement,ViewPager
il y a une approche assez simpleRecyclerView 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);
En utilisant,
PagerSnapHelper
vous pouvez obtenir le comportement commeViewPager
la source
LinearSnapHelper
place dePagerSnapHelper
et cela fonctionne pour moiLinearSnapHelper
LinearSnapHelper
, et il s'aligne sur un élément du milieu.PagerSnapHelper
empêche de faire défiler facilement (au moins, une liste d'images).Vous devez utiliser findFirstVisibleItemPosition pour aller dans la direction opposée. Et pour détecter la direction dans laquelle le balayage était, vous devrez obtenir soit la vitesse de jet, soit le changement de x. J'ai abordé ce problème sous un angle légèrement différent de celui que vous avez.
Créez une nouvelle classe qui étend la classe RecyclerView, puis remplacez la méthode fling de RecyclerView comme suit:
@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; }
la source
Ajoutez simplement
padding
etmargin
àrecyclerView
etrecyclerView item
:recyclerAfficher l'article:
<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" />
et définissez
PagerSnapHelper
:int displayWidth = Resources.getSystem().getDisplayMetrics().widthPixels; parentLayout.getLayoutParams().width = displayWidth - Utils.dpToPx(16) * 4; SnapHelper snapHelper = new PagerSnapHelper(); snapHelper.attachToRecyclerView(recyclerView);
dp en px:
public static int dpToPx(int dp) { return (int) TypedValue.applyDimension(TypedValue.COMPLEX_UNIT_DIP, dp, Resources.getSystem().getDisplayMetrics()); }
résultat:
la source
Ma solution:
/** * 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; } } }
Je passe ce gestionnaire de mise en page à RecycledView et définit le décalage requis pour centrer les éléments. Tous mes articles ont la même largeur, donc le décalage constant est correct
la source
PagerSnapHelper
ne fonctionne pasGridLayoutManager
avec spanCount> 1, donc ma solution dans cette circonstance est: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 } }
la source