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

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

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

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

ОБНОВИТЬ

Я просто нашел способ отслеживать X-Position, обновляя значение с onScrolled обратного вызова 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); } }); 

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

 mRecyclerView.computeHorizontalScrollOffset() mRecyclerView.computeVerticalScrollOffset() 

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

введите описание изображения здесь

Решение 1: setOnScrollListener

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

    }

Спасибо @ 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 + "%"); 
 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() 

путем переопределения 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's data set instead of an explicit listener. * 

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.

*/ @Override public void onScrolled(int dx, int dy) { // super.onScrolled(dx, dy); offsetX+=dx; offsetY+=dy; } }

Если вам нужно знать текущую страницу и ее смещение по сравнению с шириной RecyclerView, вы можете попробовать что-то вроде этого:

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

Если currentPage имеет индекс 0, тогда leftIndicator (стрелка) будет прозрачным, и так будет и rightIndicator если есть только одна page (в примере у адаптера всегда есть хотя бы один элемент, чтобы показать, чтобы не было проверки на пустое значение).

Таким образом, у вас будет практически такая же функциональность, как если бы вы использовали pageScrolled обратного вызова, pageScrolled из ViewPager.OnPageChangeListener .

Давайте будем гением компьютера.