溫馨提示×

溫馨提示×

您好,登錄后才能下訂單哦!

密碼登錄×
登錄注冊×
其他方式登錄
點擊 登錄注冊 即表示同意《億速云用戶服務(wù)條款》

怎么在Android中通過自定義View實現(xiàn)漸變色進(jìn)度條

發(fā)布時間:2021-05-14 17:28:54 來源:億速云 閱讀:227 作者:Leah 欄目:移動開發(fā)

今天就跟大家聊聊有關(guān)怎么在Android中通過自定義View實現(xiàn)漸變色進(jìn)度條,可能很多人都不太了解,為了讓大家更加了解,小編給大家總結(jié)了以下內(nèi)容,希望大家根據(jù)這篇文章可以有所收獲。

效果分解:

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

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

然后就進(jìn)行了精細(xì)的測量:

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

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

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

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

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

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

//計算坐標(biā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ù),做了如下幾個自定義屬性:
前景色:開始顏色,結(jié)束顏色;
進(jìn)度條未走到時的默認(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個點)
 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ì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);
 //計算坐標(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:

放了兩個進(jìn)度條,一個使用默認(rèn)值,一個設(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文件:

一個使用默認(rèn)漸變色效果,一個的漸變色使用隨機(jī)顏色,這樣每次運行效果不同,比較有趣一些,另外我們也可以從隨機(jī)效果中找到比較好的顏色組合。進(jìn)度的變化,是使用了一個定時器來推進(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);

 //第一個進(jìn)度條使用默認(rèn)進(jìn)度顏色,第二個指定顏色(隨機(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:

怎么在Android中通過自定義View實現(xiàn)漸變色進(jìn)度條

橫屏?xí)r:

怎么在Android中通過自定義View實現(xiàn)漸變色進(jìn)度條

Android是什么

Android是一種基于Linux內(nèi)核的自由及開放源代碼的操作系統(tǒng),主要使用于移動設(shè)備,如智能手機(jī)和平板電腦,由美國Google公司和開放手機(jī)聯(lián)盟領(lǐng)導(dǎo)及開發(fā)。

看完上述內(nèi)容,你們對怎么在Android中通過自定義View實現(xiàn)漸變色進(jìn)度條有進(jìn)一步的了解嗎?如果還想了解更多知識或者相關(guān)內(nèi)容,請關(guān)注億速云行業(yè)資訊頻道,感謝大家的支持。

向AI問一下細(xì)節(jié)

免責(zé)聲明:本站發(fā)布的內(nèi)容(圖片、視頻和文字)以原創(chuàng)、轉(zhuǎn)載和分享為主,文章觀點不代表本網(wǎng)站立場,如果涉及侵權(quán)請聯(lián)系站長郵箱:is@yisu.com進(jìn)行舉報,并提供相關(guān)證據(jù),一經(jīng)查實,將立刻刪除涉嫌侵權(quán)內(nèi)容。

AI