如何給RecyclerView加上滾動條--現在就教你


時隔許久,我又要更博了,含蓄的話不多說了,今天我們的主題是這個RecyclerView。至於為什么要加個scrollBar?因為我的業務需求是需要一個實現成這樣的,

效果圖:(可能看起來比較粗糙,但功能實現了,你可以自定義修改嘛~哈哈哈)

可能我比較老派,我的app 中list的垂直布局還是喜歡用listView ,使用起來簡單(可能博主我之前有個通用的adapter不舍得放棄吧,雖然RecyclerView也有通用的adapter,但總感覺顯示簡單的列表要多寫幾行代碼,有點麻煩,哈哈哈,可能我就想偷懶吧)

但既然今天的是這個RecyclerView那么我們就開始詳細的說明下吧~廢話不多說切入正題。

注意:我這個View暫時只實現了垂直的View的ScrollBar的滾動,暫時沒有橫向實現,

未來可能我會拓展,敬請期待吧(大佬可能覺得太簡單,勿噴,求教,謝謝)

先說下我的思路:

       一、初始思路(想一個新玩意總得有個過程和彎路嘛),可能跟你想法一樣哦

  1.   用一個LinearLayout 布局套住 RecyclerView 和一個自定義的View  
  2. 自定義一個可以上下滾動的scrollBar 
  3. 滾動的ScrollBar綁定RecyclerView的滾動事件
  4. Ok

       二、現在的實現的思路 (我想了下如果用上面的方案實現的話就,必須寫個xml文件,自定義一個ViewGroup,別人使用起來會很麻煩,當然你把他當成類庫來用也很簡單,而我就不想寫xml)

  1. HobbyRecyclerView繼承RecyclerView, 擴展這個功能
  2. 在HobbyRecyclerView 中先讓子view的寬度縮小 ,留出給scrollBar的寬度
  3. 在HobbyRecyclerView中畫一個ScrollBar ,這個bar的高度 = (view的可見高度 / view的所有子View的高度)* view可見高度 。
  4. 給HobbyRecyclerView加上滾動事件監聽,監聽滾動距離dy*(view的可見高度 / view的所有子View的高度)=  scrollbar 的滾動距離
  5. 給scrollBar加上監聽,監聽拖動距離轉化為    RecyclerView的滾動距離   =   (scrollBar的拖動距離/scrollBar可滾動的區域高度(這里的區域高度等於View的高度)) * view的所有子View的高度 
  6. 測試ok

思路也大概說了,那么就來看代碼的實現吧。

/**
 * @author mdm
 * @Description  HobbyRecyclerView
 * @Version 1.0
 */
public class HobbyRecyclerView extends RecyclerView {

    private RectF mRangeRectf;
    private Paint mRangPaint;
    private int showMode = 1;
    //滾動條寬高
    private float scrollBarHeight;
    private float scrollBarWidth;
    //柱間隙
    private float scrollWidthSpace ;
    //滾動條寬度的等分比例
    private int scrollWidthWeight = 10;
    //Y軸的偏移值
    private float yScrollOffset = 0;
    //所有的子view的總高度 也就是這個
    private float childViewAllHeight;
    //可視區域的高度 其實這里就是View高度
    private float visualHeight;
    //可視區域的高度/所有的子view的總高度 得出的比例
    float range;
    //recyclerView的每個Item項的寬度
    private int childWidth;
    //判斷觸摸焦點
    private boolean isFocus = false;

    //手觸摸時點的x,y坐標
    float x = 0;
    float y = 0;

    public HobbyRecyclerView(@NonNull Context context) {
        super(context);
    }
    public HobbyRecyclerView(@NonNull Context context, @Nullable AttributeSet attrs) {
        super(context, attrs);
        init(context,attrs);
    }
    public HobbyRecyclerView(@NonNull Context context, @Nullable AttributeSet attrs, int defStyle) {
        super(context, attrs, defStyle);
        init(context,attrs);
    }
    /**
     * 初始化
     * @param context
     * @param attrs
     */
    private void init(Context context, AttributeSet attrs) {
        mRangeRectf = new RectF();
//        region = new Region();
        mRangPaint = new Paint(Paint.ANTI_ALIAS_FLAG);
        mRangPaint.setStyle(Paint.Style.FILL);
        TypedArray ta = context.obtainStyledAttributes(attrs,R.styleable.HobbyRecyclerView);
        showMode = ta.getInteger(R.styleable.HobbyRecyclerView_scrollBarMode,1);
        ta.recycle();
        addOnScrollListener(onScrollListener);
    }

    @Override
    protected void onMeasure(int widthSpec, int heightSpec) {
        if(showMode != 1){
            int width = MeasureSpec.getSize(widthSpec);
            scrollBarWidth = width / scrollWidthWeight;//取10分之一
            scrollWidthSpace = scrollBarWidth / 10; //獲取間隙
            childWidth = (int) (width - scrollBarWidth);
            scrollBarWidth = scrollBarWidth - 2 * scrollWidthSpace;
            for (int i = 0; i < getChildCount(); i++) {
                measureChild(getChildAt(i),childWidth,heightSpec);
                getChildAt(i).getLayoutParams().width = childWidth;
            }
            setMeasuredDimension(width,heightSpec);
        }else {
            super.onMeasure(widthSpec, heightSpec);
        }

    }
    @Override
    protected void onLayout(boolean changed, int l, int t, int r, int b) {
        super.onLayout(changed, l, t, r, b);
        visualHeight = getMeasuredHeight();
        childViewAllHeight = getChildAt(2).getHeight() * getAdapter().getItemCount();
        range = 0;
        if(childViewAllHeight != 0){
            range = visualHeight / childViewAllHeight;
        }
        scrollBarHeight = range * visualHeight;
    }

    @Override
    protected void dispatchDraw(Canvas canvas) {
        super.dispatchDraw(canvas);
        drawRange(canvas);
    }

    private void drawRange(Canvas canvas){
        if(canvas == null) return;
        mRangeRectf.set(childWidth + scrollWidthSpace,yScrollOffset,childWidth + scrollBarWidth ,yScrollOffset + scrollBarHeight);
        if(isFocus) {
            mRangPaint.setColor(Color.parseColor("#2386BF"));
        }else{
            mRangPaint.setColor(Color.parseColor("#2EB3FF"));
        }
        canvas.drawRect(mRangeRectf,mRangPaint);
        Log.i("tag" , "yScrollOffset ------- " + yScrollOffset);
        Log.i("tag" , "scrollBarHeight ------- " + scrollBarHeight);
        Log.i("tag" , "yScrollOffset ------- " + yScrollOffset);
    }


    private RecyclerView.OnScrollListener onScrollListener = new OnScrollListener() {
        @Override
        public void onScrollStateChanged(@NonNull RecyclerView recyclerView, int newState) {
            super.onScrollStateChanged(recyclerView, newState);
        }

        @Override
        public void onScrolled(@NonNull RecyclerView recyclerView, int dx, int dy) {
            super.onScrolled(recyclerView, dx, dy);
            yScrollOffset += dy * range;
        }
    };
    @Override
    public boolean onTouchEvent(MotionEvent event) {
        switch (event.getAction()){
            case MotionEvent.ACTION_DOWN:
                //獲取屏幕上點擊的坐標
                x = event.getX();
                y = event.getY();
                if(x >= mRangeRectf.left
                        && x <= mRangeRectf.right
                        && y >= mRangeRectf.top
                        && y <= mRangeRectf.bottom){
                    isFocus = true;
                    invalidate();
                }
                break;
            case MotionEvent.ACTION_MOVE:
                if(x >= mRangeRectf.left
                        && x <= mRangeRectf.right
                        && y >= mRangeRectf.top
                        && y <= mRangeRectf.bottom){
                    float diffValue = event.getY() - y;
                    scrollBy(0, (int) ((diffValue/visualHeight) * childViewAllHeight));
                    y = event.getY();
                }
                break;
            case MotionEvent.ACTION_UP:
                isFocus = false;
                invalidate();
                break;
        }
        if(x >= childWidth
                && x <= getMeasuredWidth()
                && y >= 0
                && y <= getMeasuredHeight()){
            return true;
        }else return super.onTouchEvent(event);
    }


    /**
     *  //當前RcyclerView顯示區域的高度。水平列表屏幕從左側到右側顯示范圍
     int extent = this.computeHorizontalScrollExtent();

     //整體的高度,注意是整體,包括在顯示區域之外的。
     int range = this.computeHorizontalScrollRange();

     //已經向下滾動的距離,為0時表示已處於頂部。
     int offset = this.computeHorizontalScrollOffset();
     */
}

 

是不是很簡單呢?

ok有什么問題自己看吧。如果你看到最后幾行注釋了,那么你會疑問,那是因為我看了這個文章 

仿拼多多可水平滾動RecyclerView,自定義滾動條滾動距離

 


免責聲明!

本站轉載的文章為個人學習借鑒使用,本站對版權不負任何法律責任。如果侵犯了您的隱私權益,請聯系本站郵箱yoyou2525@163.com刪除。



 
粵ICP備18138465號   © 2018-2025 CODEPRJ.COM