Android: jak uzyskać bieżące przesunięcie X RecyclerView?

Używam Scrollview do nieskończonej "karuzeli wybierającej czas" i okazało się, że nie jest to najlepsze podejście (ostatnie pytanie )

Teraz znalazłem widok Recyclera ale nie jestem w stanie uzyskać bieżącego przesunięcia przewijania w kierunku X widoku recyclerView? (Załóżmy, że każdy element ma szerokość 100px, a drugi element jest widoczny tylko do 50%, więc przesunięcie przewijania wynosi 150px)

  • Wszystkie elementy mają tę samą szerokość, ale jest jakaś przerwa przed pierwszą, po ostatniej pozycji
  • recyclerView.getScrollX() zwraca 0 (dokumenty mówią: początkowa wartość przewijania)
  • LayoutManager mA findFirstVisibleItemPosition, ale nie mogę obliczyć przesunięcia X z tym

UPDATE

Właśnie znalazłem sposób na śledzenie pozycji X, aktualizując wartość za pomocą wywołania zwrotnego onScrolled, ale wolałbym uzyskać rzeczywistą wartość zamiast śledzić ją cały czas!

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

        }
    });
Author: Community, 2014-12-16

6 answers

RecyclerView ma już metodę, aby uzyskać poziome i pionowe przesunięcie przewijania

mRecyclerView.computeHorizontalScrollOffset()
mRecyclerView.computeVerticalScrollOffset()

Będzie to działać dla Recyclerviewów zawierających komórki o tej samej wysokości (dla pionowego przesunięcia przewijania) i tej samej szerokości (dla poziomego przesunięcia przewijania)

 62
Author: Umar Qureshi,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/doraprojects.net/template/agent.layouts/content.php on line 54
2016-09-07 08:11:11

Tutaj wpisz opis obrazka

Rozwiązanie 1: setOnScrollListener

Zapisz swoją pozycję X jako zmienną klasy i aktualizuj każdą zmianę w onScrollListener. Upewnij się, że nie zresetujesz 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);

    }
 });

Rozwiązanie 2: Oblicz aktualną pozycję.

W moim przypadku, mam poziomą listę, która jest wypełniona wartościami czasu(0:00, 0:30, 1:00, 1:30 ... 23:00, 23:30). Obliczam czas z pozycji czasu, która znajduje się na środku ekranu (punkt obliczeniowy). Dlatego ja potrzebujesz dokładnej pozycji X-scrolla mojego RecycleView

  • każdy element czasu ma tę samą szerokość 60dp (fyi: 60dp = 30min, 2DP = 1min)
  • Pierwszy element (element nagłówka) ma dodatkowe wypełnienie, aby ustawić 0min na środku

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

    }

 58
Author: longi,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/doraprojects.net/template/agent.layouts/content.php on line 54
2017-07-26 08:28:15

Podziękowania dla @ umar-qureshi za właściwą przewagę! Wygląda na to, że można określić procent przewijania za pomocą Offset, Extent, i Range takie, że

percentage = 100 * offset / (range - extent)

Na przykład (do umieszczenia w 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 + "%");
 36
Author: jbohren,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/doraprojects.net/template/agent.layouts/content.php on line 54
2016-05-08 02:21:57
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();
    }
}

Niż gdzie trzeba uzyskać offset:

recyclerView.getHorizontalOffset()
 8
Author: SemaphoreMetaphor,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/doraprojects.net/template/agent.layouts/content.php on line 54
2015-02-20 22:48:27

Jeśli chcesz znać bieżącą stronę i jej przesunięcie w stosunku do szerokości RecyclerView, możesz spróbować czegoś takiego:

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

Jeśli currentPage ma indeks 0, to leftIndicator (strzałka) będzie przezroczysta, podobnie będzie i rightIndicator jeśli jest tylko jeden page (w przykładzie adapter zawsze ma co najmniej jeden element do pokazania, więc nie ma sprawdzania pustej wartości).

W ten sposób będziesz w zasadzie miał prawie taką samą funkcjonalność, jak gdybyś używał callback pageScrolled od ViewPager.OnPageChangeListener.

 6
Author: Sébastien,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/doraprojects.net/template/agent.layouts/content.php on line 54
2018-07-17 00:46:57

Poprzez nadpisanie RecyclerView onScrolled(int dx,int dy), można uzyskać przesunięcie przewijania. ale gdy dodasz lub usuniesz element, wartość może być nieprawidłowa.

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;
     }
 }
 2
Author: chen,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/doraprojects.net/template/agent.layouts/content.php on line 54
2017-12-30 06:42:41