Android自定義view實(shí)現(xiàn)多色進(jìn)度條GradientProgressView的繪制
前言
我們常使用shape實(shí)現(xiàn)漸變色,但是shape的極限卻只有三色,如果有超過(guò)三種顏色的View的要求,那么我們就不得不去自定義View來(lái)實(shí)現(xiàn)這個(gè)需求。不過(guò)它的難度也是很低的,實(shí)現(xiàn)起來(lái)很輕松。接下來(lái)我分析一下我的思路。 先上圖:
一、GradientProgressView準(zhǔn)備工作
- color:漸變進(jìn)度條的多種顏色
- 圖形:如果是圓柱形就使用RectF,矩形就用Rect,一個(gè)用于顯示原色進(jìn)度,一個(gè)用于顯示漸變進(jìn)度條
- 漸變?nèi)旧鳎篖inearGradient
- 寬高:mHeight、mWidth
- 圓柱的半徑:mRadius 屬性代碼如下
private int colorStart; // 顏色:進(jìn)度1 private int colorEnd; // 顏色:進(jìn)度5 private int colorStart2;// 顏色:進(jìn)度2 private int colorEnd0;// 顏色:進(jìn)度4 private int colorCenter;// 顏色:進(jìn)度3 private Paint mGradientPaint;// 漸變畫筆 private Paint mPaint;// 默認(rèn)畫筆 private RectF mBackGroundRect; // 原色圖形 private RectF mGradientRect;// 染色圖形 private LinearGradient backGradient;// 漸變色 private int mHeight; private int mWidth; private int mRadius;
二、繪制
1.初始化屬性
因?yàn)檫@個(gè)自定義View只為了滿足公司需求,所有我并未將其的功能擴(kuò)展開(kāi)來(lái),所有沒(méi)有自定義屬性,都是寫死在代碼中,后續(xù)有空再優(yōu)化。 主要三步驟:初始化顏色,初始化畫筆,初始化圖形。 最好先寫死需要的寬高,避免在使用的使用還未測(cè)量就進(jìn)行繪制
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è)置防抖動(dòng) mGradientPaint.setDither(true); mGradientPaint.setStyle(Paint.Style.FILL); //設(shè)置抗鋸齒 mPaint.setAntiAlias(true); //設(shè)置防抖動(dòng) mPaint.setDither(true); mPaint.setStyle(Paint.Style.FILL); mBackGroundRect = new RectF(); // 防止渲染還未完成就顯示,使得寬高還未測(cè)量。先在初始化這里定義好跟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.測(cè)量寬高
這一步一般來(lái)說(shuō)已經(jīng)是有標(biāo)準(zhǔn)的代碼模板了,所以我這里也沒(méi)什么創(chuàng)新的地方,照抄就完事了,這里便淺淺介紹一下測(cè)量的步驟吧。 其流程是:
- 先用
getMode()和getSize()
獲取測(cè)量模式和寬高,再根據(jù)不同的模式獲取寬高。 - 模式有三種:EXACTLY(精確值模式)、AT_MOST(最大值模式)、UNSPECIFIED(未指定模式)
- 如果是EXACTLY(精確值模式),那么該屬性就是確定的,即當(dāng)前View的寬高
- 如果是AT_MOST(最大值模式),那么該屬性就是父容器的寬高
- 如果是UNSPECIFIED(未指定模式),那么該屬性要么是0,要么是EXACTLY(精確值模式)下當(dāng)前view的屬性。 用圖來(lái)說(shuō)明,即為:
@Override protected void onMeasure(int widthMeasureSpec, int heightMeasureSpec) { super.onMeasure(widthMeasureSpec, heightMeasureSpec); /** * 從提供的測(cè)量規(guī)格中獲取測(cè)量模式和寬高值 */ 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) { //測(cè)量模式是EXACTLY,說(shuō)明view的寬度值是確定的 mWidth = widthSize; } else if (widthMode == MeasureSpec.AT_MOST) { /** * 測(cè)量模式是AT_MOST,說(shuō)明view的寬度值最大是父View能提供的大小 * 比如開(kāi)發(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); //保存計(jì)算后的寬高 setMeasuredDimension(mWidth,mHeight); }
3.根據(jù)情況來(lái)畫漸變色進(jìn)度
這里因?yàn)樾枨笤颍覍⒃镜倪M(jìn)度條五色平分進(jìn)度,所以這里用到已經(jīng)測(cè)量好的寬度屬性mWidth
。 這里的流程也很簡(jiǎn)單,簡(jiǎn)要說(shuō)下
- 暴露方法給外界調(diào)用
- 獲取進(jìn)度
- 根據(jù)進(jìn)度來(lái)給漸變?nèi)旧鳎篖inearGradient初始化
- 同時(shí)給圓柱形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); // 因?yàn)闈u變色集合,需要顏色大于等于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.繪制
這里我采用最簡(jiǎn)單粗暴的方法,直接疊了兩個(gè)圓柱進(jìn)度條,一個(gè)是原色的進(jìn)度條,一個(gè)是漸變色的進(jìn)度條,這里大家可以自行優(yōu)化,即在backGradient 屬性初始化的時(shí)候進(jìn)度調(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; // 顏色:進(jìn)度1 private int colorEnd; // 顏色:進(jìn)度5 private int colorStart2;// 顏色:進(jìn)度2 private int colorEnd0;// 顏色:進(jìn)度4 private int colorCenter;// 顏色:進(jìn)度3 private List<Integer> colorList; // 顏色集合 private List<Float> colorSize; // 顏色位置 private Paint mGradientPaint;// 漸變畫筆 private Paint mPaint;// 默認(rèn)畫筆 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è)置防抖動(dòng) mGradientPaint.setDither(true); mGradientPaint.setStyle(Paint.Style.FILL); //設(shè)置抗鋸齒 mPaint.setAntiAlias(true); //設(shè)置防抖動(dòng) mPaint.setDither(true); mPaint.setStyle(Paint.Style.FILL); mBackGroundRect = new RectF(); // 防止渲染還未完成就顯示,使得寬高還未測(cè)量。先在初始化這里定義好跟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); /** * 從提供的測(cè)量規(guī)格中獲取測(cè)量模式和寬高值 */ 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) { //測(cè)量模式是EXACTLY,說(shuō)明view的寬度值是確定的 mWidth = widthSize; } else if (widthMode == MeasureSpec.AT_MOST) { /** * 測(cè)量模式是AT_MOST,說(shuō)明view的寬度值最大是父View能提供的大小 * 比如開(kāi)發(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); //保存計(jì)算后的寬高 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); // 因?yàn)闈u變色集合,需要顏色大于等于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é)
還是因?yàn)闀r(shí)間問(wèn)題,該自定義View只是單純滿足需求,并沒(méi)有太大的擴(kuò)展性功能,后續(xù)有時(shí)間我也行會(huì)出一篇后續(xù)將自定義View補(bǔ)上,實(shí)現(xiàn)一個(gè)功能性較為強(qiáng)大的漸變色進(jìn)度條。 自定義View的魅力就是當(dāng)你實(shí)現(xiàn)功能需求的時(shí)候的滿足感,不亞于以前做完一道數(shù)學(xué)大題。那種暢快和欣喜,老實(shí)說(shuō)我有點(diǎn)愛(ài)上自定義View了。
到此這篇關(guān)于Android自定義view實(shí)現(xiàn)多色進(jìn)度條GradientProgressView的繪制的文章就介紹到這了,更多相關(guān)Android自定義view繪制多色進(jìn)度條內(nèi)容請(qǐng)搜索腳本之家以前的文章或繼續(xù)瀏覽下面的相關(guān)文章希望大家以后多多支持腳本之家!
- Android自定義View實(shí)現(xiàn)水平帶數(shù)字百分比進(jìn)度條
- Android自定義View實(shí)現(xiàn)圓環(huán)帶數(shù)字百分比進(jìn)度條
- Android自定義View實(shí)現(xiàn)炫酷進(jìn)度條
- Android自定義View實(shí)現(xiàn)投票進(jìn)度條
- Android如何自定義View實(shí)現(xiàn)橫向的雙水波紋進(jìn)度條
- Android自定義View實(shí)現(xiàn)圓形進(jìn)度條
- Android自定義View實(shí)現(xiàn)進(jìn)度條動(dòng)畫
相關(guān)文章
Android實(shí)現(xiàn)驗(yàn)證碼登錄
這篇文章主要為大家詳細(xì)介紹了Android實(shí)現(xiàn)驗(yàn)證碼登錄,文中示例代碼介紹的非常詳細(xì),具有一定的參考價(jià)值,感興趣的小伙伴們可以參考一下2021-03-03android ListView和GridView拖拽移位實(shí)現(xiàn)代碼
有些朋友對(duì)android中ListView和GridView拖拽移位功能的實(shí)現(xiàn)不是很了解,接下來(lái)將詳細(xì)介紹,需要了解的朋友可以參考下2012-12-12Android中使用include標(biāo)簽和merge標(biāo)簽重復(fù)使用布局
這篇文章主要介紹了Android中使用include標(biāo)簽和merge標(biāo)簽重復(fù)使用布局,文中講解了創(chuàng)建可復(fù)用布局的例子以及include標(biāo)簽和merge標(biāo)簽使用例子,需要的朋友可以參考下2014-06-06Android仿微信和QQ多圖合并框架(類似群頭像)的實(shí)現(xiàn)方法
這篇文章主要給大家介紹了關(guān)于Android仿微信和QQ多圖合并框架的相關(guān)資料,其實(shí)就是我們平時(shí)所見(jiàn)的群聊頭像,文中通過(guò)示例代碼介紹的非常詳細(xì),對(duì)各位Android開(kāi)發(fā)者們具有一定的參考學(xué)習(xí)價(jià)值,需要的朋友們下面隨著小編來(lái)一起學(xué)習(xí)學(xué)習(xí)吧。2017-12-12Android如何幫助用戶自動(dòng)接聽(tīng)或者掛斷來(lái)電
這篇文章主要為大家詳細(xì)介紹了Android幫助用戶自動(dòng)接聽(tīng)或者掛斷來(lái)電,具有一定的參考價(jià)值,感興趣的小伙伴們可以參考一下2018-05-05Android開(kāi)發(fā)學(xué)習(xí)之WallPaper設(shè)置壁紙?jiān)敿?xì)介紹與實(shí)例
這篇文章主要介紹了Android開(kāi)發(fā)學(xué)習(xí)之WallPaper設(shè)置壁紙?jiān)敿?xì)介紹與實(shí)例,有需要的朋友可以參考一下2013-12-12