Android自定义View实现可拖拽的进度条

龙旋

共 5379字,需浏览 11分钟

 ·

2021-09-12 12:28

目录


效果展示



实现步骤

1、计算出控件宽度的直线路径


在onSizeChanged方法中进行计算,这时可以得到一条与控件宽度相同的直线,并把路径设置给PathMeasure

    @Override    protected void onSizeChanged(int w, int h, int oldw, int oldh) {        super.onSizeChanged(w, h, oldw, oldh);        //进度条绘制在控件中央,宽度为控件宽度(mProgressHeight/2是为了显示出左右两边的圆角)        mPathProgressBg.moveTo(mProgressHeight / 2,h / 2f);        mPathProgressBg.lineTo(w - mProgressHeight / 2,h / 2f);        //将进度条路径设置给PathMeasure        mPathMeasure.setPath(mPathProgressBg,false);        invalidate();    }



2、计算当前进度的路径

使用PathMeasure得出当前进度的路径并进行绘制,这里我将上一步的绘制放在了一起

private void drawProgress(Canvas canvas) {        mPathProgressFg.reset();        mPaintProgress.setColor(mColorProgressBg);        //绘制进度背景        canvas.drawPath(mPathProgressBg, mPaintProgress);        //计算进度条的进度        float stop = mPathMeasure.getLength() * mProgress;        //得到与进度对应的路径        mPathMeasure.getSegment(0,stop,mPathProgressFg,true);        mPaintProgress.setColor(mColorProgressFg);        //绘制进度        canvas.drawPath(mPathProgressFg, mPaintProgress);    }



3、计算显示进度的圆角矩形

这个矩形的宽度需要我们用绘制最长的文字来确定其宽高



另外矩形的显示位置也是以当前进度所在的点为中心点



private void drawShowProgressRoundRect(Canvas canvas) {        float stop = mPathMeasure.getLength() * mProgress;//计算进度条的进度        //根据要绘制的文字的最大长宽来计算要绘制的圆角矩形的长宽        Rect rect = new Rect();        mPaintProgressText.getTextBounds(mProgressMaxText,0, mProgressMaxText.length(),rect);        //要绘制矩形的宽、高        float rectWidth = rect.width() + (mProgressStrMarginH * 2);        float rectHeight = rect.height() + (mProgressStrMarginV * 2);        //计算边界值(为了不让矩形在左右两边超出边界)        if(stop < rectWidth / 2f){            stop = rectWidth / 2f;        }else if(stop > (getWidth() - rectWidth / 2f)){            stop = getWidth() - rectWidth / 2f;        }        //定义绘制的矩形        float left = stop - rectWidth / 2f;        float right = stop + rectWidth / 2f;        float top = getHeight() / 2f - rectHeight / 2f;        float bottom = getHeight() / 2f + rectHeight / 2f;        mProgressRoundRectF = new RectF(left,top,right,bottom);        //绘制为圆角矩形        canvas.drawRoundRect(mProgressRoundRectF, mRoundRectRadius, mRoundRectRadius,mPaintRoundRect);    }



4、计算文字的显示位置

文字显示的位置计算起来就比较简单了,直接用上一步算出的矩形的中心点即可,不过这里需要调整文字绘制的垂直的偏移,这样才能实现文字垂直居中

private void drawProgressText(Canvas canvas) {        String progressText = (int)Math.floor(100 * mProgress) + "%";        //让文字垂直居中的偏移        int offsetY = (mFontMetricsInt.bottom - mFontMetricsInt.ascent) / 2 - mFontMetricsInt.bottom;        //将文字绘制在矩形的中央        canvas.drawText(progressText,mProgressRoundRectF.centerX(),mProgressRoundRectF.centerY() + offsetY,mPaintProgressText);    }



5、实现拖拽

实现拖拽需要对onTouchEvent方法进行处理,也就是当手指触摸矩形区域的时候,根据手指横向滑动的偏移来设置当前的进度,具体如下:

    @Override    public boolean onTouchEvent(MotionEvent event) {        switch (event.getAction()){            case MotionEvent.ACTION_DOWN:                //判断手指是否触摸了显示进度的圆角矩形块,这样才可以拖拽                if(mProgressRoundRectF != null && mProgressRoundRectF.contains(event.getX(),event.getY())){                    //记录手指刚接触屏幕的X轴坐标(因为只需要在X轴上平移)                    mStartTouchX = event.getX();                    mIsTouchSeek = true;                }                break;            case MotionEvent.ACTION_MOVE:                if(mIsTouchSeek){                    //计算横向移动的距离                    float moveX = event.getX() - mStartTouchX;                    //计算出当前进度的X轴所显示的进度长度                    float currentProgressWidth = mPathMeasure.getLength() * mProgress;//计算进度条的进度                    //计算滑动后的X轴的坐标                    float showProgressWidth = currentProgressWidth + moveX;                    //计算边界值                    if(showProgressWidth < 0){                        showProgressWidth = 0;                    }else if(showProgressWidth > mPathMeasure.getLength()){                        showProgressWidth = mPathMeasure.getLength();                    }                    //计算滑动后的进度                    mProgress = showProgressWidth / mPathMeasure.getLength();                    //重绘                    invalidate();                    //刷新用于计算移动的X轴坐标                    mStartTouchX = event.getX();                }                break;            case MotionEvent.ACTION_UP:                mIsTouchSeek = false;                break;        }        return mIsTouchSeek;    }


6、计算当前自定义View的宽高

为了适配高度的wrap_content属性,我们需要计算出控件最小需要显示的高度



这里我们是用显示进度的矩形的高度作为控件最小的高度的,因为矩形的高度是所有图形最高的一个



而矩形的高度又是文字的大小与边距之和

 @Override    protected void onMeasure(int widthMeasureSpec, int heightMeasureSpec) {        setMeasuredDimension(measureSizeWidth(widthMeasureSpec), measureSizeHeight(heightMeasureSpec));    }
//计算宽度 private int measureSizeWidth(int size) { int mode = MeasureSpec.getMode(size); int s = MeasureSpec.getSize(size); if (mode == MeasureSpec.EXACTLY) { return s; } else{ return Math.min(s, 200); } } //计算高度 private int measureSizeHeight(int size) { int mode = MeasureSpec.getMode(size); int s = MeasureSpec.getSize(size); if (mode == MeasureSpec.EXACTLY) { return s; }else { //自适应模式,返回所需的最小高度 return (int) (mTextSize + mProgressStrMarginV * 2); } }


源码地址:

https://gitee.com/itfitness/seek-progress-bar

浏览 107
点赞
评论
收藏
分享

手机扫一扫分享

分享
举报
评论
图片
表情
推荐
点赞
评论
收藏
分享

手机扫一扫分享

分享
举报