RecyclerVoir le snap horizontal dans le centre

J'essaie de faire une vue de type carrousel ici à l'aide de RecyclerView, je veux que l'élément s'arrête au milieu de l'écran lors du défilement, un élément à la fois. J'ai essayé d'utiliser recyclerView.setScrollingTouchSlop(RecyclerView.TOUCH_SLOP_PAGING);

Mais la vue est toujours en mode défilement, j'ai également essayé de mettre en œuvre ma propre logique à l'aide de l'écouteur scroll:

 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 glissement de droite à gauche fonctionne bien maintenant, mais pas l'inverse, qu'est-ce que je manque ici?

5 Solutions collect form web for “RecyclerVoir le snap horizontal dans le centre”

Avec LinearSnapHelper , c'est maintenant très simple.

Tout ce que vous devez faire, c'est ceci:

 SnapHelper helper = new LinearSnapHelper(); helper.attachToRecyclerView(recyclerView); 

Mettre à jour

Disponible depuis 25.1.0, PagerSnapHelper peut aider à atteindre ViewPager comme effet. Utilisez-le comme vous voudriez utiliser LinearSnapHelper .

Ancienne solution de contournement:

Si vous souhaitez qu'il se comporte comme le ViewPager , essayez ceci à la place:

 LinearSnapHelper snapHelper = new LinearSnapHelper() { @Override public int findTargetSnapPosition(RecyclerView.LayoutManager layoutManager, int velocityX, int velocityY) { View centerView = findSnapView(layoutManager); if (centerView == null) return RecyclerView.NO_POSITION; int position = layoutManager.getPosition(centerView); int targetPosition = -1; if (layoutManager.canScrollHorizontally()) { if (velocityX < 0) { targetPosition = position - 1; } else { targetPosition = position + 1; } } if (layoutManager.canScrollVertically()) { if (velocityY < 0) { targetPosition = position - 1; } else { targetPosition = position + 1; } } final int firstItem = 0; final int lastItem = layoutManager.getItemCount() - 1; targetPosition = Math.min(lastItem, Math.max(targetPosition, firstItem)); return targetPosition; } }; snapHelper.attachToRecyclerView(recyclerView); 

La mise en œuvre ci-dessus renvoie simplement la position à côté de l'élément actuel (centrée) en fonction de la direction de la vitesse, quelle que soit la grandeur.

L'ancien est une solution de première partie incluse dans la bibliothèque de support 24.2.0. Cela signifie que vous devez l'ajouter à build.gradle de votre module d' build.gradle ou le mettre à jour.

 compile "com.android.support:recyclerview-v7:24.2.0" 

Mise à jour importante:

Google a mis en œuvre ceci dans la bibliothèque d'assistance Android 24.2.0 (publié en août 2016). Les notes de sortie disent ceci:

RecyclerView.OnFlingListener a été ajouté pour prendre en charge les comportements personnalisés en réponse aux lancers. La classe SnapHelper fournit une implémentation spécifiquement pour étaler des vues enfants et la classe LinearSnapHelper étend cette implémentation pour fournir un comportement d'accrochage aligné au centre similaire à ViewPager .

Notez que je n'ai pas encore essayé d'utiliser LinearSnapHelper . J'utilise toujours ma solution, qui fonctionne bien.

Réponse originale:

Après plusieurs heures d'essai de 3 solutions différentes trouvées ici dans SO, j'ai finalement construit une solution qui imite très étroitement le comportement trouvé dans un ViewPager .

La solution est basée sur la solution @eDizzle, qui, je crois, j'ai suffisamment amélioré pour dire que cela fonctionne presque comme un ViewPager .

Voici comment cela se présente comme suit:

Important: la largeur de mes éléments RecyclerView est exactement la même que celle de l'écran. Je n'ai pas essayé avec d'autres tailles. De même, je l'utilise avec un LinearLayoutManager horizontal. Je pense que vous devrez adapter le code si vous voulez le 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!

Vous devez utiliser findFirstVisibleItemPosition pour aller dans la direction opposée. Et pour détecter la direction dans laquelle se trouvait le glissement, vous devrez obtenir soit la vitesse de fling ou 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; } 

Si l'objectif est de rendre le RecyclerView imiter le comportement de ViewPager il existe une approche assez simple

 RecyclerView 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 comme ViewPager

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éfinis le décalage requis pour centrer les éléments. Tous mes articles ont la même largeur que le décalage constant est ok

coAndroid est un fan Android de Google, tout sur les téléphones Android, Android Wear, Android Dev et Android Games Apps.