Android實(shí)現(xiàn)絢麗的自定義進(jìn)度條
前言
進(jìn)度條是在Android項(xiàng)目中很常用的組件之一,如果想要理解它是怎么實(shí)現(xiàn)的,首先還需要了解自定義view和Android坐標(biāo)系相關(guān)的知識,下面我來詳細(xì)地介紹一下自定義進(jìn)度條的實(shí)現(xiàn)過程。
本項(xiàng)目源碼:https://gitee.com/tu_erhongjiang/android-progress-bar
效果圖

實(shí)現(xiàn)步驟
1.繪制背景圓形矩形

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

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

下面來看看怎么居中文字:
getWidth() / 2 得到的結(jié)果是中間位置的x坐標(biāo),但是從這里開始繪制文字的話不能實(shí)現(xiàn)居中的效果。所以還需要計算出文字的長度,然后把文字整體左移。mTxtWidth / 2 是文字的中心位置,也就是說文字的中心位置移到矩形的中心位置就可以實(shí)現(xiàn)居中的效果。
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坐標(biāo)
int y = getHeight() / 2 + mTxtHeight / 4; //文字在畫布中的y坐標(biāo)
if (percent < 100) {
canvas.drawText(percent + "%", x, y, mPaintText);
} else {
canvas.drawText(finishedText, x, y, mPaintText);
}
}
4.加入動畫
最后就是加入動畫效果了,讓進(jìn)度條動起來。我這里用到的是屬性動畫中的valueAnimator。這種動畫不能直接修改view,它是類似于timer,需要我們傳遞一個數(shù)值范圍和執(zhí)行時間。比如說3秒內(nèi)從1加到100。然后在接口回調(diào)時拿到當(dāng)前的進(jìn)度,執(zhí)行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();
}
完整代碼
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);//設(shè)置畫筆樣式
mPaintRoundRect.setStrokeWidth(strokeWidth);//設(shè)置畫筆寬度
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;
}
//設(shè)置控件實(shí)際大小
round = mHeight / 2;//圓角半徑
setMeasuredDimension(mWidth, mHeight);
}
@Override
protected void onDraw(Canvas canvas) {
super.onDraw(canvas);
drawBackground(canvas);//繪制背景矩形
drawProgress(canvas);//繪制進(jìn)度
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);//內(nèi)部進(jìn)度條
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坐標(biāo)
int y = getHeight() / 2 + mTxtHeight / 4; //文字在畫布中的y坐標(biāo)
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());
}
//進(jìn)度條默認(rèn)高度,未設(shè)置高度時使用
private int defaultHeight() {
float scale = getContext().getResources().getDisplayMetrics().density;
return (int) (20 * scale + 0.5f * (20 >= 0 ? 1 : -1));
}
}
以上就是橫向進(jìn)度條的實(shí)現(xiàn)步驟,整體來說還是比較簡單的,如果你對Android坐標(biāo)系和canvas比較熟悉的話自定義view實(shí)現(xiàn)起來還是很容易的。
到此這篇關(guān)于Android實(shí)現(xiàn)絢麗的自定義進(jìn)度條的文章就介紹到這了,更多相關(guān)Android自定義進(jìn)度條內(nèi)容請搜索腳本之家以前的文章或繼續(xù)瀏覽下面的相關(guān)文章希望大家以后多多支持腳本之家!
相關(guān)文章
Android實(shí)現(xiàn)錄音監(jiān)聽動畫的示例代碼
在很多app種內(nèi)置了語音助手,也存在各種動畫,這篇文章主要為大家詳細(xì)介紹了Android實(shí)現(xiàn)錄音監(jiān)聽動畫的示例代碼,感興趣的小伙伴可以跟隨小編一起學(xué)習(xí)一下2023-12-12
在Android中動態(tài)添加Panel框架的實(shí)現(xiàn)代碼
項(xiàng)目經(jīng)常會有這種需求,就是想動態(tài)的在某個界面上添加一個Panel。比如,有一個按鈕,點(diǎn)擊后會顯示下拉菜單式的界面。這種需求,就屬于動態(tài)添加一個Panel。需求多了,就要研究是否可以抽象出通用的框架代碼,以方便開發(fā),所以就有了以下內(nèi)容2013-05-05
Convert WebP to PNG using java
本文主要介紹Convert WebP to PNG using java,這里對 WebP 做了詳細(xì)說明,并講解Linux 環(huán)境下WebP 轉(zhuǎn)png格式的示例,有興趣的小伙伴可以參考下2016-08-08
AndroidStudio中重載方法@Override的使用詳解
這篇文章主要介紹了AndroidStudio中重載方法@Override的使用詳解,具有很好的參考價值,希望對大家有所幫助。一起跟隨小編過來看看吧2020-04-04
Android開發(fā)之串口編程原理和實(shí)現(xiàn)方式
提到串口編程,就不得不提到JNI,不得不提到JavaAPI中的文件描述符類:FileDescriptor;下面我分別對JNI、FileDescriptor以及串口的一些知識點(diǎn)和實(shí)現(xiàn)的源碼進(jìn)行分析說明,感興趣的朋友可以了解下2013-01-01
Android實(shí)現(xiàn)壓縮字符串的方法示例
最近在做Android開發(fā),遇到了需要壓縮字符串的功能,下面這篇文章主要給大家介紹了Android實(shí)現(xiàn)壓縮字符串的相關(guān)資料,文中通過示例代碼介紹的非常詳細(xì),對大家的學(xué)習(xí)或者工作具有一定的參考學(xué)習(xí)價值,需要的朋友們下面跟著小編來一起學(xué)習(xí)學(xué)習(xí)吧。2017-08-08
詳解Android 7.0 Settings 加載選項(xiàng)
本篇文章主要介紹了Android 7.0 Settings 加載選項(xiàng),Android 7.0 Settings頂部多了一個建議選項(xiàng),多了個側(cè)邊欄,操作更加便捷了,有興趣的可以了解一下。2017-02-02
ListView的Adapter使用 之 初學(xué)ArrayAdapter String
ListView是Android中經(jīng)常會使用的東西,綁定數(shù)據(jù)對于初學(xué)者來說,尤其是剛接觸編程的人來說,往往會覺得很難理解,我上大二的時候?qū)W的java,但是基本上相當(dāng)于沒有學(xué),什么都沒寫過,真正接觸編程就是開始上手學(xué)android,把這些記錄下來,自己可以回頭看下,也可以讓新手更好的理解2013-06-06
android dialog背景模糊化效果實(shí)現(xiàn)方法
這篇文章主要為大家詳細(xì)介紹了android dialog背景模糊化效果的實(shí)現(xiàn)方法,文中示例代碼介紹的非常詳細(xì),具有一定的參考價值,感興趣的小伙伴們可以參考一下2018-04-04

