J'utilise une Scrollview
pour un "carrousel de sélecteurs de temps" infini et j'ai découvert que ce n'était pas la meilleure approche ( dernière question )
Maintenant, j'ai trouvé le Recycler View mais je ne peux pas obtenir le décalage de défilement actuel dans la direction X du recyclerView? (Disons que chaque élément a une largeur de 100 pixels et que le deuxième élément n'est visible que jusqu'à 50%, le décalage de défilement est donc de 150 pixels)
recyclerView.getScrollX()
renvoie 0
(les docs disent: valeur de défilement initiale)LayoutManager
a findFirstVisibleItemPosition
, mais je ne peux pas calculer le décalage X avec celaMETTRE À JOUR
Je viens de trouver un moyen de suivre la position X tout en mettant à jour la valeur avec le callback onScrolled
, mais je préférerais obtenir la valeur réelle au lieu de la suivre tout le temps!
private int overallXScroll = 0;
//...
mRecyclerView.setOnScrollListener(new RecyclerView.OnScrollListener() {
@Override
public void onScrolled(RecyclerView recyclerView, int dx, int dy) {
super.onScrolled(recyclerView, dx, dy);
overallXScroll = overallXScroll + dx;
Log.i("check","overallXScroll->" + overallXScroll);
}
});
Solution 1: setOnScrollListener
Enregistrez votre position X en tant que variable de classe et mettez à jour chaque modification dans la variable onScrollListener
. Assurez-vous de ne pas réinitialiser overallXScroll
(f.e. onScreenRotationChange
)
private int overallXScroll = 0;
//...
mRecyclerView.setOnScrollListener(new RecyclerView.OnScrollListener() {
@Override
public void onScrolled(RecyclerView recyclerView, int dx, int dy) {
super.onScrolled(recyclerView, dx, dy);
overallXScroll = overallXScroll + dx;
Log.i("check","overall X = " + overallXScroll);
}
});
Solution 2: Calcule la position actuelle.
Dans mon cas, j'ai une liste horizontale qui contient des valeurs de temps (0:00, 0:30, 1:00, 1:30 ... 23:00, 23:30). Je calcule l'heure à partir de l'élément temporel, qui se trouve au milieu de l'écran (point de calcul). C'est pourquoi j'ai besoin de la position exacte de X-Scroll de ma RecycleView
Le premier élément (élément d'en-tête) a un rembourrage supplémentaire, pour définir 0min au centre
private int mScreenWidth = 0;
private int mHeaderItemWidth = 0;
private int mCellWidth = 0;
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
//init recycle views
//...
LinearLayoutManager mLLM = (LinearLayoutManager) getLayoutManager();
DisplayMetrics displaymetrics = new DisplayMetrics();
this.getWindowManager().getDefaultDisplay().getMetrics(displaymetrics);
this.mScreenWidth = displaymetrics.widthPixels;
//calculate value on current device
mCellWidth = (int) TypedValue.applyDimension(TypedValue.COMPLEX_UNIT_DIP, 60, getResources()
.getDisplayMetrics());
//get offset of list to the right (gap to the left of the screen from the left side of first item)
final int mOffset = (this.mScreenWidth / 2) - (mCellWidth / 2);
//HeaderItem width (blue rectangle in graphic)
mHeaderItemWidth = mOffset + mCellWidth;
mRecyclerView.setOnScrollListener(new RecyclerView.OnScrollListener() {
@Override
public void onScrolled(RecyclerView recyclerView, int dx, int dy) {
super.onScrolled(recyclerView, dx, dy);
//get first visible item
View firstVisibleItem = mLLM.findViewByPosition(mLLM.findFirstVisibleItemPosition());
int leftScrollXCalculated = 0;
if (firstItemPosition == 0){
//if first item, get width of headerview (getLeft() < 0, that's why I Use Math.abs())
leftScrollXCalculated = Math.abs(firstVisibleItem.getLeft());
}
else{
//X-Position = Gap to the right + Number of cells * width - cell offset of current first visible item
//(mHeaderItemWidth includes already width of one cell, that's why I have to subtract it again)
leftScrollXCalculated = (mHeaderItemWidth - mCellWidth) + firstItemPosition * mCellWidth + firstVisibleItem.getLeft();
}
Log.i("asdf","calculated X to left = " + leftScrollXCalculated);
}
});
}
RecyclerView a déjà une méthode pour obtenir un décalage de défilement horizontal et vertical
mRecyclerView.computeHorizontalScrollOffset()
mRecyclerView.computeVerticalScrollOffset()
Cela fonctionnera pour RecyclerViews contenant des cellules de même hauteur (pour le décalage de défilement vertical) et de même largeur (pour le décalage de défilement horizontal).
Merci à @ umar-qureshi pour la bonne avance! Il semble que vous puissiez déterminer le pourcentage de défilement avec Offset
, Extent
et Range
tels que
percentage = 100 * offset / (range - extent)
Par exemple (à mettre dans une OnScrollListener
):
int offset = recyclerView.computeVerticalScrollOffset();
int extent = recyclerView.computeVerticalScrollExtent();
int range = recyclerView.computeVerticalScrollRange();
int percentage = (int)(100.0 * offset / (float)(range - extent));
Log.i("RecyclerView, "scroll percentage: "+ percentage + "%");
public class CustomRecyclerView extends RecyclerView {
public CustomRecyclerView(Context context) {
super(context);
}
public CustomRecyclerView(Context context, AttributeSet attrs) {
super(context, attrs);
}
public CustomRecyclerView(Context context, AttributeSet attrs, int defStyle) {
super(context, attrs, defStyle);
}
public int getHorizontalOffset() {
return super.computeHorizontalScrollOffset();
}
}
Là où vous devez obtenir le décalage:
recyclerView.getHorizontalOffset()
Si vous avez besoin de connaître la page actuelle et son décalage par rapport à la largeur de RecyclerView, vous pouvez essayer quelque chose comme ceci:
recyclerView.addOnScrollListener(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);
final int scrollOffset = recyclerView.computeHorizontalScrollOffset();
final int width = recyclerView.getWidth();
final int currentPage = scrollOffset / width;
final float pageOffset = (float) (scrollOffset % width) / width;
// the following lines just the example of how you can use it
if (currentPage == 0) {
leftIndicator.setAlpha(pageOffset);
}
if (adapter.getItemCount() <= 1) {
rightIndicator.setAlpha(pageOffset);
} else if (currentPage == adapter.getItemCount() - 2) {
rightIndicator.setAlpha(1f - pageOffset);
}
}
});
Si currentPage
a pour index 0 alors leftIndicator
(flèche) sera transparent, il en sera de même pour rightIndicator
s'il n'y a qu'une page
(dans l'exemple, l'adaptateur a toujours au moins un élément à afficher, de sorte qu'il n'y a pas de contrôle pour la valeur vide).
De cette façon, vous aurez pratiquement les mêmes fonctionnalités que si vous utilisiez le callback pageScrolled
from ViewPager.OnPageChangeListener
.
en remplaçant RecyclerView onScrolled(int dx,int dy)
, vous pouvez obtenir le décalage de défilement. mais lorsque vous ajoutez ou supprimez l'élément, la valeur peut être fausse.
public class TempRecyclerView extends RecyclerView {
private int offsetX = 0;
private int offsetY = 0;
public TempRecyclerView(Context context) {
super(context);
}
public TempRecyclerView(Context context, @Nullable AttributeSet attrs) {
super(context, attrs);
}
public TempRecyclerView(Context context, @Nullable AttributeSet attrs,int defStyle){
super(context, attrs, defStyle);
}
/**
* Called when the scroll position of this RecyclerView changes. Subclasses
should usethis method to respond to scrolling within the adapter's data
set instead of an explicit listener.
* <p>This method will always be invoked before listeners. If a subclass
needs to perform any additional upkeep or bookkeeping after scrolling but
before listeners run, this is a good place to do so.</p>
*/
@Override
public void onScrolled(int dx, int dy) {
// super.onScrolled(dx, dy);
offsetX+=dx;
offsetY+=dy;
}
}