目錄
實作步驟:
1.繪制背景圓形矩形
2.繪制進度
3.繪制文字
4.加入影片
前言:
進度條是在Android專案中很常用的組件之一,如果想要理解它是怎么實作的,首先還需要了解自定義view和Android坐標系相關的知識,下面我來詳細地介紹一下自定義進度條的實作程序,
本專案原始碼:
碼云倉庫
https://gitee.com/tu_erhongjiang/android-progress-bar
效果圖:

實作步驟:
1.繪制背景圓形矩形

首先要畫出一個圓形矩形,RectF里面傳遞的是矩形左上角和右下角的xy坐標,這是用來確定矩形的位置和大小,然后在矩形內部畫出一個圓形矩形,
核心代碼:canvas.drawRoundRect()
private void drawBackground(Canvas canvas){
//圓角矩形
RectF rectF = new RectF(padding, padding, mWidth - padding, mHeight - padding);
canvas.drawRoundRect(rectF, round, round, mPaintRoundRect);
}
2.繪制進度

其實里面的進度條也是圓形矩形,只不過進度條的畫筆是實心的,內部進度條矩形的大小需要略小于外面的矩形,這樣就可以實作上面的這種效果,如果進度條矩形大于或等于背景矩形大小的話那就完全壓住背景中的邊框,顯示出來的只是一個沒有邊框的進度條,所以這里需要減掉strokeWidth,
private void drawProgress(Canvas canvas){
if (process!=0){
RectF rectProgress = new RectF(padding + strokeWidth, padding + strokeWidth, process, mHeight - padding - strokeWidth);//內部進度條
canvas.drawRoundRect(rectProgress, round, round, mPaint);
}
}
3.繪制文字

下面來看看怎么居中文字:
getWidth() / 2 得到的結果是中間位置的x坐標,但是從這里開始繪制文字的話不能實作居中的效果,所以還需要計算出文字的長度,然后把文字整體左移,mTxtWidth / 2 是文字的中心位置,也就是說文字的中心位置移到矩形的中心位置就可以實作居中的效果,
private void updateText(Canvas canvas) {
String finishedText = getResources().getString(R.string.finished);
String defaultText = getResources().getString(R.string.defaultText);
int percent = (int) (process / (mWidth - padding - strokeWidth) * 100);
Paint.FontMetrics fm = mPaintText.getFontMetrics();
int mTxtWidth = (int) mPaintText.measureText(finishedText, 0, defaultText.length());
int mTxtHeight = (int) Math.ceil(fm.descent - fm.ascent);
int x = getWidth() / 2 - mTxtWidth / 2; //文字在畫布中的x坐標
int y = getHeight() / 2 + mTxtHeight / 4; //文字在畫布中的y坐標
if (percent < 100) {
canvas.drawText(percent + "%", x, y, mPaintText);
} else {
canvas.drawText(finishedText, x, y, mPaintText);
}
}
4.加入影片
最后就是加入影片效果了,讓進度條動起來,我這里用到的是屬性影片中的valueAnimator,這種影片不能直接修改view,它是類似于timer,需要我們傳遞一個數值范圍和執行時間,比如說3秒內從1加到100,然后在介面回呼時拿到當前的進度,執行view的invalidate()方法,重繪UI,
//屬性影片
public void start() {
ValueAnimator valueAnimator = ValueAnimator.ofFloat(0, mWidth - padding - strokeWidth);
valueAnimator.setDuration(duration);
valueAnimator.setInterpolator(new DecelerateInterpolator());
valueAnimator.addUpdateListener(animation -> {
process = (float) animation.getAnimatedValue();
invalidate();
});
valueAnimator.start();
}
5.完整代碼
package com.example.floatingwindow.widget;
import android.animation.ValueAnimator;
import android.content.Context;
import android.graphics.Canvas;
import android.graphics.Paint;
import android.graphics.RectF;
import android.util.AttributeSet;
import android.util.TypedValue;
import android.view.View;
import android.view.animation.DecelerateInterpolator;
import androidx.annotation.Nullable;
import com.example.floatingwindow.R;
public class HorizontalProgressView extends View {
private Paint mPaint;
private Paint mPaintRoundRect;
private Paint mPaintText;
private int mWidth;
private int mHeight;
private int padding = 5;
private int strokeWidth = 8;
private int textSize = 15;
private long duration = 3500;
private int round;
private float process;
public HorizontalProgressView(Context context) {
super(context);
init();
}
public HorizontalProgressView(Context context, @Nullable AttributeSet attrs) {
super(context, attrs);
init();
}
public HorizontalProgressView(Context context, @Nullable AttributeSet attrs, int defStyleAttr) {
super(context, attrs, defStyleAttr);
init();
}
//初始化畫筆
private void init(){
mPaintRoundRect = new Paint();//圓角矩形
mPaintRoundRect.setColor(getResources().getColor(R.color.back));//圓角矩形顏色
mPaintRoundRect.setAntiAlias(true);// 抗鋸齒效果
mPaintRoundRect.setStyle(Paint.Style.STROKE);//設定畫筆樣式
mPaintRoundRect.setStrokeWidth(strokeWidth);//設定畫筆寬度
mPaint = new Paint();
mPaint.setColor(getResources().getColor(R.color.inner));
mPaint.setStyle(Paint.Style.FILL_AND_STROKE);
mPaint.setAntiAlias(true);
mPaint.setStrokeWidth(strokeWidth);
mPaintText = new Paint();
mPaintText.setAntiAlias(true);
mPaintText.setStyle(Paint.Style.FILL);
mPaintText.setColor(getResources().getColor(R.color.back));
mPaintText.setTextSize(sp2px(textSize));
}
public void setPadding(int padding) {
this.padding = padding;
}
public void setStrokeWidth(int strokeWidth) {
this.strokeWidth = strokeWidth;
}
public void setTextSize(int textSize) {
this.textSize = textSize;
}
public void setDuration(long duration) {
this.duration = duration;
}
@Override
protected void onMeasure(int widthMeasureSpec, int heightMeasureSpec) {
super.onMeasure(widthMeasureSpec, heightMeasureSpec);
int widthSpecMode = MeasureSpec.getMode(widthMeasureSpec);
int heightSpecMode = MeasureSpec.getMode(heightMeasureSpec);
int widthSpecSize = MeasureSpec.getSize(widthMeasureSpec);
int heightSpecSize = MeasureSpec.getSize(heightMeasureSpec);
//MeasureSpec.EXACTLY,精確尺寸
if (widthSpecMode == MeasureSpec.EXACTLY || widthSpecMode == MeasureSpec.AT_MOST) {
mWidth = widthSpecSize;
} else {
mWidth = 0;
}
//MeasureSpec.AT_MOST,最大尺寸,只要不超過父控制元件允許的最大尺寸即可,MeasureSpec.UNSPECIFIED未指定尺寸
if (heightSpecMode == MeasureSpec.AT_MOST || heightSpecMode == MeasureSpec.UNSPECIFIED) {
mHeight = defaultHeight();
} else {
mHeight = heightSpecSize;
}
//設定控制元件實際大小
round = mHeight / 2;//圓角半徑
setMeasuredDimension(mWidth, mHeight);
}
@Override
protected void onDraw(Canvas canvas) {
super.onDraw(canvas);
drawBackground(canvas);//繪制背景矩形
drawProgress(canvas);//繪制進度
updateText(canvas);//處理文字
}
private void drawBackground(Canvas canvas){
RectF rectF = new RectF(padding, padding, mWidth - padding, mHeight - padding);//圓角矩形
canvas.drawRoundRect(rectF, round, round, mPaintRoundRect);
}
private void drawProgress(Canvas canvas){
if (process!=0){
RectF rectProgress = new RectF(padding + strokeWidth, padding + strokeWidth, process, mHeight - padding - strokeWidth);//內部進度條
canvas.drawRoundRect(rectProgress, round, round, mPaint);
}
}
private void updateText(Canvas canvas) {
String finishedText = getResources().getString(R.string.finished);
String defaultText = getResources().getString(R.string.defaultText);
int percent = (int) (process / (mWidth - padding - strokeWidth) * 100);
Paint.FontMetrics fm = mPaintText.getFontMetrics();
int mTxtWidth = (int) mPaintText.measureText(finishedText, 0, defaultText.length());
int mTxtHeight = (int) Math.ceil(fm.descent - fm.ascent);
int x = getWidth() / 2 - mTxtWidth / 2; //文字在畫布中的x坐標
int y = getHeight() / 2 + mTxtHeight / 4; //文字在畫布中的y坐標
if (percent < 100) {
canvas.drawText(percent + "%", x, y, mPaintText);
} else {
canvas.drawText(finishedText, x, y, mPaintText);
}
}
//屬性影片
public void start() {
ValueAnimator valueAnimator = ValueAnimator.ofFloat(0, mWidth - padding - strokeWidth);
valueAnimator.setDuration(duration);
valueAnimator.setInterpolator(new DecelerateInterpolator());
valueAnimator.addUpdateListener(animation -> {
process = (float) animation.getAnimatedValue();
invalidate();
});
valueAnimator.start();
}
private int sp2px(int sp) {
return (int) TypedValue.applyDimension(TypedValue.COMPLEX_UNIT_SP, sp,
getResources().getDisplayMetrics());
}
//進度條默認高度,未設定高度時使用
private int defaultHeight() {
float scale = getContext().getResources().getDisplayMetrics().density;
return (int) (20 * scale + 0.5f * (20 >= 0 ? 1 : -1));
}
}
以上就是橫向進度條的實作步驟,整體來說還是比較簡單的,如果你對Android坐標系和canvas比較熟悉的話自定義view實作起來還是很容易的,
本專案中的圓形進度條的實作方式我在上一篇文章中介紹過,如果有興趣可以看看:
Android自定義view實作圓形進度條
https://blog.csdn.net/daydayup05/article/details/122150161
本專案中的圓形進度條在上一個版本的基礎上做了一些修改和升級,主要是改了文字的居中方式,畫筆大小和顏色等等,最終實作了更精致的圓形進度條,
轉載請註明出處,本文鏈接:https://www.uj5u.com/yidong/403998.html
標籤:其他
上一篇:畢業半個月,我是如何同時做好位元組程式媛、模特與寫作博主的?
下一篇:張雪峰:你實作“財富自由”了嗎?
