拿到項(xiàng)目需求時(shí),不要立馬就去寫(xiě)代碼琢唾,先想下實(shí)現(xiàn)的思路载荔,大致涉及到哪些技術(shù)點(diǎn),哪種實(shí)現(xiàn)方式比較好采桃,就拿上面的效果說(shuō)懒熙,系統(tǒng)的ProgressBar肯定實(shí)現(xiàn)不了該效果,那就要自定義view芍碧,自定義view大致包含以下幾步:
1煌珊、自定義屬性的聲明與獲取
2、測(cè)量onMeasure
3泌豆、布局onLayout(ViewGroup布局容器)
4定庵、繪制onDraw
5、onTouchEvent(涉及用戶手勢(shì)交互)
6踪危、onInterceptTouchEvent(處理父view和子view之間的事件攔截)
7蔬浙、狀態(tài)的恢復(fù)與保存
對(duì)于該效果:
橫向進(jìn)度條包含:左邊一個(gè)進(jìn)度、中間一個(gè)文字贞远、右邊一個(gè)進(jìn)度
圓形進(jìn)度條:最外層一個(gè)圓畴博、加載進(jìn)度的圓弧、顯示進(jìn)度的中間文字
并沒(méi)有涉及到view擺放蓝仲、用戶交互行為俱病,大致就只需要第一、第二袱结、第四亮隙、第七步;實(shí)現(xiàn)的流程大致確定后垢夹,考慮是繼承自view還是ProgressBar溢吻,上面的效果只是對(duì)系統(tǒng)ProgressBar的效果進(jìn)行改動(dòng),繼承自ProgressBar就可以了果元,這樣可以使用系統(tǒng)ProgressBar的一些屬性和方法促王,通過(guò)getProgress()和setProgress()方法就可以獲取和設(shè)置加載進(jìn)度犀盟,不用像繼承自view,還有給view添加屬性動(dòng)畫(huà)來(lái)實(shí)現(xiàn)加載進(jìn)度蝇狼,考慮清楚了那就開(kāi)始擼代碼吧阅畴。
按照流程第一步自定義屬性的定義和獲取:
<declare-styleable name="ProgressBarView">
<attr name="progress_unreach_color" format="color"></attr>
<attr name="progress_height" format="dimension"></attr>
<attr name="progress_reach_color" format="color"></attr>
<attr name="progress_text_color" format="color"></attr>
<attr name="progress_text_size" format="dimension"></attr>
<attr name="progress_text_offset" format="dimension"></attr>
</declare-styleable>
<declare-styleable name="RoundProgressBar">
<attr name="radius" format="dimension"></attr>
</declare-styleable>
/**
* 獲取自定義屬性
*
* @param attrs
*/
private void obtainStyledAttrs(AttributeSet attrs) {
TypedArray ta = getContext().obtainStyledAttributes(attrs, R.styleable.ProgressBarView);
mTextSize = ta.getDimensionPixelSize(R.styleable.ProgressBarView_progress_text_size, sp2px(mTextSize));
mTextColor = ta.getColor(R.styleable.ProgressBarView_progress_text_color, mTextColor);
mUnReachColor = ta.getColor(R.styleable.ProgressBarView_progress_unreach_color, mUnReachColor);
mProgressHeight = (int) ta.getDimension(R.styleable.ProgressBarView_progress_height, dp2px(mProgressHeight));
mReachColor = ta.getColor(R.styleable.ProgressBarView_progress_reach_color, mReachColor);
mTextOffset = (int) ta.getDimension(R.styleable.ProgressBarView_progress_text_offset, dp2px(mTextOffset));
ta.recycle();
}
第一步弄好了题翰,就是第二步測(cè)量恶阴,測(cè)量的時(shí)候需要注意控件的寬高模式,根據(jù)設(shè)置的寬高模式來(lái)測(cè)量和指定控件的寬高豹障;
@Override
protected synchronized void onMeasure(int widthMeasureSpec, int heightMeasureSpec) {
int widthVal = MeasureSpec.getSize(widthMeasureSpec);
int height = measureHeight(heightMeasureSpec);
setMeasuredDimension(widthVal, height);
//計(jì)算progressbar真正寬度=控件的寬度-paddingleft-paddingright
mRealWith = getMeasuredWidth() - getPaddingLeft() - getPaddingRight();
}
private int measureHeight(int heightMeasureSpec) {
int result = 0;
//獲取高度模式
int mode = MeasureSpec.getMode(heightMeasureSpec);
//獲取寬度模式
int size = MeasureSpec.getSize(heightMeasureSpec);
if (mode == MeasureSpec.EXACTLY) {
//精準(zhǔn)模式 用戶設(shè)置為 比如80dp match_parent fill_parent
result = size;
} else {
//計(jì)算中間文字的高度
int textHeight = (int) (mPaint.descent() - mPaint.ascent());
//paddingTop+paddingBottom+ progressbar高度和文字高度的最大值
result = getPaddingTop() + getPaddingBottom() + Math.max(mProgressHeight, Math.abs(textHeight));
if (mode == MeasureSpec.AT_MOST) {
result = Math.min(result, size);
}
}
return result;
}
測(cè)量完成后冯事,就可以進(jìn)行onDraw繪制了,因?yàn)橐淖兿到y(tǒng)ProgressBar效果就不需要super.onDraw(canvas);
@Override
protected synchronized void onDraw(Canvas canvas) {
//保存畫(huà)布
canvas.save();
//移動(dòng)畫(huà)布
canvas.translate(getPaddingLeft(), getHeight() / 2);
//定義變量用來(lái)控制是否要繪制右邊progressbar 如果寬度不夠的時(shí)候就不進(jìn)行繪制
boolean noNeedUnRech = false;
//計(jì)算左邊進(jìn)度在整個(gè)控件寬度的占比
float radio = getProgress() * 1.0f / getMax();
//獲取左邊進(jìn)度的寬度
float progressX = radio * mRealWith;
//中間文字
String text = getProgress() + "%";
//獲取文字的寬度
int textWidth = (int) mPaint.measureText(text);
if (progressX + textWidth > mRealWith) {
//左邊進(jìn)度+文字的寬度超過(guò)progressbar的寬度 重新計(jì)算左邊進(jìn)度的寬度 這個(gè)時(shí)候也就意味著不需要繪制右邊進(jìn)度
progressX = mRealWith - textWidth;
noNeedUnRech = true;
}
//計(jì)算左邊進(jìn)度結(jié)束的位置 如果結(jié)束的位置小于0就不需要繪制左邊的進(jìn)度
float endX = progressX - mTextOffset / 2;
if (endX > 0) {
//繪制左邊進(jìn)度
mPaint.setColor(mReachColor);
mPaint.setStrokeWidth(mProgressHeight);
canvas.drawLine(0, 0, endX, 0, mPaint);
}
mPaint.setColor(mTextColor);
if (getProgress() != 0) {
//計(jì)算文字基線
int y = (int) (-(mPaint.descent() + mPaint.ascent()) / 2);
//繪制文字
canvas.drawText(text, progressX, y, mPaint);
}
if (!noNeedUnRech) {
//右邊進(jìn)度的開(kāi)始位置=左邊進(jìn)度+文字間距的一半+文字寬度
float start;
if (getProgress() == 0) {
start = progressX;
} else {
start = progressX + mTextOffset / 2 + textWidth;
}
mPaint.setColor(mUnReachColor);
mPaint.setStrokeWidth(mProgressHeight);
//繪制右邊進(jìn)度
canvas.drawLine(start, 0, mRealWith, 0, mPaint);
}
//重置畫(huà)布
canvas.restore();
}
完整代碼:
/**
* 長(zhǎng)方形進(jìn)度條
*/
public class ProgressBarView extends ProgressBar {
//字體大小
protected int mTextSize = 12;
//字體顏色
protected int mTextColor = Color.BLACK;
//沒(méi)有到達(dá)(右邊progressbar的顏色)
protected int mUnReachColor = Color.GREEN;
//progressbar的高度
protected int mProgressHeight = 6;
//progressbar進(jìn)度的顏色
protected int mReachColor = mTextColor;
//字體間距
protected int mTextOffset = 10;
protected Paint mPaint;
//progressbar真正的寬度
protected int mRealWith;
public ProgressBarView(Context context) {
this(context, null);
}
public ProgressBarView(Context context, AttributeSet attrs) {
this(context, attrs, 0);
}
public ProgressBarView(Context context, AttributeSet attrs, int defStyleAttr) {
super(context, attrs, defStyleAttr);
//獲取自定義屬性
obtainStyledAttrs(attrs);
mPaint = new Paint();
mPaint.setTextSize(mTextSize);
}
/**
* 獲取自定義屬性
*
* @param attrs
*/
private void obtainStyledAttrs(AttributeSet attrs) {
TypedArray ta = getContext().obtainStyledAttributes(attrs, R.styleable.ProgressBarView);
mTextSize = ta.getDimensionPixelSize(R.styleable.ProgressBarView_progress_text_size, sp2px(mTextSize));
mTextColor = ta.getColor(R.styleable.ProgressBarView_progress_text_color, mTextColor);
mUnReachColor = ta.getColor(R.styleable.ProgressBarView_progress_unreach_color, mUnReachColor);
mProgressHeight = (int) ta.getDimension(R.styleable.ProgressBarView_progress_height, dp2px(mProgressHeight));
mReachColor = ta.getColor(R.styleable.ProgressBarView_progress_reach_color, mReachColor);
mTextOffset = (int) ta.getDimension(R.styleable.ProgressBarView_progress_text_offset, dp2px(mTextOffset));
ta.recycle();
}
@Override
protected synchronized void onMeasure(int widthMeasureSpec, int heightMeasureSpec) {
int widthVal = MeasureSpec.getSize(widthMeasureSpec);
int height = measureHeight(heightMeasureSpec);
setMeasuredDimension(widthVal, height);
//計(jì)算progressbar真正寬度=控件的寬度-paddingleft-paddingright
mRealWith = getMeasuredWidth() - getPaddingLeft() - getPaddingRight();
}
@Override
protected synchronized void onDraw(Canvas canvas) {
//保存畫(huà)布
canvas.save();
//移動(dòng)畫(huà)布
canvas.translate(getPaddingLeft(), getHeight() / 2);
//定義變量用來(lái)控制是否要繪制右邊progressbar 如果寬度不夠的時(shí)候就不進(jìn)行繪制
boolean noNeedUnRech = false;
//計(jì)算左邊進(jìn)度在整個(gè)控件寬度的占比
float radio = getProgress() * 1.0f / getMax();
//獲取左邊進(jìn)度的寬度
float progressX = radio * mRealWith;
//中間文字
String text = getProgress() + "%";
//獲取文字的寬度
int textWidth = (int) mPaint.measureText(text);
if (progressX + textWidth > mRealWith) {
//左邊進(jìn)度+文字的寬度超過(guò)progressbar的寬度 重新計(jì)算左邊進(jìn)度的寬度 這個(gè)時(shí)候也就意味著不需要繪制右邊進(jìn)度
progressX = mRealWith - textWidth;
noNeedUnRech = true;
}
//計(jì)算左邊進(jìn)度結(jié)束的位置 如果結(jié)束的位置小于0就不需要繪制左邊的進(jìn)度
float endX = progressX - mTextOffset / 2;
if (endX > 0) {
//繪制左邊進(jìn)度
mPaint.setColor(mReachColor);
mPaint.setStrokeWidth(mProgressHeight);
canvas.drawLine(0, 0, endX, 0, mPaint);
}
mPaint.setColor(mTextColor);
if (getProgress() != 0) {
//計(jì)算文字基線
int y = (int) (-(mPaint.descent() + mPaint.ascent()) / 2);
//繪制文字
canvas.drawText(text, progressX, y, mPaint);
}
if (!noNeedUnRech) {
//右邊進(jìn)度的開(kāi)始位置=左邊進(jìn)度+文字間距的一半+文字寬度
float start;
if (getProgress() == 0) {
start = progressX;
} else {
start = progressX + mTextOffset / 2 + textWidth;
}
mPaint.setColor(mUnReachColor);
mPaint.setStrokeWidth(mProgressHeight);
//繪制右邊進(jìn)度
canvas.drawLine(start, 0, mRealWith, 0, mPaint);
}
//重置畫(huà)布
canvas.restore();
}
private int measureHeight(int heightMeasureSpec) {
int result = 0;
//獲取高度模式
int mode = MeasureSpec.getMode(heightMeasureSpec);
//獲取寬度模式
int size = MeasureSpec.getSize(heightMeasureSpec);
if (mode == MeasureSpec.EXACTLY) {
//精準(zhǔn)模式 用戶設(shè)置為 比如80dp match_parent fill_parent
result = size;
} else {
//計(jì)算中間文字的高度
int textHeight = (int) (mPaint.descent() - mPaint.ascent());
//paddingTop+paddingBottom+ progressbar高度和文字高度的最大值
result = getPaddingTop() + getPaddingBottom() + Math.max(mProgressHeight, Math.abs(textHeight));
if (mode == MeasureSpec.AT_MOST) {
result = Math.min(result, size);
}
}
return result;
}
protected int dp2px(int dip) {
return (int) TypedValue.applyDimension(TypedValue.COMPLEX_UNIT_DIP, dip, getResources().getDisplayMetrics());
}
protected int sp2px(int sp) {
return (int) TypedValue.applyDimension(TypedValue.COMPLEX_UNIT_SP, sp, getResources().getDisplayMetrics());
}
}
圓形加載進(jìn)度條效果的話血公,文字和顏色可以共用橫向進(jìn)度條的昵仅,所有就讓圓形進(jìn)度條直接繼承自橫向進(jìn)度條;
/**
* 原型進(jìn)度條
*/
public class RoundProgressBar extends ProgressBarView {
//半徑
private int mRadius = 30;
private int mMaxPaintWidth;
public RoundProgressBar(Context context) {
this(context, null);
}
public RoundProgressBar(Context context, AttributeSet attrs) {
this(context, attrs, 0);
}
public RoundProgressBar(Context context, AttributeSet attrs, int defStyleAttr) {
super(context, attrs, defStyleAttr);
//繪制圓形進(jìn)度條的寬度 這里設(shè)置為長(zhǎng)方形進(jìn)度條高度的1.5倍
mRealWith = (int) (mProgressHeight * 1.5f);
TypedArray typedArray = context.obtainStyledAttributes(attrs, R.styleable.RoundProgressBar);
mRadius = (int) typedArray.getDimension(R.styleable.RoundProgressBar_radius, dp2px(mRadius));
typedArray.recycle();
mPaint.setStyle(Paint.Style.STROKE);
mPaint.setAntiAlias(true);
mPaint.setDither(true);
mPaint.setStrokeCap(Paint.Cap.ROUND);
}
@Override
protected synchronized void onMeasure(int widthMeasureSpec, int heightMeasureSpec) {
mMaxPaintWidth = mProgressHeight;
//計(jì)算控件的精準(zhǔn)值
int expect = mRadius * 2 + mMaxPaintWidth + getPaddingLeft() + getPaddingRight();
int width = resolveSize(expect, widthMeasureSpec);
int height = resolveSize(expect, heightMeasureSpec);
int readWidth = Math.min(width, height);
mRadius = (readWidth - getPaddingRight() - getPaddingLeft() - mMaxPaintWidth) / 2;
setMeasuredDimension(readWidth, readWidth);
}
@Override
protected synchronized void onDraw(Canvas canvas) {
//獲取當(dāng)前進(jìn)度
String text = getProgress() + "%";
//測(cè)量文字的寬度
float textWidth = mPaint.measureText(text);
//文字的高度
float textHeight = (mPaint.descent() + mPaint.ascent()) / 2;
//保存畫(huà)布
canvas.save();
//平移畫(huà)布位置
canvas.translate(getPaddingLeft() + mMaxPaintWidth / 2, getPaddingTop() + mMaxPaintWidth / 2);
mPaint.setStyle(Paint.Style.STROKE);
mPaint.setColor(mUnReachColor);
mPaint.setStrokeWidth(mRealWith);
//繪制圓 要注意繪制圓的x y 因?yàn)樯厦鎸?duì)畫(huà)布進(jìn)行了平移所以這里就不需要計(jì)算了累魔,如果畫(huà)布沒(méi)有進(jìn)行平移需要計(jì)算 x y
canvas.drawCircle(mRadius, mRadius, mRadius, mPaint);
//繪制圓弧
mPaint.setColor(mReachColor);
mPaint.setStrokeWidth(mReachColor);
//計(jì)算圓弧的掃過(guò)的幅度
float sweepAngle = getProgress() * 1.0f / getMax() * 360;
RectF rectF = new RectF(0, 0, mRadius * 2, mRadius * 2);
canvas.drawArc(rectF, 0, sweepAngle, false, mPaint);
//繪制中間文字
mPaint.setColor(mTextColor);
mPaint.setStyle(Paint.Style.FILL);
canvas.drawText(text, mRadius - textWidth / 2, mRadius - textHeight, mPaint);
canvas.restore();
}
}
自定義的代碼就基本完成了摔笤,在調(diào)用的時(shí)候也比較簡(jiǎn)單,不需要像繼承自View那樣給View設(shè)置屬性動(dòng)畫(huà)又調(diào)用invalidate進(jìn)行重繪垦写,直接調(diào)用setProgress方法設(shè)置當(dāng)前的進(jìn)度就可以了吕世;
public class MainActivity extends AppCompatActivity {
private ProgressBarView progressBarView,progressBarView1;
private RoundProgressBar roundProgressBar,roundProgressBar1;
private static final int MSG_UPDATA = 0X110;
private Handler handler = new Handler() {
@Override
public void handleMessage(Message msg) {
super.handleMessage(msg);
int progress = progressBarView.getProgress();
progressBarView.setProgress(++progress);
progressBarView1.setProgress(++progress);
roundProgressBar.setProgress(++progress);
roundProgressBar1.setProgress(++progress);
if (progress >= 100) {
handler.removeMessages(MSG_UPDATA);
}
handler.sendEmptyMessageDelayed(MSG_UPDATA, 100);
}
};
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_main);
progressBarView = findViewById(R.id.progress_bar_view);
roundProgressBar = findViewById(R.id.round_progressbar);
progressBarView1=findViewById(R.id.progress_bar_view1);
roundProgressBar1=findViewById(R.id.round_progressbar1);
handler.sendEmptyMessageDelayed(MSG_UPDATA, 1000);
}
}