前三种实现方式代码出自:
http://stormzhang.com/openandroid/2013/11/15/android-custom-loading/
(源代码下载)http://download.csdn.net/detail/chaoyu168/9616035
近期一直在学习自己定义控件,搜了很多大牛们Blog里分享的小教程。也上GitHub找了一些类似的控件进行学习。发现读起来都不太好懂,就想写这么一篇东西作为学习笔记吧。
一、控件介绍:
进度条在App中非经常见,比例如以下载进度、载入图片、打开文章、打开网页等等……都须要这么一个效果让用户知道我们的App正在读取。以构造良好的交互。假设没有这样一个效果的话。用户没法知道东西有没有下载好、图片载入了没有、文章打开了没……会让用户非常不爽。基于这种情景我们的UI设计师们创造了这样一个控件。
二、这篇文章会涉及的知识点:
跟我一样刚入门的Android菜鸟们。我推荐大家先了解一下这些知识点再往下看。
这些知识点我也会推荐一些博客给大家看看。更推荐大家看文档里的解释,当然大牛们能够直接无视……
1、ClipDrawable类:可以对一个drawable类进行剪切操作(即仅仅显示某一部分的区域,还有一部分隐藏),显示多大的区域由level控制(level取值是0~10000)
【博客:http://blog.csdn.net/lonelyroamer/article/details/8244777】、没文档的能够在这看【http://www.apihome.cn/api/android/ClipDrawable.html】
2、自己定义View:guolin大神的深入学习View四部曲
【Android
LayoutInflater原理分析,带你一步步深入了解View——http://blog.csdn.net/guolin_blog/article/details/12921889】
【Android视图绘制流程全然解析。带你一步步深入了解View——http://blog.csdn.net/guolin_blog/article/details/16330267】
【Android视图状态及重绘流程分析,带你一步步深入了解View——http://blog.csdn.net/guolin_blog/article/details/17045157】
【Android自己定义View的实现方法,带你一步步深入了解View——http://blog.csdn.net/guolin_blog/article/details/17357967】
3、没看过我写的:Android自己定义控件——老版优酷三级菜单的话,也许须要看看这个:
【RotateAnimation具体解释——http://blog.csdn.net/u012403246/article/details/41415799】
三、Android上的实现方式:
(前三种方法比較简单,第四种方法是GitHub项目的解析,对前三种没兴趣能够直接跳到后边……)
1、效果图:
将进度条的变换过程分解为一帧一帧的图片,将这些一帧一帧的图片连起来构成一个动画。
经常使用于:手机阅读网页、逛社区时,载入图片、文章等不须要清楚知道载入进度,可是须要知道是否进行载入的情景。
这样的方法实现能够通过创建一个animation-list的XML文件,然后给系统API提供的ProgressBar的indeterminateDrawable属性就能够了。(这个属性应该是类似于设置一个动画吧……)
XML:
-
<?xml version="1.0" encoding="utf-8"?>
-
<animation-list xmlns:android="http://schemas.android.com/apk/res/android"
-
android:oneshot="false" >
-
<item android:duration="150" >
-
<clip
-
android:clipOrientation="horizontal"
-
android:drawable="@drawable/loading_01"
-
android:gravity="left"/>
-
</item>
-
<item android:duration="150" >
-
<clip
-
android:clipOrientation="horizontal"
-
android:drawable="@drawable/loading_02"
-
android:gravity="left"/>
-
</item>
-
<item android:duration="150" >
-
<clip
-
android:clipOrientation="horizontal"
-
android:drawable="@drawable/loading_03"
-
android:gravity="left"/>
-
</item>
-
<item android:duration="150" >
-
<clip
-
android:clipOrientation="horizontal"
-
android:drawable="@drawable/loading_04"
-
android:gravity="left"/>
-
</item>
-
<item android:duration="150" >
-
<clip
-
android:clipOrientation="horizontal"
-
android:drawable="@drawable/loading_05"
-
android:gravity="left"/>
-
</item>
-
<item android:duration="150" >
-
<clip
-
android:clipOrientation="horizontal"
-
android:drawable="@drawable/loading_06"
-
android:gravity="left"/>
-
</item>
-
<item android:duration="150" >
-
<clip
-
android:clipOrientation="horizontal"
-
android:drawable="@drawable/loading_07"
-
android:gravity="left"/>
-
</item>
-
<item android:duration="150" >
-
<clip
-
android:clipOrientation="horizontal"
-
android:drawable="@drawable/loading_08"
-
android:gravity="left"/>
-
</item>
-
<item android:duration="150" >
-
<clip
-
android:clipOrientation="horizontal"
-
android:drawable="@drawable/loading_09"
-
android:gravity="left"/>
-
</item>
-
<item android:duration="150" >
-
<clip
-
android:clipOrientation="horizontal"
-
android:drawable="@drawable/loading_10"
-
android:gravity="left"/>
-
</item>
-
<item android:duration="150" >
-
<clip
-
android:clipOrientation="horizontal"
-
android:drawable="@drawable/loading_11"
-
android:gravity="left"/>
-
</item>
-
<item android:duration="150" >
-
<clip
-
android:clipOrientation="horizontal"
-
android:drawable="@drawable/loading_12"
-
android:gravity="left"/>
-
</item>
-
-
</animation-list>
-
<ProgressBar
-
android:layout_width="wrap_content"
-
android:layout_height="wrap_content"
-
android:indeterminateDrawable="@drawable/progressbar1"
-
/>
2、效果图:
在上一篇有关自己定义控件的博客里我们使用了一个RotateAnimation类来实现旋转效果 (http://blog.csdn.net/u012403246/article/details/41309161),事实上。我们在这里也能够把一张图片,通过旋转。达到我们要的效果。本质上和上一种方法没多大差别。
我们仅仅须要创建一个rotate的XML。对其属性进行一些简单的设置。然后增加我们要用的图片就能够了。
XML:
-
<?xml version="1.0" encoding="utf-8"?>
-
<rotate xmlns:android="http://schemas.android.com/apk/res/android"
-
android:pivotX="50%"
-
android:pivotY="50%"
-
android:fromDegrees="0"
-
android:toDegrees="360"
-
android:interpolator="@android:anim/accelerate_decelerate_interpolator" >
-
<bitmap
-
android:antialias="true"
-
android:filter="true"
-
android:src="@drawable/loading_360"/>
-
-
</rotate>
-
<ProgressBar
-
android:layout_width="wrap_content"
-
android:layout_height="wrap_content"
-
android:indeterminateDrawable="@drawable/progressbar2"/>
3、效果图:
我们能够弄两张照片。第一张是纯黑色的,然后把这张照片中心挖一个圆出来,圆区域弄成白色,挖出来的圆弄成第二张照片。我们最好还是叠加显示两张照片,刚開始把第二张全然“遮住”。随着载入进度的添加,我们降低遮住的区域把第二张照片慢慢的显示出来。
Android上刚好就有这么一个ClipDrawable类,可以实现剪裁的过程。
我们来看看怎么通过这种方式自己定义一个进度条控件。
代码:
-
public class MyProgressBar extends FrameLayout{
-
private boolean running;
-
private int progress = 0;
-
private static final int MAX_PROGRESS = 10000;
-
-
private ClipDrawable clip;
-
-
private Handler handler = new Handler(){
-
@Override
-
public void handleMessage(android.os.Message msg) {
-
if(msg.what == 0x123)
-
clip.setLevel(progress);
-
}
-
};
-
-
public MyProgressBar(Context context){
-
this(context,null,0);
-
}
-
-
public MyProgressBar(Context context,AttributeSet attrs){
-
this(context,null,0);
-
}
-
-
public MyProgressBar(Context context, AttributeSet attrs, int defStyle) {
-
super(context, attrs, defStyle);
-
Init(context);
-
}
-
-
public void Init(Context context){
-
View view = LayoutInflater.from(context).inflate(R.layout.view, null);
-
-
ImageView iv = (ImageView)view.findViewById(R.id.progress_img);
-
-
addView(view);
-
clip = (ClipDrawable)iv.getDrawable();
-
-
Thread thread = new Thread(new Runnable() {
-
-
@Override
-
public void run() {
-
running = true;
-
while(running){
-
handler.sendEmptyMessage(0x123);
-
if(progress == MAX_PROGRESS)
-
progress = 0;
-
progress += 100;
-
try {
-
Thread.sleep(18);
-
} catch (InterruptedException e) {
-
e.printStackTrace();
-
}
-
}
-
}
-
});
-
thread.start();
-
}
-
-
public void stop(){
-
progress = 0;
-
running = false;
-
}
-
}
通过代码我们能够看到。逻辑很easy。关键就在于ClipDrawable的setLevel()方法,这个是设置剪裁效果的。
4、效果图:
实现一个View的子类——Progress Wheel类,实现进度条效果。
详细的内容我都写在了凝视上,假设不了解自己定义控件的知识。能够去阅读guolin博客里自己定义View四部曲的解说,讲的挺好的。
代码:
-
public class ProgressWheel extends View {
-
-
-
private int layout_height = 0;
-
private int layout_width = 0;
-
private int fullRadius = 100;
-
private int circleRadius = 80;
-
private int barLength = 60;
-
private int barWidth = 20;
-
private int rimWidth = 20;
-
private int textSize = 20;
-
private float contourSize = 0;
-
-
-
private int paddingTop = 5;
-
private int paddingBottom = 5;
-
private int paddingLeft = 5;
-
private int paddingRight = 5;
-
-
-
private int barColor = 0xAA000000;
-
private int contourColor = 0xAA000000;
-
private int circleColor = 0x00000000;
-
private int rimColor = 0xAADDDDDD;
-
private int textColor = 0xFF000000;
-
-
-
private Paint barPaint = new Paint();
-
private Paint circlePaint = new Paint();
-
private Paint rimPaint = new Paint();
-
private Paint textPaint = new Paint();
-
private Paint contourPaint = new Paint();
-
-
-
@SuppressWarnings("unused")
-
private RectF rectBounds = new RectF();
-
private RectF circleBounds = new RectF();
-
private RectF circleOuterContour = new RectF();
-
private RectF circleInnerContour = new RectF();
-
-
-
-
private int spinSpeed = 2;
-
-
private int delayMillis = 0;
-
int progress = 0;
-
boolean isSpinning = false;
-
-
-
private String text = "";
-
private String[] splitText = {};
-
-
-
-
-
-
-
-
public ProgressWheel(Context context, AttributeSet attrs) {
-
super(context, attrs);
-
-
parseAttributes(context.obtainStyledAttributes(attrs,
-
R.styleable.ProgressWheel));
-
}
-
-
-
-
-
-
-
-
-
-
-
@Override
-
protected void onMeasure(int widthMeasureSpec, int heightMeasureSpec) {
-
-
-
-
super.onMeasure(widthMeasureSpec, heightMeasureSpec);
-
-
-
-
-
int size = 0;
-
int width = getMeasuredWidth();
-
int height = getMeasuredHeight();
-
int widthWithoutPadding = width - getPaddingLeft() - getPaddingRight();
-
int heigthWithoutPadding = height - getPaddingTop() - getPaddingBottom();
-
-
-
-
-
if (widthWithoutPadding > heigthWithoutPadding) {
-
size = heigthWithoutPadding;
-
} else {
-
size = widthWithoutPadding;
-
}
-
-
-
-
-
-
-
setMeasuredDimension(size + getPaddingLeft() + getPaddingRight(), size + getPaddingTop() + getPaddingBottom());
-
}
-
-
-
-
-
-
-
@Override
-
protected void onSizeChanged(int w, int h, int oldw, int oldh) {
-
super.onSizeChanged(w, h, oldw, oldh);
-
-
-
layout_width = w;
-
layout_height = h;
-
-
setupBounds();
-
setupPaints();
-
invalidate();
-
}
-
-
-
-
-
private void setupPaints() {
-
barPaint.setColor(barColor);
-
barPaint.setAntiAlias(true);
-
barPaint.setStyle(Style.STROKE);
-
barPaint.setStrokeWidth(barWidth);
-
-
rimPaint.setColor(rimColor);
-
rimPaint.setAntiAlias(true);
-
rimPaint.setStyle(Style.STROKE);
-
rimPaint.setStrokeWidth(rimWidth);
-
-
circlePaint.setColor(circleColor);
-
circlePaint.setAntiAlias(true);
-
circlePaint.setStyle(Style.FILL);
-
-
textPaint.setColor(textColor);
-
textPaint.setStyle(Style.FILL);
-
textPaint.setAntiAlias(true);
-
textPaint.setTextSize(textSize);
-
-
contourPaint.setColor(contourColor);
-
contourPaint.setAntiAlias(true);
-
contourPaint.setStyle(Style.STROKE);
-
contourPaint.setStrokeWidth(contourSize);
-
}
-
-
-
-
-
private void setupBounds() {
-
-
int minValue = Math.min(layout_width, layout_height);
-
-
-
int xOffset = layout_width - minValue;
-
int yOffset = layout_height - minValue;
-
-
-
paddingTop = this.getPaddingTop() + (yOffset / 2);
-
paddingBottom = this.getPaddingBottom() + (yOffset / 2);
-
paddingLeft = this.getPaddingLeft() + (xOffset / 2);
-
paddingRight = this.getPaddingRight() + (xOffset / 2);
-
-
int width = getWidth();
-
int height = getHeight();
-
-
rectBounds = new RectF(paddingLeft,
-
paddingTop,
-
width - paddingRight,
-
height - paddingBottom);
-
-
circleBounds = new RectF(paddingLeft + barWidth,
-
paddingTop + barWidth,
-
width - paddingRight - barWidth,
-
height - paddingBottom - barWidth);
-
circleInnerContour = new RectF(circleBounds.left + (rimWidth / 2.0f) + (contourSize / 2.0f), circleBounds.top + (rimWidth / 2.0f) + (contourSize / 2.0f), circleBounds.right - (rimWidth / 2.0f) - (contourSize / 2.0f), circleBounds.bottom - (rimWidth / 2.0f) - (contourSize / 2.0f));
-
circleOuterContour = new RectF(circleBounds.left - (rimWidth / 2.0f) - (contourSize / 2.0f), circleBounds.top - (rimWidth / 2.0f) - (contourSize / 2.0f), circleBounds.right + (rimWidth / 2.0f) + (contourSize / 2.0f), circleBounds.bottom + (rimWidth / 2.0f) + (contourSize / 2.0f));
-
-
fullRadius = (width - paddingRight - barWidth) / 2;
-
circleRadius = (fullRadius - barWidth) + 1;
-
}
-
-
-
-
-
-
-
private void parseAttributes(TypedArray a) {
-
barWidth = (int) a.getDimension(R.styleable.ProgressWheel_barWidth,
-
barWidth);
-
-
rimWidth = (int) a.getDimension(R.styleable.ProgressWheel_rimWidth,
-
rimWidth);
-
-
spinSpeed = (int) a.getDimension(R.styleable.ProgressWheel_spinSpeed,
-
spinSpeed);
-
-
delayMillis = a.getInteger(R.styleable.ProgressWheel_delayMillis,
-
delayMillis);
-
if (delayMillis < 0) {
-
delayMillis = 0;
-
}
-
-
barColor = a.getColor(R.styleable.ProgressWheel_barColor, barColor);
-
-
barLength = (int) a.getDimension(R.styleable.ProgressWheel_barLength,
-
barLength);
-
-
textSize = (int) a.getDimension(R.styleable.ProgressWheel_textSize,
-
textSize);
-
-
textColor = (int) a.getColor(R.styleable.ProgressWheel_textColor,
-
textColor);
-
-
-
if (a.hasValue(R.styleable.ProgressWheel_text)) {
-
setText(a.getString(R.styleable.ProgressWheel_text));
-
}
-
-
rimColor = (int) a.getColor(R.styleable.ProgressWheel_rimColor,
-
rimColor);
-
-
circleColor = (int) a.getColor(R.styleable.ProgressWheel_circleColor,
-
circleColor);
-
-
contourColor = a.getColor(R.styleable.ProgressWheel_contourColor, contourColor);
-
contourSize = a.getDimension(R.styleable.ProgressWheel_contourSize, contourSize);
-
-
-
-
a.recycle();
-
}
-
-
-
-
-
-
protected void onDraw(Canvas canvas) {
-
super.onDraw(canvas);
-
-
canvas.drawArc(circleBounds, 360, 360, false, circlePaint);
-
-
canvas.drawArc(circleBounds, 360, 360, false, rimPaint);
-
canvas.drawArc(circleOuterContour, 360, 360, false, contourPaint);
-
canvas.drawArc(circleInnerContour, 360, 360, false, contourPaint);
-
-
if (isSpinning) {
-
canvas.drawArc(circleBounds, progress - 90, barLength, false,
-
barPaint);
-
} else {
-
canvas.drawArc(circleBounds, -90, progress, false, barPaint);
-
}
-
-
float textHeight = textPaint.descent() - textPaint.ascent();
-
float verticalTextOffset = (textHeight / 2) - textPaint.descent();
-
-
for (String s : splitText) {
-
float horizontalTextOffset = textPaint.measureText(s) / 2;
-
canvas.drawText(s, this.getWidth() / 2 - horizontalTextOffset,
-
this.getHeight() / 2 + verticalTextOffset, textPaint);
-
}
-
if (isSpinning) {
-
scheduleRedraw();
-
}
-
}
-
-
private void scheduleRedraw() {
-
progress += spinSpeed;
-
if (progress > 360) {
-
progress = 0;
-
}
-
postInvalidateDelayed(delayMillis);
-
}
-
-
-
-
-
-
public boolean isSpinning() {
-
if(isSpinning){
-
return true;
-
} else {
-
return false;
-
}
-
}
-
-
-
-
-
public void resetCount() {
-
progress = 0;
-
setText("0%");
-
invalidate();
-
}
-
-
-
-
-
public void stopSpinning() {
-
isSpinning = false;
-
progress = 0;
-
postInvalidate();
-
}
-
-
-
-
-
-
public void spin() {
-
isSpinning = true;
-
postInvalidate();
-
}
-
-
-
-
-
public void incrementProgress() {
-
isSpinning = false;
-
progress++;
-
if (progress > 360)
-
progress = 0;
-
setText(Math.round(((float) progress / 360) * 100) + "%");
-
postInvalidate();
-
}
-
-
-
-
-
-
public void setProgress(int i) {
-
isSpinning = false;
-
progress = i;
-
postInvalidate();
-
}
-
-
-
-
-
-
-
-
-
-
-
public void setText(String text) {
-
this.text = text;
-
splitText = this.text.split("\n");
-
}
-
-
public int getCircleRadius() {
-
return circleRadius;
-
}
-
-
public void setCircleRadius(int circleRadius) {
-
this.circleRadius = circleRadius;
-
}
-
-
public int getBarLength() {
-
return barLength;
-
}
-
-
public void setBarLength(int barLength) {
-
this.barLength = barLength;
-
}
-
-
public int getBarWidth() {
-
return barWidth;
-
}
-
-
public void setBarWidth(int barWidth) {
-
this.barWidth = barWidth;
-
-
if ( this.barPaint != null ) {
-
this.barPaint.setStrokeWidth( this.barWidth );
-
}
-
}
-
-
public int getTextSize() {
-
return textSize;
-
}
-
-
public void setTextSize(int textSize) {
-
this.textSize = textSize;
-
-
if ( this.textPaint != null ) {
-
this.textPaint.setTextSize( this.textSize );
-
}
-
}
-
-
public int getPaddingTop() {
-
return paddingTop;
-
}
-
-
public void setPaddingTop(int paddingTop) {
-
this.paddingTop = paddingTop;
-
}
-
-
public int getPaddingBottom() {
-
return paddingBottom;
-
}
-
-
public void setPaddingBottom(int paddingBottom) {
-
this.paddingBottom = paddingBottom;
-
}
-
-
public int getPaddingLeft() {
-
return paddingLeft;
-
}
-
-
public void setPaddingLeft(int paddingLeft) {
-
this.paddingLeft = paddingLeft;
-
}
-
-
public int getPaddingRight() {
-
return paddingRight;
-
}
-
-
public void setPaddingRight(int paddingRight) {
-
this.paddingRight = paddingRight;
-
}
-
-
public int getBarColor() {
-
return barColor;
-
}
-
-
public void setBarColor(int barColor) {
-
this.barColor = barColor;
-
-
if ( this.barPaint != null ) {
-
this.barPaint.setColor( this.barColor );
-
}
-
}
-
-
public int getCircleColor() {
-
return circleColor;
-
}
-
-
public void setCircleColor(int circleColor) {
-
this.circleColor = circleColor;
-
-
if ( this.circlePaint != null ) {
-
this.circlePaint.setColor( this.circleColor);
-
}
-
}
-
-
public int getRimColor() {
-
return rimColor;
-
}
-
-
public void setRimColor(int rimColor) {
-
this.rimColor = rimColor;
-
-
if ( this.rimPaint != null ) {
-
this.rimPaint.setColor( this.rimColor );
-
}
-
}
-
-
-
public Shader getRimShader() {
-
return rimPaint.getShader();
-
}
-
-
public void setRimShader(Shader shader) {
-
this.rimPaint.setShader(shader);
-
}
-
-
public int getTextColor() {
-
return textColor;
-
}
-
-
public void setTextColor(int textColor) {
-
this.textColor = textColor;
-
-
if ( this.textPaint != null ) {
-
this.textPaint.setColor( this.textColor );
-
}
-
}
-
-
public int getSpinSpeed() {
-
return spinSpeed;
-
}
-
-
public void setSpinSpeed(int spinSpeed) {
-
this.spinSpeed = spinSpeed;
-
}
-
-
public int getRimWidth() {
-
return rimWidth;
-
}
-
-
public void setRimWidth(int rimWidth) {
-
this.rimWidth = rimWidth;
-
-
if ( this.rimPaint != null ) {
-
this.rimPaint.setStrokeWidth( this.rimWidth );
-
}
-
}
-
-
public int getDelayMillis() {
-
return delayMillis;
-
}
-
-
public void setDelayMillis(int delayMillis) {
-
this.delayMillis = delayMillis;
-
}
-
-
public int getContourColor() {
-
return contourColor;
-
}
-
-
public void setContourColor(int contourColor) {
-
this.contourColor = contourColor;
-
-
if ( contourPaint != null ) {
-
this.contourPaint.setColor( this.contourColor );
-
}
-
}
-
-
public float getContourSize() {
-
return this.contourSize;
-
}
-
-
public void setContourSize(float contourSize) {
-
this.contourSize = contourSize;
-
-
if ( contourPaint != null ) {
-
this.contourPaint.setStrokeWidth( this.contourSize );
-
}
-
}
-
}