/**
   * Updates the scrollbar thumb offset to match the visible scroll of the recycler view. It does
   * this by mapping the available scroll area of the recycler view to the available space for the
   * scroll bar.
   *
   * @param scrollPosState the current scroll position
   * @param rowCount the number of rows, used to calculate the total scroll height (assumes that all
   *     rows are the same height)
   * @param yOffset the offset to start tracking in the recycler view (only used for all apps)
   */
  protected void synchronizeScrollBarThumbOffsetToViewScroll(
      ScrollPositionState scrollPosState, int rowCount, int yOffset) {
    int availableScrollHeight =
        getAvailableScrollHeight(rowCount, scrollPosState.rowHeight, yOffset);
    int availableScrollBarHeight = getAvailableScrollBarHeight();

    // Only show the scrollbar if there is height to be scrolled
    if (availableScrollHeight <= 0) {
      mScrollbar.setScrollbarThumbOffset(-1, -1);
      return;
    }

    // Calculate the current scroll position, the scrollY of the recycler view accounts for the
    // view padding, while the scrollBarY is drawn right up to the background padding (ignoring
    // padding)
    int scrollY =
        getPaddingTop()
            + yOffset
            + (scrollPosState.rowIndex * scrollPosState.rowHeight)
            - scrollPosState.rowTopOffset;
    int scrollBarY =
        mBackgroundPadding.top
            + (int) (((float) scrollY / availableScrollHeight) * availableScrollBarHeight);

    // Calculate the position and size of the scroll bar
    int scrollBarX;
    if (Utilities.isRtl(getResources())) {
      scrollBarX = mBackgroundPadding.left;
    } else {
      scrollBarX = getWidth() - mBackgroundPadding.right - mScrollbar.getWidth();
    }
    mScrollbar.setScrollbarThumbOffset(scrollBarX, scrollBarY);
  }
 /**
  * Handles the touch event and determines whether to show the fast scroller (or updates it if it
  * is already showing).
  */
 private boolean handleTouchEvent(MotionEvent ev) {
   int action = ev.getAction();
   int x = (int) ev.getX();
   int y = (int) ev.getY();
   switch (action) {
     case MotionEvent.ACTION_DOWN:
       // Keep track of the down positions
       mDownX = x;
       mDownY = mLastY = y;
       if (shouldStopScroll(ev)) {
         stopScroll();
       }
       mScrollbar.handleTouchEvent(ev, mDownX, mDownY, mLastY);
       break;
     case MotionEvent.ACTION_MOVE:
       mLastY = y;
       mScrollbar.handleTouchEvent(ev, mDownX, mDownY, mLastY);
       break;
     case MotionEvent.ACTION_UP:
     case MotionEvent.ACTION_CANCEL:
       onFastScrollCompleted();
       mScrollbar.handleTouchEvent(ev, mDownX, mDownY, mLastY);
       break;
   }
   return mScrollbar.isDragging();
 }
 @Override
 protected void dispatchDraw(Canvas canvas) {
   super.dispatchDraw(canvas);
   onUpdateScrollbar();
   mScrollbar.draw(canvas);
 }
 /**
  * Returns the available scroll bar height: AvailableScrollBarHeight = Total height of the visible
  * view - thumb height
  */
 protected int getAvailableScrollBarHeight() {
   int visibleHeight = getHeight() - mBackgroundPadding.top - mBackgroundPadding.bottom;
   int availableScrollBarHeight = visibleHeight - mScrollbar.getThumbHeight();
   return availableScrollBarHeight;
 }
 /** Returns the scroll bar width when the user is scrolling. */
 public int getMaxScrollbarWidth() {
   return mScrollbar.getThumbMaxWidth();
 }