Android自定義view實現(xiàn)多色進度條GradientProgressView的繪制
前言
我們常使用shape實現(xiàn)漸變色,但是shape的極限卻只有三色,如果有超過三種顏色的View的要求,那么我們就不得不去自定義View來實現(xiàn)這個需求。不過它的難度也是很低的,實現(xiàn)起來很輕松。接下來我分析一下我的思路。 先上圖:

一、GradientProgressView準備工作
- color:漸變進度條的多種顏色
- 圖形:如果是圓柱形就使用RectF,矩形就用Rect,一個用于顯示原色進度,一個用于顯示漸變進度條
- 漸變?nèi)旧鳎篖inearGradient
- 寬高:mHeight、mWidth
- 圓柱的半徑:mRadius 屬性代碼如下
private int colorStart; // 顏色:進度1
private int colorEnd; // 顏色:進度5
private int colorStart2;// 顏色:進度2
private int colorEnd0;// 顏色:進度4
private int colorCenter;// 顏色:進度3
private Paint mGradientPaint;// 漸變畫筆
private Paint mPaint;// 默認畫筆
private RectF mBackGroundRect; // 原色圖形
private RectF mGradientRect;// 染色圖形
private LinearGradient backGradient;// 漸變色
private int mHeight;
private int mWidth;
private int mRadius;二、繪制
1.初始化屬性
因為這個自定義View只為了滿足公司需求,所有我并未將其的功能擴展開來,所有沒有自定義屬性,都是寫死在代碼中,后續(xù)有空再優(yōu)化。 主要三步驟:初始化顏色,初始化畫筆,初始化圖形。 最好先寫死需要的寬高,避免在使用的使用還未測量就進行繪制
private void init(Context context, @Nullable AttributeSet attrs){
// 初始化畫筆
mGradientPaint = new Paint(Paint.ANTI_ALIAS_FLAG);
mPaint = new Paint(Paint.ANTI_ALIAS_FLAG);
colorStart = Color.parseColor("#63DC80");
colorStart2 = Color.parseColor("#A8DB5B");
colorCenter = Color.parseColor("#FFCE48");
colorEnd0 = Color.parseColor("#FE7B39");
colorEnd = Color.parseColor("#E8402B");
mPaint.setColor(Color.parseColor("#EDEDED"));
if (attrs!=null){
}
//設置抗鋸齒
mGradientPaint.setAntiAlias(true);
//設置防抖動
mGradientPaint.setDither(true);
mGradientPaint.setStyle(Paint.Style.FILL);
//設置抗鋸齒
mPaint.setAntiAlias(true);
//設置防抖動
mPaint.setDither(true);
mPaint.setStyle(Paint.Style.FILL);
mBackGroundRect = new RectF();
// 防止渲染還未完成就顯示,使得寬高還未測量。先在初始化這里定義好跟xml中相同的寬高
mWidth = (int) (Resources.getSystem().getDisplayMetrics().widthPixels-context.getResources().getDimension(R.dimen.base_dp_56));
mHeight = (int) (context.getResources().getDimension(R.dimen.base_dp_8));
}2.測量寬高
這一步一般來說已經(jīng)是有標準的代碼模板了,所以我這里也沒什么創(chuàng)新的地方,照抄就完事了,這里便淺淺介紹一下測量的步驟吧。 其流程是:
- 先用
getMode()和getSize()獲取測量模式和寬高,再根據(jù)不同的模式獲取寬高。 - 模式有三種:EXACTLY(精確值模式)、AT_MOST(最大值模式)、UNSPECIFIED(未指定模式)
- 如果是EXACTLY(精確值模式),那么該屬性就是確定的,即當前View的寬高
- 如果是AT_MOST(最大值模式),那么該屬性就是父容器的寬高
- 如果是UNSPECIFIED(未指定模式),那么該屬性要么是0,要么是EXACTLY(精確值模式)下當前view的屬性。 用圖來說明,即為:

@Override
protected void onMeasure(int widthMeasureSpec, int heightMeasureSpec) {
super.onMeasure(widthMeasureSpec, heightMeasureSpec);
/**
* 從提供的測量規(guī)格中獲取測量模式和寬高值
*/
int widthMode = MeasureSpec.getMode(widthMeasureSpec);
int widthSize = MeasureSpec.getSize(widthMeasureSpec);
int heightMode = MeasureSpec.getMode(heightMeasureSpec);
int heightSize = MeasureSpec.getSize(heightMeasureSpec);
Log.d("TAG", "onMeasure: "+widthMode);
Log.d("TAG", "onMeasure: "+widthSize);
if (widthMode == MeasureSpec.EXACTLY) {
//測量模式是EXACTLY,說明view的寬度值是確定的
mWidth = widthSize;
} else if (widthMode == MeasureSpec.AT_MOST) {
/**
* 測量模式是AT_MOST,說明view的寬度值最大是父View能提供的大小
* 比如開發(fā)者設置wrap_content,那可能是希望填充父View
*/
mWidth = Math.max(mWidth,widthSize);
}
if (heightMode == MeasureSpec.EXACTLY) {
mHeight = heightSize;
} else if (heightMode == MeasureSpec.AT_MOST) {
mHeight = Math.min(mHeight,heightSize);
}
/**
* 為了View能更好的展示,需要設置下寬高比
*/
float times = mWidth / (float)mHeight;
if (times < 2.5 ) {
if (mHeight < DensityUtil.dip2px(getContext(),60)) {
mHeight = DensityUtil.dip2px(getContext(),60);
}
mWidth = (int) (mHeight * 2.5);
}
mRadius = (int) getContext().getResources().getDimension(R.dimen.base_dp_20);
//保存計算后的寬高
setMeasuredDimension(mWidth,mHeight);
}3.根據(jù)情況來畫漸變色進度
這里因為需求原因,我將原本的進度條五色平分進度,所以這里用到已經(jīng)測量好的寬度屬性mWidth 。 這里的流程也很簡單,簡要說下
- 暴露方法給外界調(diào)用
- 獲取進度
- 根據(jù)進度來給漸變?nèi)旧鳎篖inearGradient初始化
- 同時給圓柱形RectF初始化寬高
- 最后調(diào)用
invalidate()重新繪制該View
public void setAirType(String degree){
int level = mWidth / 5;
switch (degree){
case "1":
mGradientRect = new RectF(0,0,level,mHeight);
// 因為漸變色集合,需要顏色大于等于2,這里取巧,設置相同顏色
backGradient = new LinearGradient(0, 0, level, 0, new int[]{colorStart,colorStart}, null, Shader.TileMode.CLAMP);
break;
case "2":
mGradientRect = new RectF(0,0,level*2,mHeight);
backGradient = new LinearGradient(0, 0, level*2, 0, new int[]{colorStart, colorStart2}, null, Shader.TileMode.CLAMP);
break;
case "3":
mGradientRect = new RectF(0,0,level*3,mHeight);
backGradient = new LinearGradient(0, 0, level*3, 0, new int[]{colorStart, colorStart2,colorCenter}, null, Shader.TileMode.CLAMP);
break;
case "4":
mGradientRect = new RectF(0,0,level*4,mHeight);
backGradient = new LinearGradient(0, 0, level*4, 0, new int[]{colorStart, colorStart2,colorCenter,colorEnd0}, null, Shader.TileMode.CLAMP);
break;
case "5":
mGradientRect = new RectF(0,0,mWidth,mHeight);
backGradient = new LinearGradient(0, 0, mWidth, 0, new int[]{colorStart, colorStart2,colorCenter,colorEnd0,colorEnd}, null, Shader.TileMode.CLAMP);
break;
}
invalidate();
}4.繪制
這里我采用最簡單粗暴的方法,直接疊了兩個圓柱進度條,一個是原色的進度條,一個是漸變色的進度條,這里大家可以自行優(yōu)化,即在backGradient 屬性初始化的時候進度調(diào)整等。
@Override
protected void onDraw(Canvas canvas) {
super.onDraw(canvas);
drawRoundRect(canvas);
}
private void drawRoundRect(Canvas canvas) {
mBackGroundRect.left = 0;
mBackGroundRect.top = 0;
mBackGroundRect.bottom = mHeight;
mBackGroundRect.right = mWidth;
// 繪制底色圓角矩形
canvas.drawRoundRect(mBackGroundRect, mRadius, mRadius, mPaint);
// 漸變繪圖
mGradientPaint.setShader(backGradient);
//繪制背景 圓角矩形
if (mGradientRect != null) {
canvas.drawRoundRect(mGradientRect, mRadius, mRadius, mGradientPaint);
}
}完整代碼貼上,方便各位友友使用
public class GradientProgressView extends View {
private int colorStart; // 顏色:進度1
private int colorEnd; // 顏色:進度5
private int colorStart2;// 顏色:進度2
private int colorEnd0;// 顏色:進度4
private int colorCenter;// 顏色:進度3
private List<Integer> colorList; // 顏色集合
private List<Float> colorSize; // 顏色位置
private Paint mGradientPaint;// 漸變畫筆
private Paint mPaint;// 默認畫筆
private RectF mBackGroundRect; // 原色圖形
private RectF mGradientRect;// 染色圖形
private LinearGradient backGradient;// 漸變色
private int mHeight;
private int mWidth;
private int mRadius;
public GradientProgressView(Context context) {
super(context);
}
public GradientProgressView(Context context, @Nullable AttributeSet attrs) {
super(context, attrs);
init(context,attrs);
}
public GradientProgressView(Context context, @Nullable AttributeSet attrs, int defStyleAttr) {
super(context, attrs, defStyleAttr);
}
private void init(Context context, @Nullable AttributeSet attrs){
// 初始化畫筆
mGradientPaint = new Paint(Paint.ANTI_ALIAS_FLAG);
mPaint = new Paint(Paint.ANTI_ALIAS_FLAG);
colorStart = Color.parseColor("#63DC80");
colorStart2 = Color.parseColor("#A8DB5B");
colorCenter = Color.parseColor("#FFCE48");
colorEnd0 = Color.parseColor("#FE7B39");
colorEnd = Color.parseColor("#E8402B");
mPaint.setColor(Color.parseColor("#EDEDED"));
if (attrs!=null){
}
//設置抗鋸齒
mGradientPaint.setAntiAlias(true);
//設置防抖動
mGradientPaint.setDither(true);
mGradientPaint.setStyle(Paint.Style.FILL);
//設置抗鋸齒
mPaint.setAntiAlias(true);
//設置防抖動
mPaint.setDither(true);
mPaint.setStyle(Paint.Style.FILL);
mBackGroundRect = new RectF();
// 防止渲染還未完成就顯示,使得寬高還未測量。先在初始化這里定義好跟xml中相同的寬高
mWidth = (int) (Resources.getSystem().getDisplayMetrics().widthPixels-context.getResources().getDimension(R.dimen.base_dp_56));
mHeight = (int) (context.getResources().getDimension(R.dimen.base_dp_8));
}
@Override
protected void onMeasure(int widthMeasureSpec, int heightMeasureSpec) {
super.onMeasure(widthMeasureSpec, heightMeasureSpec);
/**
* 從提供的測量規(guī)格中獲取測量模式和寬高值
*/
int widthMode = MeasureSpec.getMode(widthMeasureSpec);
int widthSize = MeasureSpec.getSize(widthMeasureSpec);
int heightMode = MeasureSpec.getMode(heightMeasureSpec);
int heightSize = MeasureSpec.getSize(heightMeasureSpec);
Log.d("TAG", "onMeasure: "+widthMode);
Log.d("TAG", "onMeasure: "+widthSize);
if (widthMode == MeasureSpec.EXACTLY) {
//測量模式是EXACTLY,說明view的寬度值是確定的
mWidth = widthSize;
} else if (widthMode == MeasureSpec.AT_MOST) {
/**
* 測量模式是AT_MOST,說明view的寬度值最大是父View能提供的大小
* 比如開發(fā)者設置wrap_content,那可能是希望填充父View
*/
mWidth = Math.max(mWidth,widthSize);
}
if (heightMode == MeasureSpec.EXACTLY) {
mHeight = heightSize;
} else if (heightMode == MeasureSpec.AT_MOST) {
mHeight = Math.min(mHeight,heightSize);
}
/**
* 為了View能更好的展示,需要設置下寬高比
*/
float times = mWidth / (float)mHeight;
if (times < 2.5 ) {
if (mHeight < DensityUtil.dip2px(getContext(),60)) {
mHeight = DensityUtil.dip2px(getContext(),60);
}
mWidth = (int) (mHeight * 2.5);
}
mRadius = (int) getContext().getResources().getDimension(R.dimen.base_dp_20);
//保存計算后的寬高
setMeasuredDimension(mWidth,mHeight);
}
@Override
protected void onSizeChanged(int w, int h, int oldw, int oldh) {
super.onSizeChanged(w, h, oldw, oldh);
mWidth = w;
mHeight = h;
}
public void setAirType(String degree){
int level = mWidth / 5;
switch (degree){
case "1":
mGradientRect = new RectF(0,0,level,mHeight);
// 因為漸變色集合,需要顏色大于等于2,這里取巧,設置相同顏色
backGradient = new LinearGradient(0, 0, level, 0, new int[]{colorStart,colorStart}, null, Shader.TileMode.CLAMP);
break;
case "2":
mGradientRect = new RectF(0,0,level*2,mHeight);
backGradient = new LinearGradient(0, 0, level*2, 0, new int[]{colorStart, colorStart2}, null, Shader.TileMode.CLAMP);
break;
case "3":
mGradientRect = new RectF(0,0,level*3,mHeight);
backGradient = new LinearGradient(0, 0, level*3, 0, new int[]{colorStart, colorStart2,colorCenter}, null, Shader.TileMode.CLAMP);
break;
case "4":
mGradientRect = new RectF(0,0,level*4,mHeight);
backGradient = new LinearGradient(0, 0, level*4, 0, new int[]{colorStart, colorStart2,colorCenter,colorEnd0}, null, Shader.TileMode.CLAMP);
break;
case "5":
mGradientRect = new RectF(0,0,mWidth,mHeight);
backGradient = new LinearGradient(0, 0, mWidth, 0, new int[]{colorStart, colorStart2,colorCenter,colorEnd0,colorEnd}, null, Shader.TileMode.CLAMP);
break;
}
invalidate();
}
@Override
protected void onDraw(Canvas canvas) {
super.onDraw(canvas);
drawRoundRect(canvas);
}
private void drawRoundRect(Canvas canvas) {
mBackGroundRect.left = 0;
mBackGroundRect.top = 0;
mBackGroundRect.bottom = mHeight;
mBackGroundRect.right = mWidth;
// 繪制底色圓角矩形
canvas.drawRoundRect(mBackGroundRect, mRadius, mRadius, mPaint);
// 漸變繪圖
mGradientPaint.setShader(backGradient);
//繪制背景 圓角矩形
if (mGradientRect != null) {
canvas.drawRoundRect(mGradientRect, mRadius, mRadius, mGradientPaint);
}
}
}xml調(diào)用
<com.itaem.blviewtest.GradientProgressView
android:id="@+id/gradientProgress"
android:layout_width="match_parent"
android:layout_height="@dimen/base_dp_8"
android:layout_marginTop="12dp"
android:layout_marginHorizontal="@dimen/base_dp_44"/>總結(jié)
還是因為時間問題,該自定義View只是單純滿足需求,并沒有太大的擴展性功能,后續(xù)有時間我也行會出一篇后續(xù)將自定義View補上,實現(xiàn)一個功能性較為強大的漸變色進度條。 自定義View的魅力就是當你實現(xiàn)功能需求的時候的滿足感,不亞于以前做完一道數(shù)學大題。那種暢快和欣喜,老實說我有點愛上自定義View了。
到此這篇關(guān)于Android自定義view實現(xiàn)多色進度條GradientProgressView的繪制的文章就介紹到這了,更多相關(guān)Android自定義view繪制多色進度條內(nèi)容請搜索腳本之家以前的文章或繼續(xù)瀏覽下面的相關(guān)文章希望大家以后多多支持腳本之家!
相關(guān)文章
android ListView和GridView拖拽移位實現(xiàn)代碼
有些朋友對android中ListView和GridView拖拽移位功能的實現(xiàn)不是很了解,接下來將詳細介紹,需要了解的朋友可以參考下2012-12-12
Android中使用include標簽和merge標簽重復使用布局
這篇文章主要介紹了Android中使用include標簽和merge標簽重復使用布局,文中講解了創(chuàng)建可復用布局的例子以及include標簽和merge標簽使用例子,需要的朋友可以參考下2014-06-06
Android仿微信和QQ多圖合并框架(類似群頭像)的實現(xiàn)方法
這篇文章主要給大家介紹了關(guān)于Android仿微信和QQ多圖合并框架的相關(guān)資料,其實就是我們平時所見的群聊頭像,文中通過示例代碼介紹的非常詳細,對各位Android開發(fā)者們具有一定的參考學習價值,需要的朋友們下面隨著小編來一起學習學習吧。2017-12-12
Android開發(fā)學習之WallPaper設置壁紙詳細介紹與實例
這篇文章主要介紹了Android開發(fā)學習之WallPaper設置壁紙詳細介紹與實例,有需要的朋友可以參考一下2013-12-12

