Android自定义之圆形进度条

先来看看效果图,有图才有真相:

Usage

Android Studio 使用Gradle构建

dependencies { compile 'com.github.ws.circleprogress:circleprogress:2.0.4' }

Maven

<dependency>
  <groupId>com.github.ws.circleprogress</groupId>
  <artifactId>circleprogress</artifactId>
  <version>2.0.4</version>
  <type>pom</type>
</dependency>

eclipse

请下载https://github.com/HpWens/ProgressDemo,依赖circleprogress

完成上面的依赖后,在布局文件中声明控件:

            <com.example.circleprogress.widget.CircleProgressBar
                android:id="@+id/cpb0"
                android:layout_width="200dp"
                android:layout_height="200dp" />

CircleProgressBar可以替换为CircleProgressArc或者CircleProgressRise,替换成哪种进度条,根据你具体情况而定。

Activity文件中:

        cpb = (CircleProgressBar) findViewById(R.id.cpb1);

这样一个简单的进度条就出现在我们的眼前了:

Android自定义之圆形进度条_第1张图片

Attribute

每次间隔多少时间移动

setDelayTime(int delayTime)

是否加速移动 默认加速移动

setIsAcc(boolean mBool)

设置文字颜色

setTextColor(int color)

设置最大进度

setMinProgress(int minProgress)

设置最小进度

setMinProgress(int minProgress)

设置文字大小

setTextSize(float percent) //0.0f到1.0f

设置背景 空心圆大小

setBackgroundStoreWidth(int width)

其他的一些属性

    /** * 设置背景颜色 * * @param color */
    public void setBackgroundColor(int color) {
        backgroundPaint.setColor(color);
    }

    /** * 设置背景风格 * * @param style */
    public void setBackgroundStyle(Paint.Style style) {
        backgroundPaint.setStyle(style);
    }

    /** * 设置bar空心宽度大小 * * @param width */
    public void setBarStoreWidth(int width) {
        barPaint.setStrokeWidth(DensityUtil.dip2px(mContext, width));
    }

    /** * 设置bar的颜色 * * @param color */
    public void setBarColor(int color) {
        barPaint.setColor(color);
    }

    /** * 设置bar风格 * * @param style */
    public void setBarStyle(Paint.Style style) {
        barPaint.setStyle(style);
    }

Realization

接下来我以Bar进度条为例,讲讲实现过程。原理非常简单:间隔多少时间重绘界面画弧度。需要注意的是:不要直接new Handler(),可能会引起OOM,原因是:非静态的匿名内部类持有外部类的一个引用。推荐使用方法:

    protected static class MyHandler extends Handler {
        private WeakReference<BaseCircleProgress> activityWeakReference;

        public MyHandler(BaseCircleProgress circle) {
            activityWeakReference = new WeakReference<BaseCircleProgress>(circle);
        }

        @Override
        public void handleMessage(Message msg) {
            BaseCircleProgress circle = activityWeakReference.get();
            if (circle == null) {
                return;
            }
        }
    }

onMeasure

主要是处理wrap_content无效的问题。我这里就直接贴代码,没什么技巧在里面:

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

        int width;
        int height;
        int widthSpecMode = MeasureSpec.getMode(widthMeasureSpec);
        int widthSpecSize = MeasureSpec.getSize(widthMeasureSpec);
        int heightSpecMode = MeasureSpec.getMode(heightMeasureSpec);
        int heightSpecSize = MeasureSpec.getSize(heightMeasureSpec);

        if (widthSpecMode == MeasureSpec.AT_MOST && heightSpecMode == MeasureSpec.EXACTLY) {
            height = heightSpecSize;
            width = Math.min(heightSpecSize, Math.min(DensityUtil.getScreenSize(mContext)[0], DensityUtil.getScreenSize(mContext)[1]));
        } else if (widthSpecMode == MeasureSpec.EXACTLY && heightSpecMode == MeasureSpec.AT_MOST) {
            width = widthSpecSize;
            height = Math.min(widthSpecSize, Math.min(DensityUtil.getScreenSize(mContext)[0], DensityUtil.getScreenSize(mContext)[1]));
        } else if (widthSpecMode == MeasureSpec.AT_MOST && heightSpecMode == MeasureSpec.AT_MOST) {
            width = height = Math.min(DensityUtil.getScreenSize(mContext)[0], DensityUtil.getScreenSize(mContext)[1]);
        } else {
            width = widthSpecSize;
            height = heightSpecSize;
        }
        setMeasuredDimension(width, height);
    }

由于我们不需要设定控件位置,所有不需要处理onLayout方法。

onSizeChanged

  @Override
    protected void onSizeChanged(int w, int h, int oldw, int oldh) {
        super.onSizeChanged(w, h, oldw, oldh);
        circleCenterX = w / 2;
        circleCenterY = h / 2;
        circleRadius = (int) (Math.min(circleCenterX, circleCenterY) * CIRCLE_PERCENT);
    }

设定了圆形X,Y轴的坐标,半径的长度。

onDraw

我们需要在onDraw方法中绘制背景,绘制进度条,绘制文字以及间隔多少时间重绘。

绘制背景:

    private void drawBackground(Canvas canvas) {
        canvas.drawCircle(circleCenterX, circleCenterY, circleRadius, backgroundPaint);
    }

绘制进度条:

    private void drawBar(Canvas canvas) {
        RectF f = new RectF(circleCenterX - circleRadius, circleCenterY - circleRadius, circleCenterX + circleRadius, circleCenterY + circleRadius);
        canvas.drawArc(f, 0f, ((float) currentProgress / MAX_PROGRESS) * ROUND_ANGLE, false, barPaint);
    }

绘制文字:

    private void drawText(Canvas canvas) {
        Paint.FontMetrics fontMetrics = textPaint.getFontMetrics();
        float fontHeight = fontMetrics.descent - fontMetrics.ascent; //文字的高度
        textPaint.setTextAlign(Paint.Align.CENTER);
        textPaint.setTextSize(circleRadius * textPercent);
        canvas.drawText(currentProgress + mUnit, circleCenterX, circleCenterY + (fontHeight / 4), textPaint);
    }

注意:fontHeight / 4一直有争议,设定文字位于圆的中心,本应fontHeight / 2,可是测试的时候文字往下移动了,静止不动的时候是正确的,可是运动起来就不对了。

更新进度条:

    private void updateProgress() {
        if (currentProgress < maxProgress) {
            currentProgress++;
            if (isAcc) {
                delayTime--;
            }
            handler.postDelayed(new Runnable() {
                @Override
                public void run() {
                    postInvalidate();
                }
            }, delayTime);
        }
    }

当然你可能有更好的建议和方案,可以给我留言。学习很多时候就是在积累经验。源码地址请关注https://github.com/HpWens/ProgressDemo

你可能感兴趣的:(android,progress,circle)