activity_main.xml主页布局就2个button,分别弹出不同效果的2个进度条
<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:tools="http://schemas.android.com/tools"
android:layout_width="match_parent"
android:layout_height="match_parent"
tools:context="com.example.dialog.MainActivity" >
<Button
android:id="@+id/button1"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:text="@string/hello_world" />
<Button
android:id="@+id/button2"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:text="@string/hello_world" />
</LinearLayout>
MainActivity
package com.example.dialog;
import android.os.Bundle;
import android.support.v4.app.FragmentActivity;
import android.view.View;
public class MainActivity extends FragmentActivity {
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_main);
findViewById(R.id.button1).setOnClickListener(
new View.OnClickListener() {
@Override
public void onClick(View v) {
MyloadDialog myloadDialog = new MyloadDialog(
MainActivity.this, 1);
myloadDialog.show();
}
});
findViewById(R.id.button2).setOnClickListener(
new View.OnClickListener() {
@Override
public void onClick(View v) {
MyloadDialog myloadDialog = new MyloadDialog(
MainActivity.this, 2);
myloadDialog.show();
}
});
}
}
MyloadDialog
package com.example.dialog;
import android.app.Dialog;
import android.content.Context;
import android.graphics.Color;
import android.view.View;
import android.widget.ImageView;
import android.widget.TextView;
public class MyloadDialog extends Dialog{
public MyloadDialog(Context context,int args) {
super(context, R.style.PerfectDialog);
init(args);
}
private int barColor = Color.parseColor("#ff009688");
private void init(int args) {
if (args == 1) {
View contentView = View.inflate(getContext(),
R.layout.dialog_load_classic_layout, null);
setContentView(contentView);
LoadClassicView loadClassicView = (LoadClassicView) contentView
.findViewById(R.id.dialogLoadView);
loadClassicView.startLoad();
}else if (args == 2) {
View contentView = View.inflate(getContext(),
R.layout.dialog_load_material_layout, null);
setContentView(contentView);
LoadMaterialView progressWheel = (LoadMaterialView) contentView.findViewById(R.id.dialogLoadView);
progressWheel.setBarColor(barColor);
progressWheel.spin();
}
}
}
</style>
<style name="PerfectDialog" parent="@android:style/Theme.Dialog">
<item name="android:windowIsFloating">true</item>//是否浮现在activity之上
<item name="android:windowIsTranslucent">false</item>//是否半透明
<item name="android:windowNoTitle">true</item>//是否显示title标题
<item name="android:windowFrame">@null</item>//设置windowFrame框
<item name="android:windowFullscreen">false</item>//是否全屏显示
<item name="android:windowBackground">@android:color/transparent</item>//设置dialog的背景
<item name="android:windowAnimationStyle">@style/DialogAnims</item>//设置窗口动画效果
<item name="android:backgroundDimEnabled">true</item>//背景是否变暗
<item name="android:backgroundDimAmount">0.5</item>//设置背景变暗程度
<item name="android:windowCloseOnTouchOutside">false</item>
<item name="android:background">@android:color/transparent</item>
</style>
<style name="DialogAnims">
<item name="android:windowEnterAnimation">@anim/enter</item>//进入动画
<item name="android:windowExitAnimation">@anim/exit</item>//退出动画
</style>
enter.xml
<?xml version="1.0" encoding="utf-8"?>
<set xmlns:android="http://schemas.android.com/apk/res/android">
<alpha
android:duration="90"
android:fromAlpha="0"
android:toAlpha="1"/>
<scale
android:duration="135"
android:fromXScale="0.8"
android:toXScale="1.05"
android:fromYScale="0.8"
android:toYScale="1.05"
android:pivotX="50%"
android:pivotY="50%"/>
<scale
android:duration="105"
android:fromXScale="1.05"
android:toXScale="0.95"
android:fromYScale="1.05"
android:toYScale="0.95"
android:startOffset="135"
android:pivotX="50%"
android:pivotY="50%"/>
<scale
android:duration="135"
android:fromXScale="0.95"
android:toXScale="1"
android:fromYScale="0.95"
android:toYScale="1"
android:startOffset="240"
android:pivotX="50%"
android:pivotY="50%"/>
</set>
exit.xml
<?xml version="1.0" encoding="utf-8"?>
<set xmlns:android="http://schemas.android.com/apk/res/android">
<alpha
android:duration="200"
android:fromAlpha="1"
android:toAlpha="0"/>
<scale
android:duration="200"
android:fromXScale="1"
android:toXScale="0.1"
android:fromYScale="1"
android:toYScale="0.1"
android:pivotX="50%"
android:pivotY="50%"/>
</set>
首先看带第一个效果图的布局文件
dialog_load_classic_layout.xml
<?xml version="1.0" encoding="utf-8"?>
<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
android:orientation="vertical"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:background="@drawable/background_round_corner"
android:padding="35dp">
<com.example.dialog.LoadClassicView
android:id="@+id/dialogLoadView"
android:layout_width="45dp"
android:layout_height="45dp" />
</LinearLayout>
背景圆角background_round_corner.xml
<?xml version="1.0" encoding="utf-8"?>
<shape xmlns:android="http://schemas.android.com/apk/res/android"
android:shape="rectangle">
<corners android:radius="8dp"/>
<solid android:color="#ffffff"/>
</shape>
LoadClassicView
package com.example.dialog;
import android.animation.ArgbEvaluator;
import android.content.Context;
import android.content.res.Resources;
import android.graphics.Canvas;
import android.graphics.Color;
import android.graphics.Paint;
import android.util.AttributeSet;
import android.util.DisplayMetrics;
import android.util.TypedValue;
import android.view.View;
/**
* Created by dandy on 2016/5/31.
*/
public class LoadClassicView extends View {
private static final String TAG = "LoadingView";
private static final long DELAY_DURATION = 65;
private static final float SIZE = 22f;//默认大小
private static final float RAIDUS = 18f;//内部圆半径
private static final int START_COLOR = Color.parseColor("#5a5a5a");//起始颜色
private static final int END_COLOR = Color.parseColor("#dddddd");//结束颜色
private static final int COUNT = 12;//默认加载条个数
private static final float STROKE_WIDTH = 6.0f;//加载条粗值
private float size = SIZE;
private float radius = RAIDUS;
private int startColor = START_COLOR;
private int endColor = END_COLOR;
private int count = COUNT;
private float strokeWidth = STROKE_WIDTH;
private DisplayMetrics dm;
private ArgbEvaluator colorEvaluator;
private int [] colors;//加载条颜色
private LoadingLine [] loadingLines;//加载条集合
private Paint paint;
private double startAngle = 0;
private int exactlySize;
private int startIndex = 0;
/**
* 构造方法
* @param context
*/
public LoadClassicView(Context context){
this(context, null);
}
public LoadClassicView(Context context, AttributeSet attributeSet){
super(context, attributeSet);
setUpInit(attributeSet);
}
private void setUpInit(AttributeSet set){
dm = Resources.getSystem().getDisplayMetrics();
paint = new Paint();
paint.setAntiAlias(true);
paint.setStrokeWidth(strokeWidth);
paint.setStrokeCap(Paint.Cap.ROUND);
initColor();
initLoadingLines();
}
/*
* 初始化每个的颜色
*/
private void initColor(){
colorEvaluator = new ArgbEvaluator();
colors = new int[count];
for(int i = 0;i < count;i++){
colors[i] = (Integer)colorEvaluator.evaluate(i*1.0f/(count-1),startColor,endColor);
}
}
/**
* 为每个赋值颜色值
*/
private void initLoadingLines(){
loadingLines = new LoadingLine[count];
for(int i = 0;i <count;i++){
LoadingLine loadingLine = new LoadingLine();
loadingLine.drawColor = colors[i];
loadingLines[i] = loadingLine;
}
}
/**
* 设置显示颜色
* @param index,线段颜色初始化位置
*/
private void setColor(int index){
int lineIndex;
for(int i = 0;i < count;i++){
lineIndex = index + i;
loadingLines[lineIndex >= count?lineIndex - count:lineIndex].drawColor = colors[i];
}
}
@Override
protected void onMeasure(int widthMeasureSpec, int heightMeasureSpec) {
/**计算宽**/
final int widthMode = MeasureSpec.getMode(widthMeasureSpec);
int width = MeasureSpec.getSize(widthMeasureSpec);
if(widthMode == MeasureSpec.UNSPECIFIED || widthMode == MeasureSpec.AT_MOST){
width = applyDimension(size);
}
/**计算高**/
final int heightMode = MeasureSpec.getMode(heightMeasureSpec);
int height = MeasureSpec.getSize(heightMeasureSpec);
if (heightMode == MeasureSpec.UNSPECIFIED || heightMode == MeasureSpec.AT_MOST) {
height = applyDimension(size);
}
/**
* 取小的值作为控件的大小
*/
exactlySize = width >= height ? height:width;
this.radius = 0.22f * exactlySize;
setMeasuredDimension(exactlySize,exactlySize);
}
@Override
protected void onLayout(boolean changed, int left, int top, int right, int bottom) {
float delayAngle = 360.0f / count;
LoadingLine loadingLine;
double value;
for(int i = 0;i < count;i++){
loadingLine = loadingLines[i];
value = startAngle * Math.PI / 180;
loadingLine.startX = (int) Math.round(radius * Math.cos(value));
loadingLine.startY = (int) Math.round(radius * Math.sin(value));
loadingLine.endX = (int) Math.round(exactlySize / 2.5f * Math.cos(value));
loadingLine.endY = (int) Math.round(exactlySize / 2.5f * Math.sin(value));
startAngle += delayAngle;
}
startAngle = 0;
}
@Override
protected void onDraw(Canvas canvas) {
canvas.save();
canvas.translate(exactlySize/2.0f,exactlySize/2.0f);
for(int i = 0; i < count;i++){
LoadingLine loadingLine = loadingLines[i];
paint.setColor(loadingLine.drawColor);
canvas.drawLine(loadingLine.startX, loadingLine.startY, loadingLine.endX, loadingLine.endY, paint);
}
canvas.restore();
}
public void startLoad(){
postDelayed(runnable,100);
}
public void finishLoad(){
removeCallbacks(runnable);
}
private Runnable runnable = new Runnable() {
@Override
public void run() {
postInvalidate();
removeCallbacks(runnable);
setColor(startIndex % count);
startIndex++;
postDelayed(runnable,DELAY_DURATION);
}
};
/**
* px2dp
* @param value
*/
private int applyDimension(float value){
return (int) TypedValue.applyDimension(TypedValue.COMPLEX_UNIT_DIP, value, dm);
}
private static class LoadingLine{
private int drawColor;
private int startX;
private int startY;
private int endX;
private int endY;
}
}
接下来看第二个图片
dialog_load_material_layout.xml
<?xml version="1.0" encoding="utf-8"?>
<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:app="http://schemas.android.com/apk/res-auto"
android:orientation="vertical"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:background="@drawable/background_round_corner"
android:padding="35dp">
<com.example.dialog.LoadMaterialView
android:id="@id/dialogLoadView"
android:layout_width="45dp"
android:layout_height="45dp" />
</LinearLayout>
LoadMaterialView
package com.example.dialog;
/**
* Created by Seeker on 2016/8/4.
*
* @copy https://github.com/pnikosis/materialish-progress
*/
import android.annotation.TargetApi;
import android.content.Context;
import android.content.res.TypedArray;
import android.graphics.Canvas;
import android.graphics.Paint;
import android.graphics.Paint.Style;
import android.graphics.RectF;
import android.os.Build;
import android.os.Parcel;
import android.os.Parcelable;
import android.os.SystemClock;
import android.provider.Settings;
import android.util.AttributeSet;
import android.util.DisplayMetrics;
import android.util.TypedValue;
import android.view.View;
public class LoadMaterialView extends View {
private static final String TAG = "ProgressWheel";
private final int barLength = 16;
private final int barMaxLength = 270;
private final long pauseGrowingTime = 200;
/**
* *********
* DEFAULTS *
* **********
*/
//Sizes (with defaults in DP)
private int circleRadius = 28;
private int barWidth = 4;
private int rimWidth = 4;
private boolean fillRadius = false;
private double timeStartGrowing = 0;
private double barSpinCycleTime = 460;
private float barExtraLength = 0;
private boolean barGrowingFromFront = true;
private long pausedTimeWithoutGrowing = 0;
//Colors (with defaults)
private int barColor = 0xAA000000;
private int rimColor = 0x00FFFFFF;
//Paints
private Paint barPaint = new Paint();
private Paint rimPaint = new Paint();
//Rectangles
private RectF circleBounds = new RectF();
//Animation
//The amount of degrees per second
private float spinSpeed = 230.0f;
//private float spinSpeed = 120.0f;
// The last time the spinner was animated
private long lastTimeAnimated = 0;
private boolean linearProgress;
private float mProgress = 0.0f;
private float mTargetProgress = 0.0f;
private boolean isSpinning = false;
private ProgressCallback callback;
private boolean shouldAnimate;
/**
* The constructor for the ProgressWheel
*/
public LoadMaterialView(Context context, AttributeSet attrs) {
super(context, attrs);
parseAttributes(context.obtainStyledAttributes(attrs, R.styleable.LoadMaterialView));
setAnimationEnabled();
}
/**
* The constructor for the ProgressWheel
*/
public LoadMaterialView(Context context) {
super(context);
setAnimationEnabled();
}
@TargetApi(Build.VERSION_CODES.JELLY_BEAN_MR1)
private void setAnimationEnabled() {
int currentApiVersion = Build.VERSION.SDK_INT;
float animationValue;
if (currentApiVersion >= Build.VERSION_CODES.JELLY_BEAN_MR1) {
animationValue = Settings.Global.getFloat(getContext().getContentResolver(),
Settings.Global.ANIMATOR_DURATION_SCALE, 1);
} else {
animationValue = Settings.System.getFloat(getContext().getContentResolver(),
Settings.System.ANIMATOR_DURATION_SCALE, 1);
}
shouldAnimate = animationValue != 0;
}
//----------------------------------
//Setting up stuff
//----------------------------------
@Override
protected void onMeasure(int widthMeasureSpec, int heightMeasureSpec) {
super.onMeasure(widthMeasureSpec, heightMeasureSpec);
int viewWidth = circleRadius + this.getPaddingLeft() + this.getPaddingRight();
int viewHeight = circleRadius + this.getPaddingTop() + this.getPaddingBottom();
int widthMode = MeasureSpec.getMode(widthMeasureSpec);
int widthSize = MeasureSpec.getSize(widthMeasureSpec);
int heightMode = MeasureSpec.getMode(heightMeasureSpec);
int heightSize = MeasureSpec.getSize(heightMeasureSpec);
int width;
int height;
//Measure Width
if (widthMode == MeasureSpec.EXACTLY) {
//Must be this size
width = widthSize;
} else if (widthMode == MeasureSpec.AT_MOST) {
//Can't be bigger than...
width = Math.min(viewWidth, widthSize);
} else {
//Be whatever you want
width = viewWidth;
}
//Measure Height
if (heightMode == MeasureSpec.EXACTLY || widthMode == MeasureSpec.EXACTLY) {
//Must be this size
height = heightSize;
} else if (heightMode == MeasureSpec.AT_MOST) {
//Can't be bigger than...
height = Math.min(viewHeight, heightSize);
} else {
//Be whatever you want
height = viewHeight;
}
setMeasuredDimension(width, height);
}
/**
* Use onSizeChanged instead of onAttachedToWindow to get the dimensions of the view,
* because this method is called after measuring the dimensions of MATCH_PARENT & WRAP_CONTENT.
* Use this dimensions to setup the bounds and paints.
*/
@Override
protected void onSizeChanged(int w, int h, int oldw, int oldh) {
super.onSizeChanged(w, h, oldw, oldh);
setupBounds(w, h);
setupPaints();
invalidate();
}
/**
* Set the properties of the paints we're using to
* draw the progress wheel
*/
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);
}
/**
* Set the bounds of the component
*/
private void setupBounds(int layout_width, int layout_height) {
int paddingTop = getPaddingTop();
int paddingBottom = getPaddingBottom();
int paddingLeft = getPaddingLeft();
int paddingRight = getPaddingRight();
if (!fillRadius) {
// Width should equal to Height, find the min value to setup the circle
int minValue = Math.min(layout_width - paddingLeft - paddingRight,
layout_height - paddingBottom - paddingTop);
int circleDiameter = Math.min(minValue, circleRadius * 2 - barWidth * 2);
// Calc the Offset if needed for centering the wheel in the available space
int xOffset = (layout_width - paddingLeft - paddingRight - circleDiameter) / 2 + paddingLeft;
int yOffset = (layout_height - paddingTop - paddingBottom - circleDiameter) / 2 + paddingTop;
circleBounds =
new RectF(xOffset + barWidth, yOffset + barWidth, xOffset + circleDiameter - barWidth,
yOffset + circleDiameter - barWidth);
} else {
circleBounds = new RectF(paddingLeft + barWidth, paddingTop + barWidth,
layout_width - paddingRight - barWidth, layout_height - paddingBottom - barWidth);
}
}
/**
* Parse the attributes passed to the view from the XML
*
* @param a the attributes to parse
*/
private void parseAttributes(TypedArray a) {
// We transform the default values from DIP to pixels
DisplayMetrics metrics = getContext().getResources().getDisplayMetrics();
barWidth = (int) TypedValue.applyDimension(TypedValue.COMPLEX_UNIT_DIP, barWidth, metrics);
rimWidth = (int) TypedValue.applyDimension(TypedValue.COMPLEX_UNIT_DIP, rimWidth, metrics);
circleRadius =
(int) TypedValue.applyDimension(TypedValue.COMPLEX_UNIT_DIP, circleRadius, metrics);
circleRadius =
(int) a.getDimension(R.styleable.LoadMaterialView_matProg_circleRadius, circleRadius);
fillRadius = a.getBoolean(R.styleable.LoadMaterialView_matProg_fillRadius, false);
barWidth = (int) a.getDimension(R.styleable.LoadMaterialView_matProg_barWidth, barWidth);
rimWidth = (int) a.getDimension(R.styleable.LoadMaterialView_matProg_rimWidth, rimWidth);
float baseSpinSpeed =
a.getFloat(R.styleable.LoadMaterialView_matProg_spinSpeed, spinSpeed / 360.0f);
spinSpeed = baseSpinSpeed * 360;
barSpinCycleTime =
a.getInt(R.styleable.LoadMaterialView_matProg_barSpinCycleTime, (int) barSpinCycleTime);
barColor = a.getColor(R.styleable.LoadMaterialView_matProg_barColor, barColor);
rimColor = a.getColor(R.styleable.LoadMaterialView_matProg_rimColor, rimColor);
linearProgress = a.getBoolean(R.styleable.LoadMaterialView_matProg_linearProgress, false);
if (a.getBoolean(R.styleable.LoadMaterialView_matProg_progressIndeterminate, false)) {
spin();
}
// Recycle
a.recycle();
}
public void setCallback(ProgressCallback progressCallback) {
callback = progressCallback;
if (!isSpinning) {
runCallback();
}
}
//----------------------------------
//Animation stuff
//----------------------------------
protected void onDraw(Canvas canvas) {
super.onDraw(canvas);
canvas.drawArc(circleBounds, 360, 360, false, rimPaint);
boolean mustInvalidate = false;
if (!shouldAnimate) {
return;
}
if (isSpinning) {
//Draw the spinning bar
mustInvalidate = true;
long deltaTime = (SystemClock.uptimeMillis() - lastTimeAnimated);
float deltaNormalized = deltaTime * spinSpeed / 1000.0f;
updateBarLength(deltaTime);
mProgress += deltaNormalized;
if (mProgress > 360) {
mProgress -= 360f;
// A full turn has been completed
// we run the callback with -1 in case we want to
// do something, like changing the color
runCallback(-1.0f);
}
lastTimeAnimated = SystemClock.uptimeMillis();
float from = mProgress - 90;
float length = barLength + barExtraLength;
if (isInEditMode()) {
from = 0;
length = 135;
}
canvas.drawArc(circleBounds, from, length, false, barPaint);
} else {
float oldProgress = mProgress;
if (mProgress != mTargetProgress) {
//We smoothly increase the progress bar
mustInvalidate = true;
float deltaTime = (float) (SystemClock.uptimeMillis() - lastTimeAnimated) / 1000;
float deltaNormalized = deltaTime * spinSpeed;
mProgress = Math.min(mProgress + deltaNormalized, mTargetProgress);
lastTimeAnimated = SystemClock.uptimeMillis();
}
if (oldProgress != mProgress) {
runCallback();
}
float offset = 0.0f;
float progress = mProgress;
if (!linearProgress) {
float factor = 2.0f;
offset = (float) (1.0f - Math.pow(1.0f - mProgress / 360.0f, 2.0f * factor)) * 360.0f;
progress = (float) (1.0f - Math.pow(1.0f - mProgress / 360.0f, factor)) * 360.0f;
}
if (isInEditMode()) {
progress = 360;
}
canvas.drawArc(circleBounds, offset - 90, progress, false, barPaint);
}
if (mustInvalidate) {
invalidate();
}
}
@Override
protected void onVisibilityChanged(View changedView, int visibility) {
super.onVisibilityChanged(changedView, visibility);
if (visibility == VISIBLE) {
lastTimeAnimated = SystemClock.uptimeMillis();
}
}
private void updateBarLength(long deltaTimeInMilliSeconds) {
if (pausedTimeWithoutGrowing >= pauseGrowingTime) {
timeStartGrowing += deltaTimeInMilliSeconds;
if (timeStartGrowing > barSpinCycleTime) {
// We completed a size change cycle
// (growing or shrinking)
timeStartGrowing -= barSpinCycleTime;
//if(barGrowingFromFront) {
pausedTimeWithoutGrowing = 0;
//}
barGrowingFromFront = !barGrowingFromFront;
}
float distance =
(float) Math.cos((timeStartGrowing / barSpinCycleTime + 1) * Math.PI) / 2 + 0.5f;
float destLength = (barMaxLength - barLength);
if (barGrowingFromFront) {
barExtraLength = distance * destLength;
} else {
float newLength = destLength * (1 - distance);
mProgress += (barExtraLength - newLength);
barExtraLength = newLength;
}
} else {
pausedTimeWithoutGrowing += deltaTimeInMilliSeconds;
}
}
/**
* Check if the wheel is currently spinning
*/
public boolean isSpinning() {
return isSpinning;
}
/**
* Reset the count (in increment mode)
*/
public void resetCount() {
mProgress = 0.0f;
mTargetProgress = 0.0f;
invalidate();
}
/**
* Turn off spin mode
*/
public void stopSpinning() {
isSpinning = false;
mProgress = 0.0f;
mTargetProgress = 0.0f;
invalidate();
}
/**
* Puts the view on spin mode
*/
public void spin() {
lastTimeAnimated = SystemClock.uptimeMillis();
isSpinning = true;
invalidate();
}
private void runCallback(float value) {
if (callback != null) {
callback.onProgressUpdate(value);
}
}
private void runCallback() {
if (callback != null) {
float normalizedProgress = (float) Math.round(mProgress * 100 / 360.0f) / 100;
callback.onProgressUpdate(normalizedProgress);
}
}
/**
* Set the progress to a specific value,
* the bar will be set instantly to that value
*
* @param progress the progress between 0 and 1
*/
public void setInstantProgress(float progress) {
if (isSpinning) {
mProgress = 0.0f;
isSpinning = false;
}
if (progress > 1.0f) {
progress -= 1.0f;
} else if (progress < 0) {
progress = 0;
}
if (progress == mTargetProgress) {
return;
}
mTargetProgress = Math.min(progress * 360.0f, 360.0f);
mProgress = mTargetProgress;
lastTimeAnimated = SystemClock.uptimeMillis();
invalidate();
}
// Great way to save a view's state http://stackoverflow.com/a/7089687/1991053
@Override
public Parcelable onSaveInstanceState() {
Parcelable superState = super.onSaveInstanceState();
WheelSavedState ss = new WheelSavedState(superState);
// We save everything that can be changed at runtime
ss.mProgress = this.mProgress;
ss.mTargetProgress = this.mTargetProgress;
ss.isSpinning = this.isSpinning;
ss.spinSpeed = this.spinSpeed;
ss.barWidth = this.barWidth;
ss.barColor = this.barColor;
ss.rimWidth = this.rimWidth;
ss.rimColor = this.rimColor;
ss.circleRadius = this.circleRadius;
ss.linearProgress = this.linearProgress;
ss.fillRadius = this.fillRadius;
return ss;
}
@Override
public void onRestoreInstanceState(Parcelable state) {
if (!(state instanceof WheelSavedState)) {
super.onRestoreInstanceState(state);
return;
}
WheelSavedState ss = (WheelSavedState) state;
super.onRestoreInstanceState(ss.getSuperState());
this.mProgress = ss.mProgress;
this.mTargetProgress = ss.mTargetProgress;
this.isSpinning = ss.isSpinning;
this.spinSpeed = ss.spinSpeed;
this.barWidth = ss.barWidth;
this.barColor = ss.barColor;
this.rimWidth = ss.rimWidth;
this.rimColor = ss.rimColor;
this.circleRadius = ss.circleRadius;
this.linearProgress = ss.linearProgress;
this.fillRadius = ss.fillRadius;
this.lastTimeAnimated = SystemClock.uptimeMillis();
}
/**
* @return the current progress between 0.0 and 1.0,
* if the wheel is indeterminate, then the result is -1
*/
public float getProgress() {
return isSpinning ? -1 : mProgress / 360.0f;
}
//----------------------------------
//Getters + setters
//----------------------------------
/**
* Set the progress to a specific value,
* the bar will smoothly animate until that value
*
* @param progress the progress between 0 and 1
*/
public void setProgress(float progress) {
if (isSpinning) {
mProgress = 0.0f;
isSpinning = false;
runCallback();
}
if (progress > 1.0f) {
progress -= 1.0f;
} else if (progress < 0) {
progress = 0;
}
if (progress == mTargetProgress) {
return;
}
// If we are currently in the right position
// we set again the last time animated so the
// animation starts smooth from here
if (mProgress == mTargetProgress) {
lastTimeAnimated = SystemClock.uptimeMillis();
}
mTargetProgress = Math.min(progress * 360.0f, 360.0f);
invalidate();
}
/**
* Sets the determinate progress mode
*
* @param isLinear if the progress should increase linearly
*/
public void setLinearProgress(boolean isLinear) {
linearProgress = isLinear;
if (!isSpinning) {
invalidate();
}
}
/**
* @return the radius of the wheel in pixels
*/
public int getCircleRadius() {
return circleRadius;
}
/**
* Sets the radius of the wheel
*
* @param circleRadius the expected radius, in pixels
*/
public void setCircleRadius(int circleRadius) {
this.circleRadius = circleRadius;
if (!isSpinning) {
invalidate();
}
}
/**
* @return the width of the spinning bar
*/
public int getBarWidth() {
return barWidth;
}
/**
* Sets the width of the spinning bar
*
* @param barWidth the spinning bar width in pixels
*/
public void setBarWidth(int barWidth) {
this.barWidth = barWidth;
if (!isSpinning) {
invalidate();
}
}
/**
* @return the color of the spinning bar
*/
public int getBarColor() {
return barColor;
}
/**
* Sets the color of the spinning bar
*
* @param barColor The spinning bar color
*/
public void setBarColor(int barColor) {
this.barColor = barColor;
setupPaints();
if (!isSpinning) {
invalidate();
}
}
/**
* @return the color of the wheel's contour
*/
public int getRimColor() {
return rimColor;
}
/**
* Sets the color of the wheel's contour
*
* @param rimColor the color for the wheel
*/
public void setRimColor(int rimColor) {
this.rimColor = rimColor;
setupPaints();
if (!isSpinning) {
invalidate();
}
}
/**
* @return the base spinning speed, in full circle turns per second
* (1.0 equals on full turn in one second), this value also is applied for
* the smoothness when setting a progress
*/
public float getSpinSpeed() {
return spinSpeed / 360.0f;
}
/**
* Sets the base spinning speed, in full circle turns per second
* (1.0 equals on full turn in one second), this value also is applied for
* the smoothness when setting a progress
*
* @param spinSpeed the desired base speed in full turns per second
*/
public void setSpinSpeed(float spinSpeed) {
this.spinSpeed = spinSpeed * 360.0f;
}
/**
* @return the width of the wheel's contour in pixels
*/
public int getRimWidth() {
return rimWidth;
}
/**
* Sets the width of the wheel's contour
*
* @param rimWidth the width in pixels
*/
public void setRimWidth(int rimWidth) {
this.rimWidth = rimWidth;
if (!isSpinning) {
invalidate();
}
}
public interface ProgressCallback {
/**
* Method to call when the progress reaches a value
* in order to avoid float precision issues, the progress
* is rounded to a float with two decimals.
*
* In indeterminate mode, the callback is called each time
* the wheel completes an animation cycle, with, the progress value is -1.0f
*
* @param progress a double value between 0.00 and 1.00 both included
*/
public void onProgressUpdate(float progress);
}
static class WheelSavedState extends BaseSavedState {
//required field that makes Parcelables from a Parcel
public static final Creator<WheelSavedState> CREATOR =
new Creator<WheelSavedState>() {
public WheelSavedState createFromParcel(Parcel in) {
return new WheelSavedState(in);
}
public WheelSavedState[] newArray(int size) {
return new WheelSavedState[size];
}
};
float mProgress;
float mTargetProgress;
boolean isSpinning;
float spinSpeed;
int barWidth;
int barColor;
int rimWidth;
int rimColor;
int circleRadius;
boolean linearProgress;
boolean fillRadius;
WheelSavedState(Parcelable superState) {
super(superState);
}
private WheelSavedState(Parcel in) {
super(in);
this.mProgress = in.readFloat();
this.mTargetProgress = in.readFloat();
this.isSpinning = in.readByte() != 0;
this.spinSpeed = in.readFloat();
this.barWidth = in.readInt();
this.barColor = in.readInt();
this.rimWidth = in.readInt();
this.rimColor = in.readInt();
this.circleRadius = in.readInt();
this.linearProgress = in.readByte() != 0;
this.fillRadius = in.readByte() != 0;
}
@Override
public void writeToParcel(Parcel out, int flags) {
super.writeToParcel(out, flags);
out.writeFloat(this.mProgress);
out.writeFloat(this.mTargetProgress);
out.writeByte((byte) (isSpinning ? 1 : 0));
out.writeFloat(this.spinSpeed);
out.writeInt(this.barWidth);
out.writeInt(this.barColor);
out.writeInt(this.rimWidth);
out.writeInt(this.rimColor);
out.writeInt(this.circleRadius);
out.writeByte((byte) (linearProgress ? 1 : 0));
out.writeByte((byte) (fillRadius ? 1 : 0));
}
}
}
attrs_LoadMaterialView.xml
<?xml version="1.0" encoding="utf-8"?>
<resources>
<declare-styleable name="LoadMaterialView">
<attr name="matProg_progressIndeterminate" format="boolean" />
<attr name="matProg_barColor" format="color" />
<attr name="matProg_rimColor" format="color" />
<attr name="matProg_rimWidth" format="dimension" />
<attr name="matProg_spinSpeed" format="float" />
<attr name="matProg_barSpinCycleTime" format="integer" />
<attr name="matProg_circleRadius" format="dimension" />
<attr name="matProg_fillRadius" format="boolean" />
<attr name="matProg_barWidth" format="dimension" />
<attr name="matProg_linearProgress" format="boolean" />
</declare-styleable>
</resources>