因为最近被公司派去做 java 后台了, 很多东西都要学习, 后面我学习 java 后台的笔记也会在这里记录, 不然会忘得很快. 欢迎大家一起成长.
今晚抽空再练习一个控件. 这次还是圆形进度条, 继续练习画笔, Canvas中基本画圆 API 的使用, 上效果.
先来分析一波,
- 绘制内圈(360°的圆形)
- 绘制外圈(这里外圈需要绘制为圆弧了, 扫描的那种. 从0扫描360°)
- 绘制文字
- 设置百分比
- 设置属性动画
- 收工.
介于前面已经写过自定义圆形进度条了, 这次直接就不分步骤写了, 直接上代码了, 有详细的注释. 没有看过的同学请戳下面.
自定义View_04(小试牛刀)圆形进度条
1. 新建自定义属性文件 values/attrs.xml
<?xml version="1.0" encoding="utf-8"?>
<resources>
<declare-styleable name="ProgressBar">
<attr name="bottomBackground" format="color"/>
<attr name="topBackground" format="color"/>
<attr name="roundWidth" format="dimension"/>
<attr name="progressTextSize" format="dimension"/>
<attr name="progressTextColor" format="color"/>
</declare-styleable>
</resources>
2. 新建Class文件,继承View
- 继承View
- 重写三个构造函数
- 获取自定义属性文件, 给默认值
- dip 转 px 函数
- sp 转 px 函数
- 初始化三个画笔(底部, 顶部, 文字)
public class ProgressBar extends View {
//底部圆的默认亚瑟
private int mBottomBackground = Color.RED;
//顶部圆的默认颜色
private int mTopBackground = Color.RED;
//注意哦:这里的10是 dp 需要转为px
private int mRoundWidth = 10;
//注意哦:这里的15是 sp 需要转为px
private float mProgressTextSize = 15;
//设置文字的默认颜色
private int mProgressTextColor = Color.RED;
//三个画笔
private Paint mBottomPaint, mTopArcPaint, mTextPaint;
public ProgressBar(Context context) {
this(context, null);
}
public ProgressBar(Context context, @Nullable AttributeSet attrs) {
this(context, attrs, 0);
}
public ProgressBar(Context context, @Nullable AttributeSet attrs, int defStyleAttr) {
super(context, attrs, defStyleAttr);
//获取自定义属性
TypedArray array = context.obtainStyledAttributes(attrs, R.styleable.ProgressBar);
mBottomBackground = array.getColor(R.styleable.ProgressBar_bottomBackground, mBottomBackground);
mTopBackground = array.getColor(R.styleable.ProgressBar_topBackground, mTopBackground);
mProgressTextColor = array.getColor(R.styleable.ProgressBar_progressTextColor, mProgressTextColor);
mRoundWidth = (int) array.getDimension(R.styleable.ProgressBar_roundWidth, dip2px(mRoundWidth));
mProgressTextSize = array.getDimensionPixelSize(R.styleable.ProgressBar_progressTextSize, sp2px(mProgressTextSize));
//初始化三个画笔
initBottomPaint();
initTopArcPaint();
initTextPaint();
//回收, 一定不要忘了这一步
array.recycle();
}
//dip转px,这里为了演示所以才写到这里, 实际开发当中是需要抽到 Utils 类中
private float dip2px(int dip) {
return TypedValue.applyDimension(TypedValue.COMPLEX_UNIT_DIP, dip, getResources().getDisplayMetrics());
}
//sp转px
private int sp2px(float sp) {
return (int) TypedValue.applyDimension(TypedValue.COMPLEX_UNIT_SP, sp, getResources().getDisplayMetrics());
}
//初始化底部画笔
private void initBottomPaint() {
mBottomPaint = new Paint();
//设置抗锯齿
mBottomPaint.setAntiAlias(true);
//设置画笔宽度
mBottomPaint.setStrokeWidth(mRoundWidth);
//设置底部圆的颜色
mBottomPaint.setColor(mBottomBackground);
//设置为画笔只描边
mBottomPaint.setStyle(Paint.Style.STROKE);
}
//初始化顶部画笔
private void initTopArcPaint() {
mTopArcPaint = new Paint();
//抗锯齿
mTopArcPaint.setAntiAlias(true);
//设置顶部画笔宽度
mTopArcPaint.setStrokeWidth(mRoundWidth);
//顶部画笔颜色
mTopArcPaint.setColor(mTopBackground);
//只描边
mTopArcPaint.setStyle(Paint.Style.STROKE);
//设置画笔末端样式为圆形, (底部不设置是因为底部一开始就是一个360°的圆)
mTopArcPaint.setStrokeCap(Paint.Cap.ROUND);
}
//初始化文字画笔
private void initTextPaint() {
mTextPaint = new Paint();
//抗锯齿
mTextPaint.setAntiAlias(true);
//设置文字画笔颜色
mTextPaint.setColor(mProgressTextColor);
//设置文字画笔要绘制文字的大小
mTextPaint.setTextSize(mProgressTextSize);
}
}
3. 测量
准备工作做完后, 开始需要测量大小了. 覆写 onMeausr 方法, 开始测量大小
@Override
protected void onMeasure(int widthMeasureSpec, int heightMeasureSpec) {
super.onMeasure(widthMeasureSpec, heightMeasureSpec);
//获取布局文件中设置的宽高
int width = MeasureSpec.getSize(widthMeasureSpec);
int height = MeasureSpec.getSize(heightMeasureSpec);
//获取后,调用 setMeasuredDimension 设置宽高.
//保证是正方形, 这里我们假设没有设置 padiing, 如果有的话,还需要加上 padiing 的距离.
setMeasuredDimension(Math.min(width, height), Math.min(width, height));
}
4. 绘制
又到了最关键的步骤了. 还记得在 自定义View_04(小试牛刀)圆形进度条 中的步骤吗, 不过现在这里需要绘制底部是一个 360°的圆形,
- 绘制一个圆形 (底部的)
- 绘制一个圆弧 (顶部的), 开始位置是0, 扫描的角度等于 当前进度 / 总进度 * 360.
- 绘制文字.
- 定义当前进度, 总进度变量. 设置当今进度变量的 set 方法
上代码
//总进度
private int mToalProgress = 100;
//当前进度
private int mCurrentProgress = 0;
public synchronized void setCurrentProgress(int currentProgress){
this.mCurrentProgress = currentProgress;
invalidate();
}
/**
* drawCircle 参数说明
* drawCircle(float cx, float cy, float radius, Paint paint)
* cx, 圆心的 x 坐标,
* cy, 圆心的 y 坐标
* radius 圆的半径
* paint 画笔
*/
@Override
protected void onDraw(Canvas canvas) {
//1.设置中心点,画内圆
int center = getWidth() / 2;
// radius 圆的半径 为什么要用 center - 画笔的宽度 / 2 呢 ?,
//因为笔触的宽度, 并不是往笔触内侧增加宽度的, 而是外侧一半, 内侧一半
//最简单,最直白. 意思就是, 加入画笔画的宽度10,
//那么画笔落笔的时候是在中间的位置5, 外侧5,内侧5,画笔就是在中间落笔的, 所以要 / 2
canvas.drawCircle(center, center, center - mRoundWidth / 2, mBottomPaint);
//2.画外圆,不能画圆,只能画圆弧
if (mToalProgress == 0) {
return;
}
float sweepAngle = (float) mCurrentProgress / mToalProgress;
//设置落笔的左上右下, 需要考虑到画笔宽度 / 2 (原因上面已经说明)
//因为我们没有设置padiing, 所以左边起始位置落笔的就是 宽度 /2
//右边的的就需要总的宽度 - 画笔/2 的距离,得到右侧的坐标
RectF rect = new RectF(mRoundWidth / 2, mRoundWidth / 2, getWidth() - mRoundWidth / 2, getHeight() - mRoundWidth / 2);
//参数1: 定义圆弧的形状和大小的范围
//参数2: 设置圆弧是从哪个角度来顺时针绘画的
//参数3: 设置圆弧扫过的角度
//参数4: 圆弧在绘画的时候,是否经过圆心
//参数5: 画笔
canvas.drawArc(rect, 0, sweepAngle * 360, false, mTopArcPaint);
//3.绘制进度文字
String str = ((int)(sweepAngle * 100)) + "%";
//获取文本的bounds
Rect textRect = new Rect();
mTextPaint.getTextBounds(str, 0, str.length(), textRect);
//获取文本的x坐标, 整个空间的宽度/2 - 文本的宽度/2
int dx = getWidth() / 2 - textRect.width() / 2;
//获取文本的y坐标
Paint.FontMetricsInt fontMetricsInt = mTextPaint.getFontMetricsInt();
int dy = (fontMetricsInt.bottom - fontMetricsInt.top) / 2 - fontMetricsInt.bottom;
//计算基线
int baseLine = getHeight() / 2 + dy;
canvas.drawText(str, dx, baseLine, mTextPaint);
}
5. 调用
布局文件
<?xml version="1.0" encoding="utf-8"?>
<RelativeLayout xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:app="http://schemas.android.com/apk/res-auto"
xmlns:tools="http://schemas.android.com/tools"
android:layout_width="match_parent"
android:layout_height="match_parent"
tools:context=".MainActivity">
<org.zyq.ProgressBar
android:id="@+id/progressBar"
android:layout_width="match_parent"
android:layout_height="match_parent"
android:layout_margin="10dp"
app:bottomBackground="@color/colorPrimary"
app:progressTextColor="@color/colorPrimaryDark"
app:progressTextSize="20sp"
app:roundWidth="20dp"
app:topBackground="@color/colorAccent" />
<Button
android:onClick="start"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_below="@id/progressBar"
android:layout_centerInParent="true"
android:layout_marginTop="20dp"
android:text="开始" />
</RelativeLayout>
MainActivity
public class MainActivity extends AppCompatActivity {
ProgressBar progressBar;
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_main);
progressBar = findViewById(R.id.progressBar);
}
//设置属性动画
public void start(View view) {
ValueAnimator valueAnimator = ObjectAnimator.ofFloat(0, 100);
valueAnimator.setDuration(2000);
valueAnimator.start();
valueAnimator.setInterpolator(new DecelerateInterpolator());
valueAnimator.addUpdateListener(new ValueAnimator.AnimatorUpdateListener() {
@Override
public void onAnimationUpdate(ValueAnimator animation) {
float value = (float) animation.getAnimatedValue();
progressBar.setCurrentProgress((int)value);
}
});
}
}
你们的进度条出来了吗? , 反正我的是出来了, 收工睡觉! Q_Q