Android:如何获取RecyclerView的当前X偏移量?

lon*_*ngi 51 android scroll position android-recyclerview

我正在使用一个Scrollview无限的"Time Picker Carousel"并发现它不是最好的方法(最后一个问题)

现在,我找到了Recycler View,但是我无法在recyclerView的X方向上获得当前的滚动偏移量?(假设每个项目的宽度为100px,第二个项目只有50%可见,因此滚动偏移量为150px)

  • 所有项目具有相同的宽度,但在第一个项目之前,在最后一个项目之后存在一些差距
  • recyclerView.getScrollX()返回0(文档说:初始滚动值)
  • LayoutManagerfindFirstVisibleItemPosition,但我不能算出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);

        }
    });
Run Code Online (Sandbox Code Playgroud)

Uma*_*shi 56

RecyclerView已经有获得水平和垂直滚动偏移的方法

mRecyclerView.computeHorizontalScrollOffset()
mRecyclerView.computeVerticalScrollOffset()
Run Code Online (Sandbox Code Playgroud)

这适用于包含相同高度(垂直滚动偏移)和相同宽度(水平滚动偏移)的单元格的RecyclerViews

  • 事实上,你的细胞不具有相同的宽度(或高度,取决于滚动方向).computeXScrollOffset使用平均单元格尺寸,因此如果您的单元格大小不同,则此方法无用.我一直在努力寻找一个合适的计算解决方案:) (21认同)
  • 这对我来说不起作用,有时在列表中滚动时跳跃几乎400px,似乎缺少某些东西 (9认同)
  • 可能会返回错误的值,不要使用它! (2认同)

lon*_*ngi 50

在此输入图像描述

解决方案1: setOnScrollListener

将您的X-Position保存为类变量并更新其中的每个更改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);

    }
 });
Run Code Online (Sandbox Code Playgroud)

解决方案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);
    
        }
    });
    
    Run Code Online (Sandbox Code Playgroud)

    }

  • 什么是`firstItemPosition` (3认同)

jbo*_*ren 33

感谢@ umar-qureshi的正确领导!看来,你可以确定与滚动百分比Offset,ExtentRange这样

percentage = 100 * offset / (range - extent)
Run Code Online (Sandbox Code Playgroud)

例如(要放入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 + "%");
Run Code Online (Sandbox Code Playgroud)


Sem*_*hor 8

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();
    }
}
Run Code Online (Sandbox Code Playgroud)

比你需要获得抵消的地方:

recyclerView.getHorizontalOffset()
Run Code Online (Sandbox Code Playgroud)

  • 这将以任意单位返回一个int,并且不能可靠地为您提供准确的滚动量; 这不是它的用途.例如,一旦我的第一个可见位置从0移动到1,它就从1850px下降到300px. (3认同)

Séb*_*ien 6

如果您需要知道当前页面及其相对于 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);
        }
    }
});
Run Code Online (Sandbox Code Playgroud)

如果currentPage索引为 0,则leftIndicator(箭头)将是透明的,rightIndicator如果只有一个page(在示例中,适配器始终至少显示一项,因此不会检查空值)。

这样一来,如果你一直在使用回调,你会基本上有几乎相同的功能pageScrolledViewPager.OnPageChangeListener