欧美bbbwbbbw肥妇,免费乱码人妻系列日韩,一级黄片

Android自定義Scrollbar的兩種實現(xiàn)方式

 更新時間:2025年03月25日 09:20:00   作者:望佑  
本文介紹兩種實現(xiàn)自定義滾動條的方法,分別通過ItemDecoration方案和獨立View方案實現(xiàn)滾動條定制化,文章通過代碼示例講解的非常詳細,對大家的學習或工作有一定的幫助,需要的朋友可以參考下

本文介紹兩種實現(xiàn)自定義滾動條的方法,分別通過ItemDecoration方案和獨立View方案實現(xiàn)滾動條定制化。兩種方案均支持以下核心功能:

  • 支持自定義右側間距
  • 支持按住上下拖動
  • 按住時放大1.5倍
  • 自動隱藏與顯示邏輯
  • 流暢的動畫效果

方案一:ItemDecoration實現(xiàn)(推薦用于RecyclerView)

實現(xiàn)原理

通過繼承RecyclerView.ItemDecoration,在onDrawOver中繪制滾動條,結合觸摸事件處理實現(xiàn)交互

完整代碼實現(xiàn)

package com.example.scrollbardecoration;

import android.content.Context;
import android.graphics.Canvas;
import android.graphics.Paint;
import android.graphics.Rect;
import android.view.MotionEvent;
import android.view.View;
import androidx.annotation.NonNull;
import androidx.recyclerview.widget.RecyclerView;

public class ScrollBarItemDecoration extends RecyclerView.ItemDecoration {
    // 尺寸配置(單位:dp)
    private static final int DEFAULT_THUMB_WIDTH = 8;
    private static final int DEFAULT_MIN_LENGTH = 20;
    private static final int DEFAULT_RIGHT_MARGIN = 20;
    private static final float SCALE_FACTOR = 1.5f;
    
    // 顏色配置
    private static final int DEFAULT_THUMB_COLOR = 0xFF888888;
    private static final int DEFAULT_TRACK_COLOR = 0xFFEEEEEE;

    // 繪制工具
    private final Paint thumbPaint = new Paint(Paint.ANTI_ALIAS_FLAG);
    private final Paint trackPaint = new Paint(Paint.ANTI_ALIAS_FLAG);
    private final Rect thumbRect = new Rect();
    private final Rect trackRect = new Rect();

    // 狀態(tài)控制
    private float scrollRange;
    private boolean isDragging;
    private float thumbScale = 1f;
    private RecyclerView recyclerView;

    public ScrollBarItemDecoration(Context context) {
        // 尺寸轉換
        int thumbWidth = dpToPx(context, DEFAULT_THUMB_WIDTH);
        int rightMargin = dpToPx(context, DEFAULT_RIGHT_MARGIN);
        
        // 畫筆初始化
        thumbPaint.setColor(DEFAULT_THUMB_COLOR);
        trackPaint.setColor(DEFAULT_TRACK_COLOR);
    }

    public void attachToRecyclerView(RecyclerView recyclerView) {
        this.recyclerView = recyclerView;
        recyclerView.addItemDecoration(this);
        
        // 滾動監(jiān)聽
        recyclerView.addOnScrollListener(new RecyclerView.OnScrollListener() {
            @Override
            public void onScrolled(@NonNull RecyclerView recyclerView, int dx, int dy) {
                updateScrollParams();
                recyclerView.invalidate();
            }
        });

        // 觸摸事件處理
        recyclerView.addOnItemTouchListener(new RecyclerView.SimpleOnItemTouchListener() {
            @Override
            public boolean onInterceptTouchEvent(@NonNull RecyclerView rv, @NonNull MotionEvent e) {
                handleTouch(e);
                return false;
            }
        });
    }

    private void updateScrollParams() {
        int totalHeight = recyclerView.computeVerticalScrollRange();
        int visibleHeight = recyclerView.getHeight();
        scrollRange = totalHeight - visibleHeight;
    }

    @Override
    public void onDrawOver(@NonNull Canvas c, @NonNull RecyclerView parent, @NonNull RecyclerView.State state) {
        // 繪制軌道
        trackRect.set(parent.getWidth() - thumbWidth - rightMargin, 0, 
                    parent.getWidth() - rightMargin, parent.getHeight());
        c.drawRect(trackRect, trackPaint);

        // 計算滑塊位置
        float thumbPosition = (recyclerView.computeVerticalScrollOffset() / scrollRange) * 
                            (parent.getHeight() - thumbLength);
        int scaledWidth = (int)(thumbWidth * thumbScale);
        
        // 繪制滑塊
        thumbRect.set(parent.getWidth() - scaledWidth - rightMargin, (int)thumbPosition,
                    parent.getWidth() - rightMargin, (int)(thumbPosition + thumbLength));
        c.drawRect(thumbRect, thumbPaint);
    }

    private void handleTouch(MotionEvent e) {
        switch (e.getAction()) {
            case MotionEvent.ACTION_DOWN:
                if (thumbRect.contains(e.getX(), e.getY())) {
                    isDragging = true;
                    thumbScale = SCALE_FACTOR;
                    recyclerView.invalidate();
                }
                break;
            case MotionEvent.ACTION_MOVE:
                if (isDragging) {
                    float newOffset = (e.getY() / recyclerView.getHeight()) * scrollRange;
                    recyclerView.scrollToPosition((int)newOffset);
                }
                break;
            case MotionEvent.ACTION_UP:
                isDragging = false;
                thumbScale = 1f;
                recyclerView.invalidate();
                break;
        }
    }

    private int dpToPx(Context context, int dp) {
        return (int)(dp * context.getResources().getDisplayMetrics().density + 0.5f);
    }
}

使用示例

<!-- activity_main.xml -->
<androidx.recyclerview.widget.RecyclerView
    android:id="@+id/recyclerView"
    android:layout_width="match_parent"
    android:layout_height="match_parent"/>
// MainActivity.java
public class MainActivity extends AppCompatActivity {
    protected void onCreate(Bundle savedInstanceState) {
        RecyclerView recyclerView = findViewById(R.id.recyclerView);
        new ScrollBarItemDecoration(this).attachToRecyclerView(recyclerView);
        // 設置Adapter等后續(xù)操作...
    }
}

優(yōu)點與局限

優(yōu)點:

  • 與RecyclerView深度集成
  • 內存占用低
  • 無需修改布局結構

局限:

  • 僅適用于RecyclerView
  • 復雜手勢處理需要額外開發(fā)

方案二:獨立View實現(xiàn)(支持任意滾動視圖)

實現(xiàn)原理

通過自定義View實現(xiàn)滾動條,可適配RecyclerView/NestedScrollView等多種滾動容器

public class CustomScrollBarView extends View {
    // 繪制參數(shù)
    private final Paint thumbPaint = new Paint(Paint.ANTI_ALIAS_FLAG);
    private final RectF thumbRect = new RectF();
    
    // 狀態(tài)控制
    private float scrollRange;
    private boolean isDragging;
    private ValueAnimator widthAnimator;

    public CustomScrollBarView(Context context) {
        super(context);
        thumbPaint.setColor(0xCCCCCC);
    }

    public void attachToView(View scrollView) {
        if (scrollView instanceof RecyclerView) {
            ((RecyclerView)scrollView).addOnScrollListener(new RecyclerView.OnScrollListener() {
                @Override
                public void onScrolled(@NonNull RecyclerView rv, int dx, int dy) {
                    updateScrollParams(rv);
                }
            });
        } else if (scrollView instanceof NestedScrollView) {
            ((NestedScrollView)scrollView).setOnScrollChangeListener((v, x, y, oldX, oldY) -> {
                updateScrollParams(v);
            });
        }
        
        setOnTouchListener((v, event) -> {
            handleTouch(event);
            return true;
        });
    }

    private void updateScrollParams(View scrollView) {
        int totalHeight = scrollView.computeVerticalScrollRange();
        int visibleHeight = scrollView.getHeight();
        scrollRange = totalHeight - visibleHeight;
        invalidate();
    }

    @Override
    protected void onDraw(Canvas canvas) {
        float thumbPos = (scrollOffset / scrollRange) * (getHeight() - thumbLength);
        thumbRect.set(getWidth()-thumbWidth, thumbPos, getWidth(), thumbPos+thumbLength);
        canvas.drawRoundRect(thumbRect, 20, 20, thumbPaint);
    }

    private void handleTouch(MotionEvent event) {
        switch (event.getAction()) {
            case MotionEvent.ACTION_DOWN:
                if (thumbRect.contains(event.getX(), event.getY())) {
                    startWidthAnimation(thumbWidth, (int)(thumbWidth*1.5f));
                }
                break;
            case MotionEvent.ACTION_MOVE:
                if (isDragging) {
                    float deltaY = event.getY() - lastTouchY;
                    scrollView.scrollBy(0, (int)(deltaY * 3.5f));
                    invalidate();
                }
                break;
            case MotionEvent.ACTION_UP:
                startWidthAnimation(thumbWidthWhenDragging, thumbWidth);
                break;
        }
    }

    private void startWidthAnimation(int from, int to) {
        if (widthAnimator != null) widthAnimator.cancel();
        widthAnimator = ValueAnimator.ofInt(from, to);
        widthAnimator.addUpdateListener(anim -> {
            thumbWidth = (int)anim.getAnimatedValue();
            invalidate();
        });
        widthAnimator.start();
    }
}

使用示例

<!-- 布局文件 -->
<FrameLayout>
    <androidx.core.widget.NestedScrollView
        android:id="@+id/scrollView"
        android:layout_width="match_parent"
        android:layout_height="match_parent"/>
        
    <com.example.CustomScrollBarView
        android:layout_width="8dp"
        android:layout_height="match_parent"
        android:layout_gravity="right"/>
</FrameLayout>

優(yōu)點與局限

優(yōu)點:

  • 支持任意滾動視圖
  • 動畫效果更豐富
  • 更高的定制自由度

局限:

  • 需要手動維護布局位置
  • 內存占用略高

方案對比

特性ItemDecoration方案獨立View方案
集成難度★★☆☆☆★★★☆☆
性能表現(xiàn)★★★★☆★★★☆☆
功能擴展性★★☆☆☆★★★★★
多容器支持僅RecyclerView所有滾動視圖
動畫效果支持基礎縮放支持復雜動畫

最佳實踐建議

  • RecyclerView專用場景推薦使用ItemDecoration方案,具有更好的性能表現(xiàn)和內存效率

  • 復雜交互需求當需要實現(xiàn)以下功能時,建議采用獨立View方案:

    • 跨視圖類型統(tǒng)一滾動條
    • 復雜手勢識別(如雙擊操作)
    • 多步驟動畫效果
    • 非垂直方向滾動支持
  • 性能優(yōu)化建議

    • 避免在draw方法中創(chuàng)建對象
    • 使用ValueAnimator代替ObjectAnimator
    • 對于長列表,啟用RecyclerView的setHasFixedSize
  • 視覺定制技巧

// 修改滾動條樣式
scrollBar.setThumbColor(Color.RED);
scrollBar.setTrackColor(Color.GRAY);
scrollBar.setThumbWidth(12); // 單位:dp

常見問題解決

Q1 滾動條顯示位置不正確?

  • 檢查父容器的clipToPadding屬性
  • 確認滾動條寬度計算包含margin值

Q2 拖動時出現(xiàn)卡頓?

  • 確保未在UI線程執(zhí)行耗時操作
  • 降低滾動事件的觸發(fā)頻率
  • 使用硬件加速圖層

Q3 與下拉刷新沖突?

// 在CoordinatorLayout中增加觸摸攔截判斷
@Override
public boolean onInterceptTouchEvent(MotionEvent e) {
    if (isDragging) {
        getParent().requestDisallowInterceptTouchEvent(true);
        return true;
    }
    return super.onInterceptTouchEvent(e);
}

通過兩種方案的對比實現(xiàn),ItemDecoration方案適合RecyclerView的輕量級定制,而獨立View方案則提供了更大的靈活性和擴展性。

以上就是Android自定義Scrollbar的兩種實現(xiàn)方式的詳細內容,更多關于Android自定義Scrollbar的資料請關注腳本之家其它相關文章!

相關文章

最新評論