Android自定義View實現(xiàn)漸變色進度條

在網(wǎng)上看到一個進度條效果圖,非常美觀,如下:

image.png

進行效果分解:

1.漸變色,看起來顏色變化并不復雜,使用LinearGradient應該可以實現(xiàn)。 2.圓頭,無非是畫兩個圓,外圓使用漸變色的顏色,內(nèi)圓固定為白色。 3.灰底,還沒有走到的進度部分為灰色。 4.進度值,使用文本來顯示; 5.弧形的頭部,考慮使用直線進行連接,或者使用曲線,例如貝塞爾曲線;

我首先初步實現(xiàn)了進度條的模樣,發(fā)現(xiàn)樣子有了,卻不太美觀。 反思了一下,我只是個寫代碼的,對于哪種比例比較美觀,是沒有清晰的認識的,所以,還是參考原圖吧。

然后就進行了精細的測量:

將圖像放大4倍,進行測量,然后獲取到各部分的比例關系,具體過程就不細說了,說一下測量結果(按比例的):

視圖總長300,其中前面留空5,進度長258,然后再留空5,顯示文本占26,后面留空6;

高度分為4個: 外圓:10 字高:9 內(nèi)圓:6 線粗:5 考慮上下各留空10,則視圖的高度為30。

考慮到視圖整體的效果,可以由用戶來設置長度值與高度值,按比例取最小值來進行繪圖。 首先計算出一個單位的實際像素數(shù),各部分按比例來顯示即可。

還有一個弧形的頭部,是怎么實現(xiàn)的呢? 在放大之后,能看出來圖形比較簡單,看不出有弧度,那么,使用一小段直線連接就可以了。 估算這小段直線:線粗為2,呈30度角,長為8-10即可,連接直線與弧頂,起點在弧頂之左下方。 注意:在進度的起點時,不能畫出。避免出現(xiàn)一個很突兀的小尾巴。在2%進度之后,才開始畫。

在文字的繪制過程中,遇到一個小問題,就是文字不居中,略微偏下,上網(wǎng)查了下,原因是這樣的:我們繪制文本時,使用的這個函數(shù):canvas.drawText(“30%”, x, y, paint); 其中的參數(shù) y 是指字符串baseline的的位置,不是文本的中心。通過計算可以調(diào)整為居中,如下:

//計算坐標使文字居中
FontMetrics fontMetrics = mPaint.getFontMetrics(); 
float fontHeight = fontMetrics.bottom - fontMetrics.top;
float baseY = height/2 + fontHeight/2 - fontMetrics.bottom;

按比例來繪制之后,就確實是原來那個修長優(yōu)雅的感覺了。 實際運行后,發(fā)現(xiàn)字體偏小,不太適合豎屏觀看,調(diào)大了些。

另外對于參數(shù),做了如下幾個自定義屬性: 前景色:開始顏色,結束顏色; 進度條未走到時的默認顏色, 字體顏色。

屬性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};//進度條顏色(漸變色的2個點)
private int backgroundColor = Color.GRAY;//進度條默認顏色
private int textColor = Color.GRAY;//文本顏色
private Paint mPaint;//畫筆
private int progressValue=0;//進度值
// 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:
// 漸變色之起始顏色,默認設置為紅色
mColors[0] = a.getColor(attr, Color.RED);
break; 
case R.styleable.GoodProgressView_endColor:
// 漸變色之結束顏色,默認設置為品紅
mColors[1] = a.getColor(attr, Color.MAGENTA);
break; 
case R.styleable.GoodProgressView_backgroundColor:
// 進度條默認顏色,默認設置為灰色
backgroundColor = a.getColor(attr, Color.GRAY);
break; 
case R.styleable.GoodProgressView_textColor:
// 文字顏色,默認設置為灰色
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;
/**
* 設置寬度
*/
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;
}
/**
* 設置高度
*/
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();
//按比例計算進度條各部分的值
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); 
//底部灰色背景,指示進度條總長度
mPaint.setShader(null);
mPaint.setColor(backgroundColor); 
canvas.drawLine(offsetWidth+section * progressWidth, offsetHeight, offsetWidth+progressWidth, offsetHeight, mPaint);
//設置漸變色區(qū)域
LinearGradient shader = new LinearGradient(0, 0, offsetWidth*2+progressWidth , 0, colors, null,
Shader.TileMode.CLAMP);
mPaint.setShader(shader);
//畫出漸變色進度條
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);
//繪制兩條斜線,使外圓到進度條的連接更自然
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);
//計算坐標使文字居中
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:

放了兩個進度條,一個使用默認值,一個設置了進度條默認顏色與字體顏色:

<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文件:

一個使用默認漸變色效果,一個的漸變色使用隨機顏色,這樣每次運行效果不同,比較有趣一些,另外我們也可以從隨機效果中找到比較好的顏色組合。進度的變化,是使用了一個定時器來推進。

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);
//第一個進度條使用默認進度顏色,第二個指定顏色(隨機生成)
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,進度值有變化
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); 
} 
}; 
}

最終效果如下:

豎屏時:

image.png

橫屏時:

image.png

以上就是本文的全部內(nèi)容,希望對大家的學習有所幫助。

?著作權歸作者所有,轉載或內(nèi)容合作請聯(lián)系作者
【社區(qū)內(nèi)容提示】社區(qū)部分內(nèi)容疑似由AI輔助生成,瀏覽時請結合常識與多方信息審慎甄別。
平臺聲明:文章內(nèi)容(如有圖片或視頻亦包括在內(nèi))由作者上傳并發(fā)布,文章內(nèi)容僅代表作者本人觀點,簡書系信息發(fā)布平臺,僅提供信息存儲服務。

相關閱讀更多精彩內(nèi)容

友情鏈接更多精彩內(nèi)容