-
这一次主要说一下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>
12345678<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>
1234567891011121314151617181920212223242526272829303132333435363738394041424344<!--?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.
-->
<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的代码量不算多,除去注释估计也就是几百行左右。首先从构造方法看是看,12345678910111213141516171819202122232425262728293031323334353637383940414243444546474849505152535455565758596061626364656667686970717273747576777879/**
* 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();
}
1234567891011121314151617181920212223242526272829303132333435363738R.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>
12345678910111213private
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
;
}
1234567891011121314151617@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
));
}
12345678910111213141516171819202122232425262728293031323334@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
;
}
}
123public
boolean
isLayoutRtl() {
return
(getLayoutDirection() == LAYOUT_DIRECTION_RTL);
}
1234567891011121314151617181920212223242526272829303132package
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了,完全可以直接继承于View类开发。 那下面就从两个方面来自定义ProgressBar,一、继承于系统ProgressBar 首先看一下上面给出的进度条其中的一个,
思路: Mini ProgressBar在原生ProgressBar的基础上加入了一个指示器,并且有文字显示。实现的时候可以这样,
也就是说,自定义的ProgressBar包含了两个部分,一部分是默认的;另一部分是新添加的指示器。其实指示器就是一个Drawable和文本的组合,而且直接画在系统ProgressBar的上面即可。接着,关于自定义的ProgressBar的属性也要定义一下,比如Drawable、比如文本、比如间隔等。所以attrs文件可以这样来写了:
123456789101112131415161718192021<!--?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>
123456789101112131415161718192021222324/**
* @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);
}
}
1234567891011121314151617181920212223242526272829303132333435363738394041424344454647484950515253545556575859/**
*
*/
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();
}
}
}
123456789101112131415public
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;
}
1234567891011121314151617181920212223242526@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;
}
1234567<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>
12345678910<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>
123456789101112131415161718if
(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();
}
}
12345678910111213141516171819202122232425262728293031323334353637383940414243444546private
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);
}
}
}
}
12345678910111213141516171819202122232425262728293031if
(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();
}
-
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的其他常用属性,
-
Widget.ProgressBar.HorizontalWidget.ProgressBar.SmallWidget.ProgressBar.LargeWidget.ProgressBar.InverseWidget.ProgressBar.Small.InverseWidget.ProgressBar.Large.Inverse 使用的时候可以这样:style="@android:style/Widget.ProgressBar.Small"。另外还有一种方式就是使用系统的attr,上面的方式是系统的style:
Android ProgressBar详解以及自定义
最新推荐文章于 2022-10-29 17:38:53 发布