先来看看效果图,有图才有真相:
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);
这样一个简单的进度条就出现在我们的眼前了:
每次间隔多少时间移动
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);
}
接下来我以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;
}
}
}
主要是处理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
方法。
@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
方法中绘制背景,绘制进度条,绘制文字以及间隔多少时间重绘。
绘制背景:
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