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){ } //設(shè)置抗鋸齒 mGradientPaint.setAntiAlias(true); //設(shè)置防抖動 mGradientPaint.setDither(true); mGradientPaint.setStyle(Paint.Style.FILL); //設(shè)置抗鋸齒 mPaint.setAntiAlias(true); //設(shè)置防抖動 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ā)者設(shè)置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能更好的展示,需要設(shè)置下寬高比 */ 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,這里取巧,設(shè)置相同顏色 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){ } //設(shè)置抗鋸齒 mGradientPaint.setAntiAlias(true); //設(shè)置防抖動 mGradientPaint.setDither(true); mGradientPaint.setStyle(Paint.Style.FILL); //設(shè)置抗鋸齒 mPaint.setAntiAlias(true); //設(shè)置防抖動 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ā)者設(shè)置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能更好的展示,需要設(shè)置下寬高比 */ 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,這里取巧,設(shè)置相同顏色 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-12Android中使用include標簽和merge標簽重復使用布局
這篇文章主要介紹了Android中使用include標簽和merge標簽重復使用布局,文中講解了創(chuàng)建可復用布局的例子以及include標簽和merge標簽使用例子,需要的朋友可以參考下2014-06-06Android仿微信和QQ多圖合并框架(類似群頭像)的實現(xiàn)方法
這篇文章主要給大家介紹了關(guān)于Android仿微信和QQ多圖合并框架的相關(guān)資料,其實就是我們平時所見的群聊頭像,文中通過示例代碼介紹的非常詳細,對各位Android開發(fā)者們具有一定的參考學習價值,需要的朋友們下面隨著小編來一起學習學習吧。2017-12-12Android開發(fā)學習之WallPaper設(shè)置壁紙詳細介紹與實例
這篇文章主要介紹了Android開發(fā)學習之WallPaper設(shè)置壁紙詳細介紹與實例,有需要的朋友可以參考一下2013-12-12