Android: Как получить текущее смещение X для RecyclerView?

Я использую Scrollview для бесконечной "Карусели таймера" и выяснил, что это не лучший подход (последний вопрос)

Теперь я нашел Recycler View, но Я не могу получить текущее смещение прокрутки в направлении X recyclerView? (Пусть говорят, что каждый элемент имеет ширину 100 пикселей, а второй - только на 50%, поэтому смещение прокрутки составляет 150 пикселей)

  • все элементы имеют одинаковую ширину, но therer - некоторый пробел перед первым, после последнего элемента
  • recyclerView.getScrollX() возвращает 0 (говорят: начальное значение прокрутки)
  • LayoutManager имеет findFirstVisibleItemPosition, но я не могу вычислить смещение X с этим

UPDATE

Я просто нашел способ отслеживать X-Position, обновляя значение с помощью обратного вызова onScrolled, но я предпочел бы получать фактическое значение, а не отслеживать его все время!

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

        }
    });

Ответы

Ответ 1

enter image description here

Решение 1: setOnScrollListener

Сохраните переменную X-Position как переменную класса и обновите каждое изменение в onScrollListener. Убедитесь, что вы не reset 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);

    }
 });

Решение 2: Рассчитать текущую позицию.

В моем случае у меня есть горизонтальный список, который заполняется значениями времени (0:00, 0:30, 1:00, 1:30... 23:00, 23:30). Я вычисляю время с момента времени, которое находится посередине экрана (точка вычисления). Вот почему мне нужна точная позиция X-Scroll моего RecycleView

  • Каждый элемент времени имеет ту же ширину 60dp (fyi: 60dp = 30min, 2dp = 1min)
  • Первый элемент (элемент заголовка) имеет дополнительное дополнение, чтобы установить 0мин в центр

    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 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 why I have to subtract it again)
                leftScrollXCalculated = (mHeaderItemWidth - mCellWidth) + firstItemPosition  * mCellWidth + firstVisibleItem.getLeft();
            }
    
            Log.i("asdf","calculated X to left = " + leftScrollXCalculated);
    
        }
    });
    

    }

Ответ 2

RecyclerView уже имеет способ получить горизонтальное и вертикальное смещение прокрутки

mRecyclerView.computeHorizontalScrollOffset()
mRecyclerView.computeVerticalScrollOffset()

Это будет работать для RecyclerViews, содержащих ячейки с одинаковой высотой (для вертикального сдвига прокрутки) и той же ширины (для горизонтального сдвига прокрутки)

Ответ 3

Спасибо @umar-qureshi за правильный лидер! По-видимому, вы можете определить процент прокрутки с помощью Offset, Extent и Range, чтобы

percentage = 100 * offset / (range - extent)

Например (для размещения 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 + "%");

Ответ 4

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

Чем вам нужно получить смещение:

recyclerView.getHorizontalOffset()

Ответ 5

переопределив RecyclerView onScrolled(int dx,int dy), вы можете получить смещение прокрутки. но когда вы добавляете или удаляете элемент, значение может быть неправильным.

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