Ich verwende eine Scrollview
für ein unendliches "Time Picker Carousel" und habe herausgefunden, dass es nicht der beste Ansatz ist ( letzte Frage )
Nun habe ich die Recycler View gefunden aber Ich kann den aktuellen Scroll-Offset in X-Richtung der RecyclerView nicht ermitteln(Nehmen wir an, jedes Element hat eine Breite von 100px und das zweite Element ist nur zu 50% sichtbar, der Scroll-Offset beträgt also 150px.)
recyclerView.getScrollX()
gibt 0
zurück (Dokumente sagen: anfänglicher Bildlaufwert)LayoutManager
hat findFirstVisibleItemPosition
, aber ich kann den X-Versatz damit nicht berechnenUPDATE
Ich habe gerade einen Weg gefunden, die X-Position weiter zu verfolgen, während der Wert mit dem onScrolled
-Callback aktualisiert wird, aber ich würde es vorziehen, den tatsächlichen Wert zu erhalten, anstatt ihn ständig zu verfolgen.
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);
}
});
Lösung 1: setOnScrollListener
Speichern Sie Ihre X-Position als Klassenvariable und aktualisieren Sie jede Änderung innerhalb der onScrollListener
. Stellen Sie sicher, dass Sie nicht overallXScroll
zurücksetzen (z. B. 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);
}
});
Lösung 2: Aktuelle Position berechnen.
In meinem Fall habe ich eine horizontale Liste, die mit Zeitwerten gefüllt ist (0:00, 0:30, 1:00, 1:30 ... 23:00, 23:30 Uhr). Ich berechne die Zeit aus der Zeitposition, die sich in der Mitte des Bildschirms befindet (Berechnungspunkt). Deshalb brauche ich die exakte X-Scroll-Position meiner RecycleView
Der erste Eintrag (Kopfzeile) verfügt über eine zusätzliche Auffüllung, um 0 Minuten auf die Mitte einzustellen
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 verfügt bereits über eine Methode zum horizontalen und vertikalen Bildlaufversatz
mRecyclerView.computeHorizontalScrollOffset()
mRecyclerView.computeVerticalScrollOffset()
Dies funktioniert für RecyclerViews, die Zellen derselben Höhe (für vertikalen Bildlaufversatz) und derselben Breite (für horizontalen Bildlaufversatz) enthalten.
Danke an @ umar-qureshi für die richtige Führung! Es scheint, dass Sie den Scroll-Prozentsatz mit Offset
, Extent
und Range
so bestimmen können
percentage = 100 * offset / (range - extent)
Zum Beispiel (in eine OnScrollListener
einfügen):
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();
}
}
Dann wo Sie den Offset benötigen:
recyclerView.getHorizontalOffset()
Wenn Sie die aktuelle Seite und ihren Versatz im Vergleich zur Breite von RecyclerView kennen müssen, versuchen Sie Folgendes:
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);
}
}
});
Wenn currentPage
den Index 0 hat, ist leftIndicator
(Pfeil) transparent, und rightIndicator
, wenn nur eine page
vorhanden ist (im Beispiel hat der Adapter immer mindestens ein Element, das angezeigt werden muss, sodass der leere Wert nicht geprüft wird).
Auf diese Weise haben Sie praktisch die gleiche Funktionalität, als wenn Sie den Rückruf pageScrolled
von ViewPager.OnPageChangeListener
verwendet hätten.
durch Überschreiben von RecyclerView onScrolled(int dx,int dy)
können Sie den Scroll-Offset ermitteln. Wenn Sie jedoch das Element hinzufügen oder entfernen, kann der Wert falsch sein.
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;
}
}