it-swarm.com.de

Android: Wie bekomme ich den aktuellen X-Offset von RecyclerView?

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.)

  • alle Elemente haben die gleiche Breite, es gibt jedoch eine Lücke vor dem ersten, nach dem letzten Element
  • recyclerView.getScrollX() gibt 0 zurück (Dokumente sagen: anfänglicher Bildlaufwert)
  • LayoutManager hat findFirstVisibleItemPosition, aber ich kann den X-Versatz damit nicht berechnen

UPDATE

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);

        }
    });
47
longilong

enter image description here

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

  • Jedes Element hat die gleiche Breite von 60dp (fyi: 60dp = 30min, 2dp = 1min)
  • 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);
    
        }
    });
    

    }

45
longilong

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.

49
Umar Qureshi

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 + "%");
26
jbohren
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()
7

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.

2
Sébastien

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;
     }
 }
0
chen