Android自定義View實(shí)現(xiàn)漸變色進(jìn)度條
在網(wǎng)上看到一個(gè)進(jìn)度條效果圖,非常美觀,如下:
進(jìn)行效果分解:
1.漸變色,看起來顏色變化并不復(fù)雜,使用LinearGradient應(yīng)該可以實(shí)現(xiàn)。
2.圓頭,無非是畫兩個(gè)圓,外圓使用漸變色的顏色,內(nèi)圓固定為白色。
3.灰底,還沒有走到的進(jìn)度部分為灰色。
4.進(jìn)度值,使用文本來顯示;
5.弧形的頭部,考慮使用直線進(jìn)行連接,或者使用曲線,例如貝塞爾曲線;
我首先初步實(shí)現(xiàn)了進(jìn)度條的模樣,發(fā)現(xiàn)樣子有了,卻不太美觀。
反思了一下,我只是個(gè)寫代碼的,對于哪種比例比較美觀,是沒有清晰的認(rèn)識(shí)的,所以,還是參考原圖吧。
然后就進(jìn)行了精細(xì)的測量:
將圖像放大4倍,進(jìn)行測量,然后獲取到各部分的比例關(guān)系,具體過程就不細(xì)說了,說一下測量結(jié)果(按比例的):
視圖總長300,其中前面留空5,進(jìn)度長258,然后再留空5,顯示文本占26,后面留空6;
高度分為4個(gè):
外圓:10
字高:9
內(nèi)圓:6
線粗:5
考慮上下各留空10,則視圖的高度為30。
考慮到視圖整體的效果,可以由用戶來設(shè)置長度值與高度值,按比例取最小值來進(jìn)行繪圖。
首先計(jì)算出一個(gè)單位的實(shí)際像素?cái)?shù),各部分按比例來顯示即可。
還有一個(gè)弧形的頭部,是怎么實(shí)現(xiàn)的呢?
在放大之后,能看出來圖形比較簡單,看不出有弧度,那么,使用一小段直線連接就可以了。
估算這小段直線:線粗為2,呈30度角,長為8-10即可,連接直線與弧頂,起點(diǎn)在弧頂之左下方。
注意:在進(jìn)度的起點(diǎn)時(shí),不能畫出。避免出現(xiàn)一個(gè)很突兀的小尾巴。在2%進(jìn)度之后,才開始畫。
在文字的繪制過程中,遇到一個(gè)小問題,就是文字不居中,略微偏下,上網(wǎng)查了下,原因是這樣的:我們繪制文本時(shí),使用的這個(gè)函數(shù):canvas.drawText(“30%”, x, y, paint);
其中的參數(shù) y 是指字符串baseline的的位置,不是文本的中心。通過計(jì)算可以調(diào)整為居中,如下:
//計(jì)算坐標(biāo)使文字居中 FontMetrics fontMetrics = mPaint.getFontMetrics(); float fontHeight = fontMetrics.bottom - fontMetrics.top; float baseY = height/2 + fontHeight/2 - fontMetrics.bottom;
按比例來繪制之后,就確實(shí)是原來那個(gè)修長優(yōu)雅的感覺了。
實(shí)際運(yùn)行后,發(fā)現(xiàn)字體偏小,不太適合豎屏觀看,調(diào)大了些。
另外對于參數(shù),做了如下幾個(gè)自定義屬性:
前景色:開始顏色,結(jié)束顏色;
進(jìn)度條未走到時(shí)的默認(rèn)顏色,
字體顏色。
屬性xml如下:
<?xml version="1.0" encoding="utf-8"?> <resources> <attr name="startColor" format="color" /> <attr name="endColor" format="color" /> <attr name="backgroundColor" format="color" /> <attr name="textColor" format="color" /> <declare-styleable name="GoodProgressView"> <attr name="startColor" /> <attr name="endColor" /> <attr name="backgroundColor" /> <attr name="textColor" /> </declare-styleable> </resources>
自定義View文件:
package com.customview.view; import android.content.Context; import android.content.res.TypedArray; import android.graphics.Canvas; import android.graphics.Color; import android.graphics.LinearGradient; import android.graphics.Paint; import android.graphics.Shader; import android.graphics.Paint.Cap; import android.graphics.Paint.FontMetrics; import android.graphics.Paint.Style; import android.util.AttributeSet; import android.util.Log; import android.view.View; import com.customview.R; public class GoodProgressView extends View { private int[] mColors = { Color.RED, Color.MAGENTA};//進(jìn)度條顏色(漸變色的2個(gè)點(diǎn)) private int backgroundColor = Color.GRAY;//進(jìn)度條默認(rèn)顏色 private int textColor = Color.GRAY;//文本顏色 private Paint mPaint;//畫筆 private int progressValue=0;//進(jìn)度值 // private RectF rect;//繪制范圍 public GoodProgressView(Context context, AttributeSet attrs) { this(context, attrs, 0); } public GoodProgressView(Context context) { this(context, null); } // 獲得我自定義的樣式屬性 public GoodProgressView(Context context, AttributeSet attrs, int defStyle) { super(context, attrs, defStyle); // 獲得我們所定義的自定義樣式屬性 TypedArray a = context.getTheme().obtainStyledAttributes(attrs, R.styleable.GoodProgressView, defStyle, 0); int n = a.getIndexCount(); for (int i = 0; i < n; i++) { int attr = a.getIndex(i); switch (attr) { case R.styleable.GoodProgressView_startColor: // 漸變色之起始顏色,默認(rèn)設(shè)置為紅色 mColors[0] = a.getColor(attr, Color.RED); break; case R.styleable.GoodProgressView_endColor: // 漸變色之結(jié)束顏色,默認(rèn)設(shè)置為品紅 mColors[1] = a.getColor(attr, Color.MAGENTA); break; case R.styleable.GoodProgressView_backgroundColor: // 進(jìn)度條默認(rèn)顏色,默認(rèn)設(shè)置為灰色 backgroundColor = a.getColor(attr, Color.GRAY); break; case R.styleable.GoodProgressView_textColor: // 文字顏色,默認(rèn)設(shè)置為灰色 textColor = a.getColor(attr, Color.GRAY); break; } } a.recycle(); mPaint = new Paint(); progressValue=0; } public void setProgressValue(int progressValue){ if(progressValue>100){ progressValue = 100; } this.progressValue = progressValue; Log.i("customView","log: progressValue="+progressValue); } public void setColors(int[] colors){ mColors = colors; } @Override protected void onMeasure(int widthMeasureSpec, int heightMeasureSpec) { int width = 0; int height = 0; /** * 設(shè)置寬度 */ int specMode = MeasureSpec.getMode(widthMeasureSpec); int specSize = MeasureSpec.getSize(widthMeasureSpec); switch (specMode) { case MeasureSpec.EXACTLY:// 明確指定了 width = specSize; break; case MeasureSpec.AT_MOST:// 一般為WARP_CONTENT width = getPaddingLeft() + getPaddingRight() ; break; } /** * 設(shè)置高度 */ specMode = MeasureSpec.getMode(heightMeasureSpec); specSize = MeasureSpec.getSize(heightMeasureSpec); switch (specMode) { case MeasureSpec.EXACTLY:// 明確指定了 height = specSize; break; case MeasureSpec.AT_MOST:// 一般為WARP_CONTENT height = width/10; break; } Log.i("customView","log: w="+width+" h="+height); setMeasuredDimension(width, height); } @Override protected void onDraw(Canvas canvas) { super.onDraw(canvas); int mWidth = getMeasuredWidth(); int mHeight = getMeasuredHeight(); //按比例計(jì)算進(jìn)度條各部分的值 float unit = Math.min(((float)mWidth)/300, ((float)mHeight)/30); float lineWidth = 5*unit;//線粗 float innerCircleDiameter = 6*unit;//內(nèi)圓直徑 float outerCircleDiameter = 10*unit;//外圓直徑 float wordHeight = 12*unit;//字高//9*unit // float wordWidth = 26*unit;//字長 float offsetLength = 5*unit;//留空 // float width = 300*unit;//繪畫區(qū)域的長度 float height = 30*unit;//繪畫區(qū)域的高度 float progressWidth = 258*unit;//繪畫區(qū)域的長度 mPaint.setAntiAlias(true); mPaint.setStrokeWidth((float) lineWidth ); mPaint.setStyle(Style.STROKE); mPaint.setStrokeCap(Cap.ROUND); mPaint.setColor(Color.TRANSPARENT); float offsetHeight=height/2; float offsetWidth=offsetLength; float section = ((float)progressValue) / 100; if(section>1) section=1; int count = mColors.length; int[] colors = new int[count]; System.arraycopy(mColors, 0, colors, 0, count); //底部灰色背景,指示進(jìn)度條總長度 mPaint.setShader(null); mPaint.setColor(backgroundColor); canvas.drawLine(offsetWidth+section * progressWidth, offsetHeight, offsetWidth+progressWidth, offsetHeight, mPaint); //設(shè)置漸變色區(qū)域 LinearGradient shader = new LinearGradient(0, 0, offsetWidth*2+progressWidth , 0, colors, null, Shader.TileMode.CLAMP); mPaint.setShader(shader); //畫出漸變色進(jìn)度條 canvas.drawLine(offsetWidth, offsetHeight, offsetWidth+section*progressWidth, offsetHeight, mPaint); //漸變色外圓 mPaint.setStrokeWidth(1); mPaint.setStyle(Paint.Style.FILL); canvas.drawCircle(offsetWidth+section * progressWidth, offsetHeight, outerCircleDiameter/2, mPaint); //繪制兩條斜線,使外圓到進(jìn)度條的連接更自然 if(section*100>1.8){ mPaint.setStrokeWidth(2*unit); canvas.drawLine(offsetWidth+section * progressWidth-6*unit, offsetHeight-(float)1.5*unit, offsetWidth+section * progressWidth-1*unit,offsetHeight-(float)3.8*unit, mPaint); canvas.drawLine(offsetWidth+section * progressWidth-6*unit, offsetHeight+(float)1.5*unit, offsetWidth+section * progressWidth-1*unit,offsetHeight+(float)3.8*unit, mPaint); } //白色內(nèi)圓 mPaint.setShader(null); mPaint.setColor(Color.WHITE); canvas.drawCircle(offsetWidth+section * progressWidth, offsetHeight, innerCircleDiameter/2, mPaint);//白色內(nèi)圓 //繪制文字--百分比 mPaint.setStrokeWidth(2*unit); mPaint.setColor(textColor); mPaint.setTextSize(wordHeight); //計(jì)算坐標(biāo)使文字居中 FontMetrics fontMetrics = mPaint.getFontMetrics(); float fontHeight = fontMetrics.bottom - fontMetrics.top; float baseY = height/2 + fontHeight/2 - fontMetrics.bottom; canvas.drawText(""+progressValue+"%", progressWidth+2*offsetWidth, baseY, mPaint);//略微偏下,baseline } }
主xml:
放了兩個(gè)進(jìn)度條,一個(gè)使用默認(rèn)值,一個(gè)設(shè)置了進(jìn)度條默認(rèn)顏色與字體顏色:
<RelativeLayout xmlns:android="http://schemas.android.com/apk/res/android" xmlns:tools="http://schemas.android.com/tools" xmlns:custom="http://schemas.android.com/apk/res/com.customview" android:layout_width="match_parent" android:layout_height="match_parent" > <com.customview.view.GoodProgressView android:id="@+id/good_progress_view1" android:layout_width="match_parent" android:layout_height="wrap_content" android:padding="10dp" /> <com.customview.view.GoodProgressView android:id="@+id/good_progress_view2" android:layout_width="match_parent" android:layout_height="wrap_content" android:layout_centerInParent="true" custom:backgroundColor="#ffcccccc" custom:textColor="#ff000000" android:padding="10dp" /> </RelativeLayout>
Activity文件:
一個(gè)使用默認(rèn)漸變色效果,一個(gè)的漸變色使用隨機(jī)顏色,這樣每次運(yùn)行效果不同,比較有趣一些,另外我們也可以從隨機(jī)效果中找到比較好的顏色組合。進(jìn)度的變化,是使用了一個(gè)定時(shí)器來推進(jìn)。
package com.customview; import android.os.Bundle; import android.os.Handler; import android.os.Message; import android.util.Log; import android.view.WindowManager; import java.util.Random; import java.util.Timer; import java.util.TimerTask; import com.customview.view.GoodProgressView; import android.app.Activity; import android.graphics.Color; public class MainActivity extends Activity { GoodProgressView good_progress_view1; GoodProgressView good_progress_view2; int progressValue=0; @Override protected void onCreate(Bundle savedInstanceState) { super.onCreate(savedInstanceState); this.getWindow().setFlags(WindowManager.LayoutParams.FLAG_FULLSCREEN,WindowManager.LayoutParams.FLAG_FULLSCREEN);//去掉信息欄 setContentView(R.layout.activity_main); good_progress_view1 = (GoodProgressView)findViewById(R.id.good_progress_view1); good_progress_view2 = (GoodProgressView)findViewById(R.id.good_progress_view2); //第一個(gè)進(jìn)度條使用默認(rèn)進(jìn)度顏色,第二個(gè)指定顏色(隨機(jī)生成) good_progress_view2.setColors(randomColors()); timer.schedule(task, 1000, 1000); // 1s后執(zhí)行task,經(jīng)過1s再次執(zhí)行 } Handler handler = new Handler() { public void handleMessage(Message msg) { if (msg.what == 1) { Log.i("log","handler : progressValue="+progressValue); //通知view,進(jìn)度值有變化 good_progress_view1.setProgressValue(progressValue*2); good_progress_view1.postInvalidate(); good_progress_view2.setProgressValue(progressValue); good_progress_view2.postInvalidate(); progressValue+=1; if(progressValue>100){ timer.cancel(); } } super.handleMessage(msg); }; }; private int[] randomColors() { int[] colors=new int[2]; Random random = new Random(); int r,g,b; for(int i=0;i<2;i++){ r=random.nextInt(256); g=random.nextInt(256); b=random.nextInt(256); colors[i]=Color.argb(255, r, g, b); Log.i("customView","log: colors["+i+"]="+Integer.toHexString(colors[i])); } return colors; } Timer timer = new Timer(); TimerTask task = new TimerTask() { @Override public void run() { // 需要做的事:發(fā)送消息 Message message = new Message(); message.what = 1; handler.sendMessage(message); } }; }
最終效果如下:
豎屏?xí)r:
橫屏?xí)r:
源碼下載:Android漸變色進(jìn)度條
以上就是本文的全部內(nèi)容,希望對大家的學(xué)習(xí)有所幫助,也希望大家多多支持腳本之家。
- Android 自定義View實(shí)現(xiàn)多節(jié)點(diǎn)進(jìn)度條功能
- Android自定義View實(shí)現(xiàn)水平帶數(shù)字百分比進(jìn)度條
- Android自定義View實(shí)現(xiàn)音頻播放圓形進(jìn)度條
- Android自定義View實(shí)現(xiàn)加載進(jìn)度條效果
- Android自定義帶進(jìn)度條WebView仿微信加載過程
- Android view自定義實(shí)現(xiàn)動(dòng)態(tài)進(jìn)度條
- Android 自定義view和屬性動(dòng)畫實(shí)現(xiàn)充電進(jìn)度條效果
- android 自定義view實(shí)現(xiàn)彩虹進(jìn)度條功能
相關(guān)文章
Android自定義相機(jī)、預(yù)覽區(qū)域裁剪
這篇文章主要為大家詳細(xì)介紹了Android自定義相機(jī)、預(yù)覽區(qū)域裁剪,文中示例代碼介紹的非常詳細(xì),具有一定的參考價(jià)值,感興趣的小伙伴們可以參考一下2022-05-05詳解MVP模式在Android開發(fā)中的應(yīng)用
MVP是MVC衍生而來的,很早以前就由某軟公司提出,近年來在Android應(yīng)用開發(fā)中越來越多的被提及,越來越重要了。這篇文章主要介紹了詳解MVP模式在Android開發(fā)中的應(yīng)用,有興趣的可以了解一下。2016-11-11Android基于CountDownTimer實(shí)現(xiàn)倒計(jì)時(shí)功能
這篇文章主要介紹了Android基于CountDownTimer實(shí)現(xiàn)倒計(jì)時(shí)功能,簡單分析了基于CountDownTimer類實(shí)現(xiàn)倒計(jì)時(shí)功能的技巧,需要的朋友可以參考下2015-12-12Android入門之在子線程中調(diào)用Handler詳解
這篇文章主要為大家詳細(xì)介紹了Android如何在子線程中調(diào)用Handler,文中的示例代碼講解詳細(xì),有需要的朋友可以借鑒參考下,希望能夠?qū)Υ蠹矣兴鶐椭?/div> 2022-12-12android端實(shí)現(xiàn)驗(yàn)證碼隨機(jī)生成功能
這篇文章主要為大家詳細(xì)介紹了android端實(shí)現(xiàn)驗(yàn)證碼隨機(jī)生成功能,具有一定的參考價(jià)值,感興趣的小伙伴們可以參考一下2017-07-07Android自定義view仿微信刷新旋轉(zhuǎn)小風(fēng)車
這篇文章主要介紹了Android自定義view仿微信刷新旋轉(zhuǎn)小風(fēng)車,具有一定的參考價(jià)值,感興趣的小伙伴們可以參考一下2018-12-12Android Flutter圖片處理之高斯模糊的實(shí)現(xiàn)
這篇文章主要為大家詳細(xì)介紹了如何利用Android Flutter實(shí)現(xiàn)高斯模糊效果,文中示例代碼介紹的非常詳細(xì),具有一定的參考價(jià)值,感興趣的小伙伴們可以參考一下2022-08-08Android的八種對話框的實(shí)現(xiàn)代碼示例
本篇文章主要介紹了Android的八種對話框的實(shí)現(xiàn)代碼示例,這里整理了詳細(xì)的代碼,非常具有實(shí)用價(jià)值,有需要的小伙伴可以參考下。2017-09-09最新評(píng)論