






這樣一來肯定是須要自己定義了,所以方向有兩個:要么繼承於系統的ProgressBar。要么繼承於View類(前者就是如此實現)。那就先看一下系統的進度條吧。

對於ProgressBar的使用,有三個地方須要注意一下:
相反地。不確定的就是不清楚、不確定一個操作須要多長時間來完畢,這個時候就須要用的不確定的ProgressBar了。這個是由屬性android:indeterminate來控制的,假設設置為true的話。那么ProgressBar就可能是圓形的滾動欄或者水平的滾動欄(由樣式決定)。默認情況下,假設是水平進度條,那么就是確定的。
- Widget.ProgressBar.Horizontal
- Widget.ProgressBar.Small
- Widget.ProgressBar.Large
- Widget.ProgressBar.Inverse
- Widget.ProgressBar.Small.Inverse
- Widget.ProgressBar.Large.Inverse
- style="?
android:attr/progressBarStyle"
- style="?android:attr/progressBarStyleHorizontal"
- style="?android:attr/progressBarStyleInverse"
- style="?android:attr/progressBarStyleLarge"
- style="?android:attr/progressBarStyleLargeInverse"
- style="?android:attr/progressBarStyleSmall"
- style="?
android:attr/progressBarStyleSmallInverse"
- style="?
android:attr/progressBarStyleSmallTitle"

當中第一個android:animationResolution已經唄舍棄了。所以不要去研究它了。重點說一下android:progressDrawable以及android:indeterminateDrawable。那這個Drawable在ProgressBar中是怎樣使用的呢,假設我們是這樣在xml中設置ProgressBar的話,
<ProgressBar android:id="@+id/progressbar" style="@android:style/Widget.ProgressBar.Horizontal" android:layout_width="match_parent" android:layout_height="wrap_content" android:secondaryProgress="50" />盡管沒有設置android:indeterminateDrawable,可是樣式Widget.ProgressBar.Horizontal已經幫我們設置好了。
查看源代碼例如以下:
<style name="Widget.ProgressBar.Horizontal"> <item name="android:indeterminateOnly">false</item> <item name="android:progressDrawable">@android:drawable/progress_horizontal</item> <item name="android:indeterminateDrawable">@android:drawable/progress_indeterminate_horizontal</item> <item name="android:minHeight">20dip</item> <item name="android:maxHeight">20dip</item> <item name="android:mirrorForRtl">true</item> </style>
<?xml version="1.0" encoding="utf-8"?> <!-- Copyright (C) 2008 The Android Open Source Project Licensed under the Apache License, Version 2.0 (the "License"); you may not use this file except in compliance with the License. You may obtain a copy of the License at http://www.apache.org/licenses/LICENSE-2.0 Unless required by applicable law or agreed to in writing, software distributed under the License is distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the License for the specific language governing permissions and limitations under the License. --> <layer-list xmlns:android="http://schemas.android.com/apk/res/android"> <item android:id="@android:id/background"> <shape> <corners android:radius="5dip" /> <gradient android:startColor="#ff9d9e9d" android:centerColor="#ff5a5d5a" android:centerY="0.75" android:endColor="#ff747674" android:angle="270" /> </shape> </item> <item android:id="@android:id/secondaryProgress"> <clip> <shape> <corners android:radius="5dip" /> <gradient android:startColor="#80ffd300" android:centerColor="#80ffb600" android:centerY="0.75" android:endColor="#a0ffcb00" android:angle="270" /> </shape> </clip> </item> <item android:id="@android:id/progress"> <clip> <shape> <corners android:radius="5dip" /> <gradient android:startColor="#ffffd300" android:centerColor="#ffffb600" android:centerY="0.75" android:endColor="#ffffcb00" android:angle="270" /> </shape> </clip> </item> </layer-list>
二是和繪制相關的部分,如圖所看到的:

所以、所以我們本次最重要的部分來了。那就是怎樣自己定義一個美麗ProgressBar。
在自己定義之前。先看一下系統是怎樣實現的。
Android下ProgressBar的代碼量不算多,除去凝視預計也就是幾百行左右。首先從構造方法看是看,
/** * Create a new progress bar with range 0...100 and initial progress of 0. * @param context the application environment */ public ProgressBar(Context context) { this(context, null); } public ProgressBar(Context context, AttributeSet attrs) { this(context, attrs, com.android.internal.R.attr.progressBarStyle); } public ProgressBar(Context context, AttributeSet attrs, int defStyle) { this(context, attrs, defStyle, 0); } /** * @hide */ public ProgressBar(Context context, AttributeSet attrs, int defStyle, int styleRes) { super(context, attrs, defStyle); mUiThreadId = Thread.currentThread().getId(); initProgressBar(); TypedArray a = context.obtainStyledAttributes(attrs, R.styleable.ProgressBar, defStyle, styleRes); mNoInvalidate = true; Drawable drawable = a.getDrawable(R.styleable.ProgressBar_progressDrawable); if (drawable != null) { drawable = tileify(drawable, false); // Calling this method can set mMaxHeight, make sure the corresponding // XML attribute for mMaxHeight is read after calling this method setProgressDrawable(drawable); } mDuration = a.getInt(R.styleable.ProgressBar_indeterminateDuration, mDuration); mMinWidth = a.getDimensionPixelSize(R.styleable.ProgressBar_minWidth, mMinWidth); mMaxWidth = a.getDimensionPixelSize(R.styleable.ProgressBar_maxWidth, mMaxWidth); mMinHeight = a.getDimensionPixelSize(R.styleable.ProgressBar_minHeight, mMinHeight); mMaxHeight = a.getDimensionPixelSize(R.styleable.ProgressBar_maxHeight, mMaxHeight); mBehavior = a.getInt(R.styleable.ProgressBar_indeterminateBehavior, mBehavior); final int resID = a.getResourceId( com.android.internal.R.styleable.ProgressBar_interpolator, android.R.anim. linear_interpolator); // default to linear interpolator if (resID > 0) { setInterpolator(context, resID); } setMax(a.getInt(R.styleable.ProgressBar_max, mMax)); setProgress(a.getInt(R.styleable.ProgressBar_progress, mProgress)); setSecondaryProgress( a.getInt(R.styleable.ProgressBar_secondaryProgress, mSecondaryProgress)); drawable = a.getDrawable(R.styleable.ProgressBar_indeterminateDrawable); if (drawable != null) { drawable = tileifyIndeterminate(drawable); setIndeterminateDrawable(drawable); } mOnlyIndeterminate = a.getBoolean( R.styleable.ProgressBar_indeterminateOnly, mOnlyIndeterminate); mNoInvalidate = false; setIndeterminate( mOnlyIndeterminate || a.getBoolean( R.styleable.ProgressBar_indeterminate, mIndeterminate)); mMirrorForRtl = a.getBoolean(R.styleable.ProgressBar_mirrorForRtl, mMirrorForRtl); a.recycle(); }樣式文件例如以下:
R.styleable.Progre: <declare-styleable name="ProgressBar"> <!-- Defines the maximum value the progress can take. --> <attr name="max" format="integer" /> <!-- Defines the default progress value, between 0 and max. --> <attr name="progress" format="integer" /> <!-- Defines the secondary progress value, between 0 and max. This progress is drawn between the primary progress and the background. It can be ideal for media scenarios such as showing the buffering progress while the default progress shows the play progress. --> <attr name="secondaryProgress" format="integer" /> <!-- Allows to enable the indeterminate mode. In this mode the progress bar plays an infinite looping animation. --> <attr name="indeterminate" format="boolean" /> <!-- Restricts to ONLY indeterminate mode (state-keeping progress mode will not work). --> <attr name="indeterminateOnly" format="boolean" /> <!-- Drawable used for the indeterminate mode. --> <attr name="indeterminateDrawable" format="reference" /> <!-- Drawable used for the progress mode. --> <attr name="progressDrawable" format="reference" /> <!-- Duration of the indeterminate animation. --> <attr name="indeterminateDuration" format="integer" min="1" /> <!-- Defines how the indeterminate mode should behave when the progress reaches max. --> <attr name="indeterminateBehavior"> <!-- Progress starts over from 0. --> <enum name="repeat" value="1" /> <!-- Progress keeps the current value and goes back to 0. --> <enum name="cycle" value="2" /> </attr> <attr name="minWidth" format="dimension" /> <attr name="maxWidth" /> <attr name="minHeight" format="dimension" /> <attr name="maxHeight" /> <attr name="interpolator" format="reference" /> <!-- Timeout between frames of animation in milliseconds {@deprecated Not used by the framework.} --> <attr name="animationResolution" format="integer" /> </declare-styleable>
private void initProgressBar() { mMax = 100; mProgress = 0; mSecondaryProgress = 0; mIndeterminate = false; mOnlyIndeterminate = false; mDuration = 4000; mBehavior = AlphaAnimation.RESTART; mMinWidth = 24; mMaxWidth = 48; mMinHeight = 24; mMaxHeight = 48; }
一是mUiThreadId,他是干嘛的呢,它獲取的是當前UI線程的id,然后在更新ProgressBar進度的時候進行一個推斷。假設是UI線程。那么直接進行更新,假設不是就post出去。使用Handler等進行更新。二是tileify(drawable, false)方法和tileifyIndeterminate(drawable)方法。這兩個方法主要是對Drawable進行一個解析、轉換的過程。在這里須要重點強調一下,在ProgressBar中,最重要的部分就是Drawable的使用了,由於不僅是它的背景包括進度等都是使用Drawable來完畢的,所以在源代碼中也能夠看到基本上百分之七八十的代碼都是和Drawable有關的。由於這一部分篇幅較多。所以就不具體介紹了,以下重點說一下怎樣繪制ProgressBar。首先看onMeasure()方法,
@Override protected synchronized void onMeasure( int widthMeasureSpec, int heightMeasureSpec) { Drawable d = mCurrentDrawable; int dw = 0; int dh = 0; if (d != null) { dw = Math. max(mMinWidth , Math.min( mMaxWidth, d.getIntrinsicWidth())); dh = Math. max(mMinHeight , Math.min( mMaxHeight, d.getIntrinsicHeight())); } updateDrawableState(); dw += mPaddingLeft + mPaddingRight; dh += mPaddingTop + mPaddingBottom; setMeasuredDimension( resolveSizeAndState(dw, widthMeasureSpec, 0), resolveSizeAndState(dh, heightMeasureSpec, 0)); }這是測量View大小的方法,也就是ProgressBar的大小,由於每個ProgressBar默認都會使用Drawable。所以ProgressBar的大小即是Drawable的大小加上Padding的大小,假設沒有Padding。那非常顯然就是Drawable的大小。最后使用setMeasuredDimension()方法設置ProgressBar的大小。
@Override protected synchronized void onDraw(Canvas canvas) { super.onDraw(canvas); Drawable d = mCurrentDrawable; if (d != null) { // Translate canvas so a indeterminate circular progress bar with padding // rotates properly in its animation canvas.save(); if(isLayoutRtl() && mMirrorForRtl) { canvas.translate(getWidth() - mPaddingRight, mPaddingTop); canvas.scale(-1.0f, 1.0f); } else { canvas.translate(mPaddingLeft, mPaddingTop); } long time = getDrawingTime(); if ( mHasAnimation) { mAnimation.getTransformation(time, mTransformation); float scale = mTransformation.getAlpha(); try { mInDrawing = true; d.setLevel(( int) (scale * MAX_LEVEL)); } finally { mInDrawing = false; } postInvalidateOnAnimation(); } d.draw(canvas); canvas.restore(); if ( mShouldStartAnimationDrawable && d instanceof Animatable) { ((Animatable) d).start(); mShouldStartAnimationDrawable = false ; } }
public boolean isLayoutRtl() { return (getLayoutDirection() == LAYOUT_DIRECTION_RTL); }
package android.util; /** * A class for defining layout directions. A layout direction can be left-to-right (LTR) * or right-to-left (RTL). It can also be inherited (from a parent) or deduced from the default * language script of a locale. */ public final class LayoutDirection { // No instantiation private LayoutDirection() {} /** * Horizontal layout direction is from Left to Right. */ public static final int LTR = 0; /** * Horizontal layout direction is from Right to Left. */ public static final int RTL = 1; /** * Horizontal layout direction is inherited. */ public static final int INHERIT = 2; /** * Horizontal layout direction is deduced from the default language script for the locale. */ public static final int LOCALE = 3; }
最后調用Drawable對象去畫出來d.draw(canvas)。



<?xml version= "1.0" encoding ="utf-8"?> <resources> <declare-styleable > <attr name= "progressIndicator" format="reference" ></attr> <attr name= "offset" format ="dimension"></ attr> <attr name= "textSize" format ="dimension"></ attr> <attr name= "textColor" format="reference|color" ></attr> <attr name= "textStyle"> <flag name= "normal" value ="0" /> <flag name= "bold" value ="1" /> <flag name= "italic" value ="2" /> </attr> <attr name= "textAlign"> <flag name= "left" value ="0" /> <flag name= "center" value ="1" /> <flag name= "right" value ="2" /> </attr> </declare-styleable > </resources>
/** * @author kince * */ public class IndicatorProgressBar extends ProgressBar { public IndicatorProgressBar(Context context) { this(context, null); } public IndicatorProgressBar(Context context, AttributeSet attrs) { this(context, attrs, 0); } public IndicatorProgressBar(Context context, AttributeSet attrs, int defStyle) { super(context, attrs, defStyle); } }
/** * */ package com.example.indicatorprogressbar.widget; import com.example.indicatorprogressbar.R; import android.content.Context; import android.content.res.TypedArray; import android.graphics.Color; import android.graphics.Paint; import android.graphics.Paint.Align; import android.graphics.drawable.Drawable; import android.text.TextPaint; import android.util.AttributeSet; import android.widget.ProgressBar; /** * @author kince * */ public class IndicatorProgressBar extends ProgressBar { private TextPaint mTextPaint; private Drawable mDrawableIndicator; private int offset=5; public IndicatorProgressBar(Context context) { this(context, null); } public IndicatorProgressBar(Context context, AttributeSet attrs) { this(context, attrs, 0); mTextPaint=new TextPaint(Paint.ANTI_ALIAS_FLAG); mTextPaint.density=getResources().getDisplayMetrics().density; mTextPaint.setColor(Color.WHITE); mTextPaint.setTextSize(10); mTextPaint.setTextAlign(Align.CENTER); mTextPaint.setFakeBoldText(true); } public IndicatorProgressBar(Context context, AttributeSet attrs, int defStyle) { super(context, attrs, defStyle); TypedArray array=context.obtainStyledAttributes(attrs, R.styleable.IndicatorProgressBar, defStyle, 0); if(array!=null){ mDrawableIndicator=array.getDrawable(R.styleable.IndicatorProgressBar_progressIndicator); offset=array.getInt(R.styleable.IndicatorProgressBar_offset, 0); array.recycle(); } } }
public Drawable getmDrawableIndicator() { return mDrawableIndicator ; } public void setmDrawableIndicator(Drawable mDrawableIndicator) { this.mDrawableIndicator = mDrawableIndicator; } public int getOffset() { return offset ; } public void setOffset(int offset) { this.offset = offset; }
因此在onMeasure()方法中就能夠這樣來寫:
@Override protected synchronized void onMeasure(int widthMeasureSpec, int heightMeasureSpec) { // TODO Auto-generated method stub super.onMeasure(widthMeasureSpec, heightMeasureSpec); if(mDrawableIndicator!=null){ //獲取系統進度條的寬度 這個寬度也是自己定義進度條的寬度 所以在這里直接賦值 final int width=getMeasuredWidth(); final int height=getMeasuredHeight()+getIndicatorHeight(); setMeasuredDimension(width, height); } } /** * @category 獲取指示器的高度 * @return */ private int getIndicatorHeight(){ if(mDrawableIndicator==null){ return 0; } Rect r=mDrawableIndicator.copyBounds(); int height=r.height(); return height; }
<style name="Widget.ProgressBar.RegularProgressBar"> <item name="android:indeterminateOnly" >false </item> <item name="android:progressDrawable" >@drawable/progressbar </item> <item name="android:indeterminateDrawable" >@android:drawable/progress_indeterminate_horizontal </item> <item name= "android:minHeight">1dip</item > <item name= "android:maxHeight">10dip</item > </style >
<layer-list xmlns:android="http://schemas.android.com/apk/res/android" > <item android:id="@android:id/background" android:drawable="@drawable/progressbar_bg" /> <item android:id="@+id/progress" android:drawable="@drawable/progressbar_bar" > </item > <item android:id="@+id/pattern"> <bitmap android:src="@drawable/progressbar_pattern" android:tileMode="repeat" /> </item > </layer-list>能夠發現。是一個layer類型的drawable,所以在計算大小的時候。須要特別考慮這個情況。 代碼例如以下:
if (m_indicator != null) { if (progressDrawable != null && progressDrawable instanceof LayerDrawable) { LayerDrawable d = (LayerDrawable) progressDrawable; for (int i = 0; i < d.getNumberOfLayers(); i++) { d.getDrawable(i).getBounds(). top = getIndicatorHeight(); d.getDrawable(i).getBounds(). bottom = d.getDrawable(i) .getBounds().height() + getIndicatorHeight(); } } else if (progressDrawable != null) { progressDrawable.getBounds(). top = m_indicator .getIntrinsicHeight(); progressDrawable.getBounds(). bottom = progressDrawable .getBounds().height() + getIndicatorHeight(); } }
private void updateProgressBar () { Drawable progressDrawable = getProgressDrawable(); if (progressDrawable != null && progressDrawable instanceof LayerDrawable) { LayerDrawable d = (LayerDrawable) progressDrawable; final float scale = getScale(getProgress()); // 獲取進度條 更新它的大小 Drawable progressBar = d.findDrawableByLayerId(R.id.progress ); final int width = d.getBounds(). right - d.getBounds().left ; if (progressBar != null) { Rect progressBarBounds = progressBar.getBounds(); progressBarBounds. right = progressBarBounds.left + ( int ) (width * scale + 0.5f); progressBar.setBounds(progressBarBounds); } // 獲取疊加的圖層 Drawable patternOverlay = d.findDrawableByLayerId(R.id.pattern ); if (patternOverlay != null) { if (progressBar != null) { // 使疊加圖層適應進度條大小 Rect patternOverlayBounds = progressBar.copyBounds(); final int left = patternOverlayBounds.left ; final int right = patternOverlayBounds.right ; patternOverlayBounds. left = (left + 1 > right) ?left : left + 1; patternOverlayBounds. right = (right > 0) ? right - 1 : right; patternOverlay.setBounds(patternOverlayBounds); } else { // 沒有疊加圖層 Rect patternOverlayBounds = patternOverlay.getBounds(); patternOverlayBounds. right = patternOverlayBounds.left + ( int ) (width * scale + 0.5f); patternOverlay.setBounds(patternOverlayBounds); } } } }
if (m_indicator != null) { canvas.save(); int dx = 0; // 獲取系統進度條最右邊的位置 也就是頭部的位置 if (progressDrawable != null && progressDrawable instanceof LayerDrawable) { LayerDrawable d = (LayerDrawable) progressDrawable; Drawable progressBar = d.findDrawableByLayerId(R.id.progress ); dx = progressBar.getBounds(). right; } else if (progressDrawable != null) { dx = progressDrawable.getBounds().right ; } //增加offset dx = dx - getIndicatorWidth() / 2 - m_offset + getPaddingLeft(); // 移動畫筆位置 canvas.translate(dx, 0); // 畫出指示器 m_indicator .draw(canvas); // 畫出進度數字 canvas.drawText( m_formatter != null ? m_formatter .getText(getProgress()) : Math.round(getScale(getProgress()) * 100.0f) + "%" , getIndicatorWidth() / 2, getIndicatorHeight() / 2 + 1, m_textPaint ); // restore canvas to original canvas.restore(); }源代碼下載:
Github地址: https://github.com/wangjinyu501/SaundProgressBar