【Android】實現(xiàn)垂直可拖動的進度條

效果圖.png

分析設(shè)計

從圖上可以看出,需要實現(xiàn)一個垂直的進度條(VerticalSeekbar),該進度條需要包含以下三個部分:

  • 進度條
    進度條為圓角、漸變背景色效果。
  • 可拖拽圓點
    圓點需要繪制陰影(shadow)效果或直接根據(jù)UI提供的圖片繪制
  • 進度指示游標
    根據(jù)拖拽進度展示,拖拽結(jié)束隱藏。

實現(xiàn)

這里通過自定義View來實現(xiàn)上述效果
代碼如下:

public class VerticalSeekBar extends View {

    private static final int MAX_VALUE = 100;
    private static final int MIN_VALUE = 0;

    private int startColor = Color.parseColor("#FAD0C4");
    private int middleColor = Color.parseColor("#FAD0C4");
    private int endColor = Color.parseColor("#FFD1FF");
    private int thumbColor = Color.WHITE;
    private final int[] colorArray = {startColor, middleColor, endColor};
    private float x, y;
    private float mRadius;
    private float progress;
    private float sLeft, sTop, sRight, sBottom;
    private float sWidth, sHeight;
    private float shadowWidth;
    private final Paint paintPb = new Paint();
    private final RectF rectBlackBg = new RectF();
    private final Paint thumbPaint = new Paint();
    protected OnStateChangeListener onStateChangeListener;

    public VerticalSeekBar(Context context) {
        this(context, null);
        init();
    }

    public VerticalSeekBar(Context context, AttributeSet attrs) {
        super(context, attrs);
        init();
    }

    private void init() {

    }

    @Override
    protected synchronized void onMeasure(int widthMeasureSpec, int heightMeasureSpec) {
        super.onMeasure(widthMeasureSpec, heightMeasureSpec);
        setMeasuredDimension(getMeasuredWidth(), getMeasuredHeight());
        int h = getMeasuredHeight();
        int w = getMeasuredWidth();
        shadowWidth = getResources().getDimension(R.dimen.common_measure_2dp);
        mRadius = (w - shadowWidth) / 2F;
        sLeft = w * 0.25f; // 背景左邊緣坐標
        sRight = w * 0.75f;// 背景右邊緣坐標
        sTop = 0;
        sBottom = h;
        sWidth = sRight - sLeft; // 背景寬度
        sHeight = sBottom - sTop; // 背景高度
        x = (float) w / 2;//圓心的x坐標
        y = (float) (1 - 0.01 * progress) * sHeight;//圓心y坐標

        // init paint
        thumbPaint.setAntiAlias(true);
        thumbPaint.setStyle(Paint.Style.FILL);
        thumbPaint.setColor(thumbColor);
        // 添加陰影效果
        thumbPaint.setShadowLayer(shadowWidth, 0, 0, Color.GRAY);

        rectBlackBg.set(sLeft, sTop, sRight, sBottom);
        paintPb.setAntiAlias(true);
        paintPb.setStyle(Paint.Style.FILL);
    }

    public void setColor(int startColor, int middleColor, int endColor, int thumbColor, int thumbBorderColor) {
        this.startColor = startColor;
        this.middleColor = middleColor;
        this.endColor = endColor;
        this.thumbColor = thumbColor;
        colorArray[0] = startColor;
        colorArray[1] = middleColor;
        colorArray[2] = endColor;
    }

    @Override
    protected void onDraw(Canvas canvas) {
        super.onDraw(canvas);
        drawBackground(canvas);
        drawCircle(canvas);
        paintPb.reset();
    }

    private void drawBackground(Canvas canvas) {
        // 設(shè)置渲染器
        LinearGradient linearGradient = new LinearGradient(sLeft, sTop, sWidth, sHeight, colorArray, null, Shader.TileMode.MIRROR);
        paintPb.setShader(linearGradient);
        canvas.drawRoundRect(rectBlackBg, sWidth / 2, sWidth / 2, paintPb);
    }

    private void drawCircle(Canvas canvas) {
        y = Math.max(y, mRadius);// 判斷thumb邊界
        y = Math.min(y, sHeight - mRadius);
        canvas.drawCircle(x, y, mRadius, thumbPaint);
    }

    float indicatorOffset = 0;

    @Override
    public boolean onTouchEvent(MotionEvent event) {
        this.y = event.getY();
        progress = (sHeight - y) / sHeight * MAX_VALUE;
        if (progress < MIN_VALUE) {
            this.progress = MIN_VALUE;
        } else if (progress > MAX_VALUE) {
            this.progress = MAX_VALUE;
        }

        indicatorOffset = sHeight / MAX_VALUE * progress - mRadius * 1.5F;
        indicatorOffset = indicatorOffset < 0 ? 0 : (Math.min(indicatorOffset, sHeight - mRadius * 2));

        switch (event.getAction()) {
            case MotionEvent.ACTION_DOWN:
                if (onStateChangeListener != null) {
                    onStateChangeListener.onStartTouch(this);
                }
                break;
            case MotionEvent.ACTION_UP:
                if (onStateChangeListener != null) {
                    onStateChangeListener.onStopTrackingTouch(this, progress);
                }
                break;
            case MotionEvent.ACTION_MOVE:
                if (onStateChangeListener != null) {
                    onStateChangeListener.onStateChangeListener(this, progress, indicatorOffset);
                }
                setProgress(progress);
                this.invalidate();
                break;
        }

        return true;
    }


    public interface OnStateChangeListener {
        void onStartTouch(View view);

        void onStateChangeListener(View view, float progress, float indicatorOffset);

        void onStopTrackingTouch(View view, float progress);
    }

    public void setOnStateChangeListener(OnStateChangeListener onStateChangeListener) {
        this.onStateChangeListener = onStateChangeListener;
    }

    public void setProgress(float progress) {
        this.progress = progress;
        invalidate();
    }
}

拓展閱讀

?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請聯(lián)系作者
【社區(qū)內(nèi)容提示】社區(qū)部分內(nèi)容疑似由AI輔助生成,瀏覽時請結(jié)合常識與多方信息審慎甄別。
平臺聲明:文章內(nèi)容(如有圖片或視頻亦包括在內(nèi))由作者上傳并發(fā)布,文章內(nèi)容僅代表作者本人觀點,簡書系信息發(fā)布平臺,僅提供信息存儲服務(wù)。

相關(guān)閱讀更多精彩內(nèi)容

友情鏈接更多精彩內(nèi)容