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

目录

效果展示

实现步骤

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

©著作权归作者所有,转载或内容合作请联系作者
  • 序言:七十年代末,一起剥皮案震惊了整个滨河市,随后出现的几起案子,更是在滨河造成了极大的恐慌,老刑警刘岩,带你破解...
    沈念sama阅读 206,013评论 6 481
  • 序言:滨河连续发生了三起死亡事件,死亡现场离奇诡异,居然都是意外死亡,警方通过查阅死者的电脑和手机,发现死者居然都...
    沈念sama阅读 88,205评论 2 382
  • 文/潘晓璐 我一进店门,熙熙楼的掌柜王于贵愁眉苦脸地迎上来,“玉大人,你说我怎么就摊上这事。” “怎么了?”我有些...
    开封第一讲书人阅读 152,370评论 0 342
  • 文/不坏的土叔 我叫张陵,是天一观的道长。 经常有香客问我,道长,这世上最难降的妖魔是什么? 我笑而不...
    开封第一讲书人阅读 55,168评论 1 278
  • 正文 为了忘掉前任,我火速办了婚礼,结果婚礼上,老公的妹妹穿的比我还像新娘。我一直安慰自己,他们只是感情好,可当我...
    茶点故事阅读 64,153评论 5 371
  • 文/花漫 我一把揭开白布。 她就那样静静地躺着,像睡着了一般。 火红的嫁衣衬着肌肤如雪。 梳的纹丝不乱的头发上,一...
    开封第一讲书人阅读 48,954评论 1 283
  • 那天,我揣着相机与录音,去河边找鬼。 笑死,一个胖子当着我的面吹牛,可吹牛的内容都是我干的。 我是一名探鬼主播,决...
    沈念sama阅读 38,271评论 3 399
  • 文/苍兰香墨 我猛地睁开眼,长吁一口气:“原来是场噩梦啊……” “哼!你这毒妇竟也来了?” 一声冷哼从身侧响起,我...
    开封第一讲书人阅读 36,916评论 0 259
  • 序言:老挝万荣一对情侣失踪,失踪者是张志新(化名)和其女友刘颖,没想到半个月后,有当地人在树林里发现了一具尸体,经...
    沈念sama阅读 43,382评论 1 300
  • 正文 独居荒郊野岭守林人离奇死亡,尸身上长有42处带血的脓包…… 初始之章·张勋 以下内容为张勋视角 年9月15日...
    茶点故事阅读 35,877评论 2 323
  • 正文 我和宋清朗相恋三年,在试婚纱的时候发现自己被绿了。 大学时的朋友给我发了我未婚夫和他白月光在一起吃饭的照片。...
    茶点故事阅读 37,989评论 1 333
  • 序言:一个原本活蹦乱跳的男人离奇死亡,死状恐怖,灵堂内的尸体忽然破棺而出,到底是诈尸还是另有隐情,我是刑警宁泽,带...
    沈念sama阅读 33,624评论 4 322
  • 正文 年R本政府宣布,位于F岛的核电站,受9级特大地震影响,放射性物质发生泄漏。R本人自食恶果不足惜,却给世界环境...
    茶点故事阅读 39,209评论 3 307
  • 文/蒙蒙 一、第九天 我趴在偏房一处隐蔽的房顶上张望。 院中可真热闹,春花似锦、人声如沸。这庄子的主人今日做“春日...
    开封第一讲书人阅读 30,199评论 0 19
  • 文/苍兰香墨 我抬头看了看天上的太阳。三九已至,却和暖如春,着一层夹袄步出监牢的瞬间,已是汗流浃背。 一阵脚步声响...
    开封第一讲书人阅读 31,418评论 1 260
  • 我被黑心中介骗来泰国打工, 没想到刚下飞机就差点儿被人妖公主榨干…… 1. 我叫王不留,地道东北人。 一个月前我还...
    沈念sama阅读 45,401评论 2 352
  • 正文 我出身青楼,却偏偏与公主长得像,于是被迫代替她去往敌国和亲。 传闻我的和亲对象是个残疾皇子,可洞房花烛夜当晚...
    茶点故事阅读 42,700评论 2 345

推荐阅读更多精彩内容