Android: How to get the current X offset of RecyclerView?

前端 未结 6 1668
逝去的感伤
逝去的感伤 2020-12-02 07:48

I\'m using a Scrollview for an infinite \"Time Picker Carousel\" and found out, that it is not the best approach (last question)

Now, I found the Recycl

相关标签:
6条回答
  • 2020-12-02 08:01

    enter image description here

    Solution 1: setOnScrollListener

    Save your X-Position as class variable and update each change within the onScrollListener. Ensure you don't 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);
    
        }
     });
    

    Solution 2: Calculate current position.

    In my case, I have a horizontal List which is filled with time values (0:00, 0:30, 1:00, 1:30 ... 23:00, 23:30). I'm calculating the time from the time-item, which is in the middle of the screen (calculation point). That's why I need the exact X-Scroll Position of my RecycleView

    • Each time item has the same width of 60dp (fyi: 60dp = 30min, 2dp = 1min)
    • First item (Header item) has an extra padding, to set 0min to the center

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

      }

    0 讨论(0)
  • 2020-12-02 08:03
    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();
        }
    }
    

    Than where you need to get the offset:

    recyclerView.getHorizontalOffset()
    
    0 讨论(0)
  • 2020-12-02 08:05

    RecyclerView already have method to get horizontal and vertical scroll offset

    mRecyclerView.computeHorizontalScrollOffset()
    mRecyclerView.computeVerticalScrollOffset()
    

    This will work for RecyclerViews containing cells of the same height (for vertical scroll offset) and the same width (for horizontal scroll offset)

    0 讨论(0)
  • 2020-12-02 08:06

    Thanks to @umar-qureshi for the right lead! It appears that you can determine the scroll percentage with Offset, Extent, and Range such that

    percentage = 100 * offset / (range - extent)
    

    For example (to be put in an 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 + "%");
    
    0 讨论(0)
  • 2020-12-02 08:06

    If you need to know the current page and its offset in comparison to the width of RecyclerView you may wanna try something like this:

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

    If currentPage has index 0 then leftIndicator (arrow) will be transparent, and so will be and rightIndicator if there's only one page (in the example, the adapter always has at least one item to show so there's no check for the empty value).

    This way you will basically have almost the same functionality as if you have been using the callback pageScrolled from ViewPager.OnPageChangeListener.

    0 讨论(0)
  • 2020-12-02 08:08

    by override RecyclerView onScrolled(int dx,int dy) , you can get the scroll offset. but when you add or remove the item, the value may be wrong.

    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 讨论(0)
提交回复
热议问题