-
版本:1.0日期:2014.5.16版權:© 2014 kince 轉載注明出處
這一次主要說一下Android下的進度條,為什么是它呢,因為近期被其各種美輪美奐的設計所傾倒,計划逐漸去實現。另外一個因素也是它也是為數不多的直接繼承於View類的控件,從中 是不是很漂亮,其實就像上面圖形展示的那樣,進度條大體上無非就是這幾種形式。這樣一來肯定是需要自定義了,所以方向有兩個:要么繼承於系統的ProgressBar;要么繼承於View類(前者就是如此實現)。那就先看一下系統的進度條吧。
繼承於View類,直接子類有AbsSeekBar和ContentLoadingProgressBar,其中AbsSeekBar的子類有SeekBar和RatingBar,可見這二者也是基於ProgressBar實現的。對於ProgressBar的使用,有三個地方需要注意一下: 1、ProgressBar有兩個進度,一個是android:progress,另一個是android:secondaryProgress。后者主要是為緩存需要所涉及的,比如在看網絡視頻時候都會有一個緩存的進度條以及還要一個播放的進度,在這里緩存的進度就可以是android:secondaryProgress,而播放進度就是android:progress。 2、ProgressBar分為確定的和不確定的,上面說的播放進度、緩存等就是確定的。相反地,不確定的就是不清楚、不確定一個操作需要多長時間來完成,這個時候就需要用的不確定的ProgressBar了。這個是由屬性android:indeterminate來控制的,如果設置為true的話,那么ProgressBar就可能是圓形的滾動條或者水平的滾動條(由樣式決定)。默認情況下,如果是水平進度條,那么就是確定的。 3、ProgressBar的樣式設定其實有兩種方式,在API文檔中說明的方式如下:-
Widget.ProgressBar.HorizontalWidget.ProgressBar.SmallWidget.ProgressBar.LargeWidget.ProgressBar.InverseWidget.ProgressBar.Small.InverseWidget.ProgressBar.Large.Inverse 使用的時候可以這樣:style="@android:style/Widget.ProgressBar.Small"。另外還有一種方式就是使用系統的attr,上面的方式是系統的style:
-
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" 然后再看一下ProgressBar的其他常用屬性,
關於這些屬性的使用還是比較簡單,不多做介紹。其中第一個android:animationResolution已經唄舍棄了,所以不要去研究它了。重點說一下android:progressDrawable以及android:indeterminateDrawable。那這個Drawable在ProgressBar中是如何使用的呢,如果我們是這樣在xml中設置ProgressBar的話,1
<progressbar android:id=
"@+id/progressbar"
style=
"@android:style/Widget.ProgressBar.Horizontal"
android:layout_width=
"match_parent"
android:layout_height=
"wrap_content"
android:secondaryprogress=
"50"
></progressbar>
雖然沒有設置android:indeterminateDrawable,但是樣式Widget.ProgressBar.Horizontal已經幫我們設置好了。查看源碼如下:
1
2
3
4
5
6
7
8
<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>
先看一下progress_horizontal,源碼如下:
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
<!--?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"
>
</gradient></corners></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"
>
</gradient></corners></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"
>
</gradient></corners></shape>
</clip>
</item>
</layer-list>
可以看到,系統使用的是圖層方式,以覆蓋的方式進行的。所以如果需要其他的樣式的話,改變系統默認的值即可,或者參考一下系統自帶的樣式設置就行了。
緊接着,說一下ProgressBar的方法,總體來說,可以分為兩個部分。一是和自身屬性相關的,比如獲取進度、設置進度的最大值、設置插入器等等。二是和繪制相關的部分,如圖所示:
所以、所以我們本次最重要的部分來了,那就是如何自定義一個漂亮ProgressBar。在自定義之前,先看一下系統是如何實現的。Android下ProgressBar的代碼量不算多,除去注釋估計也就是幾百行左右。首先從構造方法看是看,1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
/**
* 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();
}
樣式文件如下:
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
R.styleable.Progre:
<declare-styleable name=
"ProgressBar"
>
<!-- Defines the maximum value the progress can take. -->
<!-- Defines the
default
progress value, between
0
and max. -->
<!-- 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. -->
<!-- Allows to enable the indeterminate mode. In
this
mode the progress
bar plays an infinite looping animation. -->
<!-- Restricts to ONLY indeterminate mode (state-keeping progress mode will not work). -->
<!-- Drawable used
for
the indeterminate mode. -->
<!-- Drawable used
for
the progress mode. -->
<!-- Duration of the indeterminate animation. -->
<!-- Defines how the indeterminate mode should behave when the progress
reaches max. -->
<!-- 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"
>
</
enum
></
enum
></attr>
<!-- Timeout between frames of animation in milliseconds
{
@deprecated
Not used by the framework.} -->
</attr></attr></attr></attr></attr></attr></attr></attr></attr></attr></attr></attr></attr></attr></declare-styleable>
ProgressBar把三個構造方法都列出來了,並使用了遞歸調用的方式,還有一個方式就是分別在每一個構造方法中都調用初始化的代碼,個人覺得還是此處比較正規。然后看一下第三個構造方法,在這里主要做了兩件事情,一個是從attrs文件中讀取設置的屬性;一個是initProgressBar()方法,為ProgressBar設置一些默認的屬性值。
1
2
3
4
5
6
7
8
9
10
11
12
13
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
;
}
這就是默認的屬性值。這在自定義View中算是最基礎的了,不多說,不過在這里需要注意兩個地方。一是mUiThreadId,他是干嘛的呢,它獲取的是當前UI線程的id,然后在更新ProgressBar進度的時候進行一個判斷,如果是UI線程,那么直接進行更新,如果不是就post出去,使用Handler等進行更新。二是tileify(drawable, false)方法和tileifyIndeterminate(drawable)方法。這兩個方法主要是對Drawable進行一個解析、轉換的過程。在這里需要重點強調一下,在ProgressBar中,最重要的部分就是Drawable的使用了,因為不僅是它的背景包括進度等都是使用Drawable來完成的,所以在源碼中也可以看到基本上百分之七八十的代碼都是和Drawable有關的。因為這一部分篇幅較多,所以就不詳細介紹了,下面重點說一下如何繪制ProgressBar,首先看onMeasure()方法,
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
@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的大小。 按照正常的流程,有些朋友可能會想到重寫onLayout()方法了,但是這里ProgressBar只是一個View,不需要進行位置的處理。所以直接進入onDraw()方法,在
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
@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
;
}
}
首先也是先獲取當前的Drawable對象,如果不為空就開始繪圖,先是一個判斷,根據布局的方向來轉移畫布,isLayoutRtl()是View類的方法,
1
2
3
public
boolean
isLayoutRtl() {
return
(getLayoutDirection() == LAYOUT_DIRECTION_RTL);
}
這個LAYOUT_DIRECTION_RTL是LayoutDirection的一個常量,
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
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
;
}
然后再判斷有沒有動畫,如果有的話,就調用View類的postInvalidateOnAnimation()方法去執行一個動畫。最后調用Drawable對象去畫出來d.draw(canvas)。 總的來說,系統的ProgressBar是和Drawable緊密相關的,所以說,如果我們自定義的ProgressBar和Drawable有關,那么完全可以繼承於系統的ProgressBar來開發即可。如果你的自定義ProgressBar和Drawable關系不大,比如是這樣的,
其實,就不需要Drawable了,完全可以直接繼承於View類開發。 那下面就從兩個方面來自定義ProgressBar,一、繼承於系統ProgressBar 首先看一下上面給出的進度條其中的一個,
思路: Mini ProgressBar在原生ProgressBar的基礎上加入了一個指示器,並且有文字顯示。實現的時候可以這樣,
也就是說,自定義的ProgressBar包含了兩個部分,一部分是默認的;另一部分是新添加的指示器。其實指示器就是一個Drawable和文本的組合,而且直接畫在系統ProgressBar的上面即可。接着,關於自定義的ProgressBar的屬性也要定義一下,比如Drawable、比如文本、比如間隔等。所以attrs文件可以這樣來寫了:
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
<!--?xml version=
"1.0"
encoding =
"utf-8"
?-->
<resources>
<declare-styleable>
</attr>
<!-- attr-->
<!-- attr-->
</attr>
<flag name=
"normal"
value=
"0"
>
<flag name=
"bold"
value=
"1"
>
<flag name=
"italic"
value=
"2"
>
</flag></flag></flag></attr>
<flag name=
"left"
value=
"0"
>
<flag name=
"center"
value=
"1"
>
<flag name=
"right"
value=
"2"
>
</flag></flag></flag></attr>
</attr></attr></declare-styleable>
</resources>
ps:我發現eclipse在寫declare-styleable不會自動提示,不清楚什么原因,知道的朋友望告知。 之后我們新建一個類繼承於ProgressBar,
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
/**
* @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);
}
}
然后在第三個構造方法中初始化數據,因為用到了文本以及Drawable,所以還需要聲明全局變量,初始化完畢后代碼如下:
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
/**
*
*/
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();
}
}
}
然后,為全局變量設置set、get方法,方便在程序中調用。
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
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()、onDraw()方法了。在onMeasure()中,需要對進度條計算好具體大小,那根據上面的圖示,這個進度條的寬度和系統進度條的寬度是一樣的,也就是getMeasuredWidth();高度的話,因為加了一個指示器,所以高度是指示器的高度加上系統進度條的高度。因此在onMeasure()方法中就可以這樣來寫:
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
@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;
}
然后是onDraw()方法,因為在onMeasure()方法中增加了進度條的高度,所以在畫的時候需要將系統進度條與指示器分隔開來。在進度條的樣式文件中,我們是這樣配置的:
1
2
3
4
5
6
7
<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>
在android:progressDrawable的屬性中,使用的drawable是這樣的:
1
2
3
4
5
6
7
8
9
10
<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"
>
</bitmap></item>
</item></layer-list>
可以發現,是一個layer類型的drawable,所以在計算大小的時候,需要特別考慮這個情況。代碼如下:
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
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();
}
}
然后需要更新進度條的位置,
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
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);
}
}
}
}
最后,需要把指示器畫出來,
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
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();
}
源碼下載: