Android那些事兒之自定義進度條

 Android原生控件只有橫向進度條一種,並且無法變換樣式,好比原生rom的樣子android

很醜是吧,當偉大的產品設計要求更換前背景,甚至縱向,甚至圓弧狀的,咋辦,好比canvas

ok,咱們開始吧:ide

 

一)變換前背景佈局

先來看看progressbar的屬性:spa

  
  
  
  
  1. <ProgressBar 
  2.             android:id="@+id/progressBar" 
  3.             style="?android:attr/progressBarStyleHorizontal" 
  4.             android:layout_width="match_parent" 
  5.             android:layout_height="wrap_content" 
  6.             android:layout_margin="5dip" 
  7.             android:layout_toRightOf="@+id/progressBarV" 
  8.             android:indeterminate="false" 
  9.             android:padding="2dip" 
  10.             android:progress="50" /> 

根據style="?android:attr/progressBarStyleHorizontal",咱們找到源碼中的style.xml設計

  
  
  
  
  1. <style name="Widget.ProgressBar.Horizontal"> 
  2.         <item name="android:indeterminateOnly">false</item> 
  3.         <item name="android:progressDrawable">@android:drawable/progress_horizontal</item> 
  4.         <item name="android:indeterminateDrawable">@android:drawable/progress_indeterminate_horizontal</item> 
  5.         <item name="android:minHeight">20dip</item> 
  6.         <item name="android:maxHeight">20dip</item> 
  7.     </style> 

看到orm

<item name="android:progressDrawable">@android:drawable/progress_horizontal</item> xml

木有,繼續發掘源碼,找到drawable下面的progress_horizontal.xml,這就是咱們今天的主角了:
blog


  
  
  
  
  1. <layer-list xmlns:android="http://schemas.android.com/apk/res/android"> 
  2.      
  3.     <item android:id="@android:id/background"> 
  4.         <shape> 
  5.             <corners android:radius="5dip" /> 
  6.             <gradient 
  7.                     android:startColor="#ff9d9e9d" 
  8.                     android:centerColor="#ff5a5d5a" 
  9.                     android:centerY="0.75" 
  10.                     android:endColor="#ff747674" 
  11.                     android:angle="270" 
  12.             /> 
  13.         </shape> 
  14.     </item> 
  15.      
  16.     <item android:id="@android:id/secondaryProgress"> 
  17.         <clip> 
  18.             <shape> 
  19.                 <corners android:radius="5dip" /> 
  20.                 <gradient 
  21.                         android:startColor="#80ffd300" 
  22.                         android:centerColor="#80ffb600" 
  23.                         android:centerY="0.75" 
  24.                         android:endColor="#a0ffcb00" 
  25.                         android:angle="270" 
  26.                 /> 
  27.             </shape> 
  28.         </clip> 
  29.     </item> 
  30.      
  31.     <item android:id="@android:id/progress"> 
  32.         <clip> 
  33.             <shape> 
  34.                 <corners android:radius="5dip" /> 
  35.                 <gradient 
  36.                         android:startColor="#ffffd300" 
  37.                         android:centerColor="#ffffb600" 
  38.                         android:centerY="0.75" 
  39.                         android:endColor="#ffffcb00" 
  40.                         android:angle="270" 
  41.                 /> 
  42.             </shape> 
  43.         </clip> 
  44.     </item> 
  45.      
  46. </layer-list> 

 看到android:id="@android:id/progress"木有,看到android:id="@android:id/secondaryProgress"木有圖片

把這個文件複製到本身工程下的drawable,就能夠爲所欲爲的修改shape的屬性,漸變,圓角等等

那麼怎麼放一個圖片進去呢,ok,新建progress_horizontal1.xml:

  
  
  
  
  1. <?xml version="1.0" encoding="utf-8"?> 
  2. <layer-list xmlns:android="http://schemas.android.com/apk/res/android"> 
  3.      
  4.     <item android:id="@android:id/progress" android:drawable="@drawable/progressbar" /> 
  5.      
  6. </layer-list> 

在android:drawable中指定你處理好的圖片

而後回到佈局中

  
  
  
  
  1. <ProgressBar 
  2.             android:id="@+id/progressBar1" 
  3.             android:layout_width="match_parent" 
  4.             android:layout_height="wrap_content" 
  5.             android:layout_below="@+id/progressBar" 
  6.             android:layout_margin="5dip" 
  7.             android:layout_toRightOf="@+id/progressBarV" 
  8.             android:background="@drawable/progress_bg" 
  9.             android:indeterminate="false" 
  10.             android:indeterminateOnly="false" 
  11.             android:maxHeight="20dip" 
  12.             android:minHeight="20dip" 
  13.             android:padding="2dip" 
  14.             android:progress="50" 
  15.             android:progressDrawable="@drawable/progress_horizontal1" /> 

android:background="@drawable/progress_bg"指定背景

android:progressDrawable="@drawable/progress_horizontal1"前景使用上面的progress_horizontal1

ok,搞定

 注意看,四角仍是有圓倒角的,貌似是系統本身加上去的,總之個人圖片裏面是沒有作這個倒角處理的

 

二)縱向進度條

仍是得從源碼入手,看回progress_horizontal.xml

  
  
  
  
  1. <item android:id="@android:id/progress"> 
  2.         <clip> 
  3.             <shape> 
  4.                 <corners android:radius="5dip" /> 
  5.                 <gradient 
  6.                         android:startColor="#ffffd300" 
  7.                         android:centerColor="#ffffb600" 
  8.                         android:centerY="0.75" 
  9.                         android:endColor="#ffffcb00" 
  10.                         android:angle="270" 
  11.                 /> 
  12.             </shape> 
  13.         </clip> 
  14.     </item> 

爲何shape外面要包一層clip呢,官方文檔解釋是clipdrawable是能夠自我複製的,來看看定義

  
  
  
  
  1. <?xml version="1.0" encoding="utf-8"?> 
  2. <clip 
  3.     xmlns:android="http://schemas.android.com/apk/res/android" 
  4.     android:drawable="@drawable/drawable_resource" 
  5.     android:clipOrientation=["horizontal" | "vertical"] 
  6.     android:gravity=["top" | "bottom" | "left" | "right" | "center_vertical" | 
  7.                      "fill_vertical" | "center_horizontal" | "fill_horizontal" | 
  8.                      "center" | "fill" | "clip_vertical" | "clip_horizontal"] /> 

android:clipOrientation有兩個屬性,默認爲horizontal

android:gravity有兩個屬性,默認爲left

那咱們試試改爲vertical和bottom會有什麼效果,新建一個progress_vertical.xml,把源碼progress_horizontal.xml的內容複製過來,這裏去掉了secondaryProgress,修改了clip,shape的漸變中心centerY改成centerX

  
  
  
  
  1. <item android:id="@android:id/progress"> 
  2.         <clip 
  3.             android:clipOrientation="vertical" 
  4.             android:gravity = "bottom"> 
  5.             <shape> 
  6.                 <corners android:radius="5dip" /> 
  7.                 <gradient 
  8.                         android:startColor="#ffffd300" 
  9.                         android:centerColor="#ffffb600" 
  10.                         android:centerX="0.75" 
  11.                         android:endColor="#ffffcb00" 
  12.                         android:angle="90" 
  13.                 /> 
  14.             </shape> 
  15.         </clip> 
  16.     </item> 

佈局中android:progressDrawable="@drawable/progress_vertical"

ok,搞定,就是這麼簡單:

 

 

三)弧形bar

這個也許算不上是進度條,用的也很少,最多也就儀表盤用用,否則誰會把進度條整成圓弧的呢。好吧這個可不是改改源碼就能搞定的,看代碼

  
  
  
  
  1. public class Arcs extends View {   
  2.     private Paint mArcPaint;   
  3.     private Paint mArcBGPaint;   
  4.    
  5.     private RectF mOval;   
  6.     private float mSweep = 0;   
  7.     private int mSpeedMax = 200
  8.     private int mThreshold = 100
  9.     private int mIncSpeedValue = 0
  10.     private int mCurrentSpeedValue = 0;  
  11.     private float mCenterX;   
  12.     private float mCenterY;   
  13.     private float mSpeedArcWidth;   
  14.   
  15.     private final float SPEED_VALUE_INC = 2;  
  16.   
  17.     ..........  
  18.   
  19. }  

首先是一堆成員變量,兩個Paint用來畫圓弧一個前景一個背景,一個RectF圓弧就畫在上面,而後是一些控制參數好比sweep圓弧掃過的角度,xy座標等等

 

  
  
  
  
  1. mArcPaint = new Paint(Paint.ANTI_ALIAS_FLAG); 
  2.         mArcPaint.setStyle(Paint.Style.STROKE); 
  3.         mArcPaint.setStrokeWidth(mSpeedArcWidth); 
  4. //        mPaint.setStrokeCap(Paint.Cap.ROUND); 
  5.         mArcPaint.setColor(0xff81ccd6); 
  6.         BlurMaskFilter mBlur = new BlurMaskFilter(8, BlurMaskFilter.Blur.INNER); 
  7.         mArcPaint.setMaskFilter(mBlur); 
  8.          
  9.         mArcBGPaint = new Paint(Paint.ANTI_ALIAS_FLAG); 
  10.         mArcBGPaint.setStyle(Paint.Style.STROKE); 
  11.         mArcBGPaint.setStrokeWidth(mSpeedArcWidth+8); 
  12.         mArcBGPaint.setColor(0xff171717); 
  13.          
  14.         BlurMaskFilter mBGBlur = new BlurMaskFilter(8, BlurMaskFilter.Blur.INNER); 
  15.       mArcBGPaint.setMaskFilter(mBGBlur); 

設置兩個畫筆,顏色,寬度,樣式等等,BlurMaskFilter筆是邊緣模糊效果,有幾種,能夠本身嘗試

 

  
  
  
  
  1. @Override 
  2. protected void onSizeChanged(int w, int h, int ow, int oh) { 
  3.         super.onSizeChanged(w, h, ow, oh); 
  4.         Log.i("onSizeChanged w", w+""); 
  5.         Log.i("onSizeChanged h", h+""); 
  6.         mCenterX = w * 0.5f;  // remember the center of the screen 
  7.         mCenterY = h - mSpeedArcWidth; 
  8.         mOval = new RectF(mCenterX - mCenterY, mSpeedArcWidth, mCenterX + mCenterY, mCenterY * 2); 
  9.     } 

重寫父類View的onSizeChanged,爲的是本身根據佈局中的大小作居中處理

 

  
  
  
  
  1. @Override   
  2.     protected void onDraw(Canvas canvas) {  
  3.         drawSpeed(canvas);  
  4.         calcSpeed();  
  5.     }  
  6.   
  7. private void drawSpeed(Canvas canvas) {  
  8.         canvas.drawArc(mOval, 179181false, mArcBGPaint);  
  9.   
  10.         mSweep = (float) mIncSpeedValue / mSpeedMax * 180;  
  11.         if (mIncSpeedValue > mThreshold) {  
  12.             mArcPaint.setColor(0xFFFF0000);  
  13.         }  
  14.         else {  
  15.             mArcPaint.setColor(0xFF00B0F0);  
  16.         }  
  17.           
  18.         canvas.drawArc(mOval, 180, mSweep, false, mArcPaint);          
  19.     }  
  20.   
  21. private void calcSpeed() {  
  22.         if (mIncSpeedValue < mCurrentSpeedValue) {  
  23.             mIncSpeedValue += SPEED_VALUE_INC;  
  24.             if (mIncSpeedValue > mCurrentSpeedValue) {  
  25.                 mIncSpeedValue = mCurrentSpeedValue;  
  26.             }  
  27.             invalidate();  
  28.         }  
  29.         else if (mIncSpeedValue > mCurrentSpeedValue) {  
  30.             mIncSpeedValue -= SPEED_VALUE_INC;  
  31.             if (mIncSpeedValue < mCurrentSpeedValue) {  
  32.                 mIncSpeedValue = mCurrentSpeedValue;  
  33.             }  
  34.             invalidate();  
  35.         }  
  36.     }  

重寫onDraw以便重繪canvas

drawSpeed裏面

經過計算mSweep = (float) mIncSpeedValue / mSpeedMax * 180;

而後canvas.drawArc(mOval, 180, mSweep, false, mArcPaint);

會根據mSweep的變化,畫出相應長度的弧度來

根據與閾值的對比,還能夠設定不一樣的 顏色:

 

if (mIncSpeedValue > mThreshold) {

mArcPaint.setColor(0xFFFF0000);

}

else {

mArcPaint.setColor(0xFF00B0F0);

}

 

 

calcSpeed經過一個步進來控制增量或減量,以使弧度天然過渡,減小跳躍

ok,大功告成