• Skip to primary navigation
  • Skip to main content
  • Skip to primary sidebar

陈文管的博客

分享有价值的内容

  • Android
  • Affiliate
  • SEO
  • 前后端
  • 网站建设
  • 自动化
  • 开发资源
  • 关于

Android 自定义菱形横向滑动指示器控件

2024年6月11日发布 | 最近更新于 2024年6月11日

常规形状的滑动指示控件样式一般只需要通过Shape去配置样式就行,但如果UI设计需要的是自定义形状的控件就需要自定绘制实现,这边给出Android自定义菱形横向滑动指示器控件实现。

一、指示器控件自定义绘制实现

实现抽象效果大概如下:

 --------------
/----/-------/

实现代码不多,直接贴结果,自己在style.xml样式文件里面定义下ScrollIndicator参数,滚动条背景色color_scroll_indicator_bg 和前景色 color_scroll_indicator_progress 也自己在样式文件里面自定义配置下就行。indicator_progress_width 为你自己需要配置的进度条宽度,不配置的话默认progress宽度为总宽度的三分一。

/**
 * 滚动指示条
 */
public class ScrollIndicatorView extends View {
    private int mBackgroundColor = Color.BLACK;
    private int mProgressColor = Color.YELLOW;
    private int mProgressWidth = 0;
    private int mProgressRemainWidth = 0;
    private Paint mPaint = null;
    private Path mBackgroundPath = null;
    private Path mProgressPath = null;

    private float mScrollOffset = 0;
    private float mAreaOffsetX = 0;

    public ScrollIndicatorView(@NonNull Context context) {
        this(context, null);
    }

    public ScrollIndicatorView(@NonNull Context context, @Nullable AttributeSet attrs) {
        this(context, attrs, -1);
    }

    public ScrollIndicatorView(@NonNull Context context, @Nullable AttributeSet attrs, int defStyleAttr) {
        this(context, attrs, defStyleAttr, -1);
    }

    public ScrollIndicatorView(@NonNull Context context, @Nullable AttributeSet attrs, int defStyleAttr, int defStyleRes) {
        super(context, attrs, defStyleAttr, defStyleRes);
        // 这边在样式配置文件里面自己配置下ScrollIndicator
        TypedArray a = getContext().obtainStyledAttributes(attrs, R.styleable.ScrollIndicator, defStyleAttr, defStyleRes);
        mBackgroundColor = a.getColor(R.styleable.ScrollIndicator_indicator_background, getResources().getColor(R.color.color_scroll_indicator_bg, context.getTheme()));
        mProgressColor = a.getColor(R.styleable.ScrollIndicator_indicator_progress, getResources().getColor(R.color.color_scroll_indicator_progress, context.getTheme()));
        mProgressWidth = a.getDimensionPixelSize(R.styleable.ScrollIndicator_indicator_progress_width, 0);
        a.recycle();

        mPaint = new Paint();
        mPaint.setAntiAlias(true);
        mPaint.setStyle(Paint.Style.FILL);

        mBackgroundPath = new Path();
        mProgressPath = new Path();
    }

    private void initPath() {
        int viewWidth = getMeasuredWidth();
        int viewHeight = getMeasuredHeight();
        // 梯形X轴偏移量
        mAreaOffsetX = (float) (viewHeight / Math.sqrt(3));
        mBackgroundPath.moveTo(0, viewHeight);
        mBackgroundPath.lineTo(mAreaOffsetX, 0);
        mBackgroundPath.lineTo(viewWidth, 0);
        mBackgroundPath.lineTo(viewWidth - mAreaOffsetX, viewHeight);
        mBackgroundPath.close();
        // 设置绘制为填充效果
        mBackgroundPath.setFillType(Path.FillType.WINDING);
        mProgressPath.setFillType(Path.FillType.WINDING);
        if (mProgressWidth == 0) {// 默认progress宽度为总宽度的30%
            mProgressWidth = viewWidth * 3 / 10;
        }
        mProgressRemainWidth = viewWidth - mProgressWidth;
    }

    /**
     * 滚动滑出部分在剩余范围的比例,百分比参数单位
     *
     * @param offset
     */
    public void updateProgress(float offset) {
        mScrollOffset = offset;
        invalidate();
    }

    @Override
    protected void onMeasure(int widthMeasureSpec, int heightMeasureSpec) {
        super.onMeasure(widthMeasureSpec, heightMeasureSpec);
        initPath();
    }

    @Override
    protected void onDraw(Canvas canvas) {
        super.onDraw(canvas);
        // 绘制背景
        mPaint.setColor(mBackgroundColor);
        canvas.drawPath(mBackgroundPath, mPaint);
        // 刷新绘制滚动条区域
        mPaint.setColor(mProgressColor);
        float maxRight = mProgressRemainWidth * mScrollOffset + mProgressWidth + mAreaOffsetX;
        float maxLeft = mProgressRemainWidth * mScrollOffset + mAreaOffsetX;
        // 校验滑动最大值,不超过控件宽度
        if(maxRight > getMeasuredWidth()) {
            maxRight = getMeasuredWidth();
            maxLeft = maxRight - mProgressWidth;
        }
        // 路线要先重置再重新配置参数
        mProgressPath.reset();
        mProgressPath.moveTo(maxLeft - mAreaOffsetX, getMeasuredHeight());
        mProgressPath.lineTo(maxLeft, 0);
        mProgressPath.lineTo(maxRight , 0);
        mProgressPath.lineTo(maxRight - mAreaOffsetX, getMeasuredHeight());
        mProgressPath.close();
        canvas.drawPath(mProgressPath, mPaint);
    }
}

二、滚动控件使用

在Fragment或Activity中设置RecyclerView的滑动监听:

mRecyclerView.addOnScrollListener(mScrollListener);

滚动监听类实现:

private RecyclerView.OnScrollListener mScrollListener = 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);
            int range=0;
            int temp = mRecyclerView.computeHorizontalScrollRange();
            if (temp > range) {
                range = temp;
            }
            //滑块的偏移量
            int offset = mRecyclerView.computeHorizontalScrollOffset();
            //可视区域长度
            int extent = mRecyclerView.computeHorizontalScrollExtent();
            //滑出部分在剩余范围的比例
            float proportion = (float) (offset * 1.0 / (range - extent));
            // mIndicator为布局中的ScrollIndicatorView引用
            mIndicator.updateProgress(proportion);
        }
    };

到这就完成了,可以自己修改ScrollIndicatorView中Path路径参数,去实现自己需要的形状。

三、其他实现参考

如果不需要自定义形状的滑动指示,可以参考 Android仿天猫横向滑动指示器功能的实现

扩展阅读:

  • Android 弧形 RecyclerView 实现(Kotlin)
  • 美图手机音乐Widget动画实现
  • Android 心率动画自定义控件实现
  • Android 卡片旋转切换动效实现详解
  • Android 残影数字动画实现详解

博客公众号

微信公众号

转载请注明出处:陈文管的博客 – Android 自定义菱形横向滑动指示器控件

文章目录

  • 一、指示器控件自定义绘制实现
  • 二、滚动控件使用
  • 三、其他实现参考
博客公众号

闽ICP备18001825号-1 · Copyright © 2025 · Powered by chenwenguan.com