package cn.darkal.networkdiagnosis.View;
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.graphics.Shader;
import android.os.Handler;
import android.os.Message;
import android.util.AttributeSet;
import android.view.View;
import cn.darkal.networkdiagnosis.R;
/**
* An indicator of progress, similar to Android's ProgressBar.
* Can be used in 'spin mode' or 'increment mode'
* @author Todd Davies
*
* Licensed under the Creative Commons Attribution 3.0 license see:
* http://creativecommons.org/licenses/by/3.0/
*/
public class ProgressWheel extends View {
//Sizes (with defaults)
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 int barDegree = 60;
private float arcR = barWidth/2; //内部圆弧的半径
//Padding (with defaults)
private int paddingTop = 5;
private int paddingBottom = 5;
private int paddingLeft = 5;
private int paddingRight = 5;
//Colors (with defaults)
private int barColor = 0xAA000000;
private int circleColor = 0x0000ffff;
private int rimColor = 0xAADDDDDD;
private int spinRimColor = 0xAADDDDDD;
private int textColor = 0xFF000000;
private int spinCircleColor = 0x00000000;
//Paints
private Paint barPaint = new Paint(); //圆画笔
private Paint circlePaint = new Paint(); //内部填充圆画笔
private Paint barCirclePaint = new Paint(); //圆弧上两端点圆的画笔
private Paint rimPaint = new Paint(); //底部圈画笔
private Paint textPaint = new Paint();
private Paint spinRimPaint = new Paint(); //旋转时,底部圈画笔,主要用于此时改变底部圈颜色
private Paint spinCirclePaint = new Paint();//旋转时,圆的画笔
//Rectangles
@SuppressWarnings("unused")
private RectF rectBounds = new RectF();
private RectF circleBounds = new RectF();
private int startDegree = -90; //圆弧的起始位置, -90 顶上
private float startArcX = 0;
private float startArcY = 0;
//Animation
//The amount of pixels to move the bar by on each draw
private int spinSpeed = 2;
//The number of milliseconds to wait inbetween each draw
private int delayMillis = 0;
private Handler spinHandler = new Handler() {
/**
* This is the code that will increment the progress variable
* and so spin the wheel
*/
@Override
public void handleMessage(Message msg) {
switch (msg.what) {
case 0:
invalidate();
if (isSpinning) {
progress += spinSpeed;
if (progress > 360) {
progress = 0;
}
spinHandler.sendEmptyMessageDelayed(0, delayMillis);
}
break;
case 1:
spinHandler.removeMessages(0);
isSpinning = false;
invalidate();
break;
}
}
};
int progress = 0;
boolean isSpinning = false;
//Other
private String text = "";
private String[] splitText = {};
/**
* The constructor for the ProgressWheel
* @param context
* @param attrs
*/
public ProgressWheel(Context context, AttributeSet attrs) {
super(context, attrs);
parseAttributes(context.obtainStyledAttributes(attrs,
R.styleable.ProgressWheel));
}
//----------------------------------
//Setting up stuff
//----------------------------------
/**
* Now we know the dimensions of the view, setup the bounds and paints
*/
@Override
public void onAttachedToWindow() {
super.onAttachedToWindow();
setupBounds();
setupPaints();
invalidate();
}
@Override
protected void onDetachedFromWindow() {
invalidate();
super.onDetachedFromWindow();
}
/**
* 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);
spinRimPaint.setColor(spinRimColor);
spinRimPaint.setAntiAlias(true);
spinRimPaint.setStyle(Style.STROKE);
spinRimPaint.setStrokeWidth(rimWidth);
spinCirclePaint.setColor(spinCircleColor);
spinCirclePaint.setAntiAlias(true);
spinCirclePaint.setStyle(Style.FILL);
spinCirclePaint.setShadowLayer(1, 2, 2, 0x40000000);
circlePaint.setColor(circleColor);
circlePaint.setAntiAlias(true);
circlePaint.setStyle(Style.FILL);
barCirclePaint.setColor(barColor);
barCirclePaint.setAntiAlias(true);
barCirclePaint.setStyle(Style.FILL);
barCirclePaint.setStrokeWidth(barWidth);
textPaint.setColor(textColor);
textPaint.setStyle(Style.FILL);
textPaint.setAntiAlias(true);
textPaint.setTextSize(textSize);
}
/**
* Set the bounds of the component
*/
private void setupBounds() {
paddingTop = this.getPaddingTop();
paddingBottom = this.getPaddingBottom();
paddingLeft = this.getPaddingLeft();
paddingRight = this.getPaddingRight();
rectBounds = new RectF(paddingLeft,
paddingTop,
this.getLayoutParams().width - paddingRight,
this.getLayoutParams().height - paddingBottom);
circleBounds = new RectF(paddingLeft + barWidth,
paddingTop + barWidth,
this.getLayoutParams().width - paddingRight - barWidth,
this.getLayoutParams().height - paddingBottom - barWidth);
fullRadius = (this.getLayoutParams().width - paddingRight - barWidth)/2;
circleRadius = (fullRadius - barWidth) + 1; //内部圆的半径
arcR = barWidth/2; //圆弧的半径
startArcX = (float) (-circleBounds.width()/2* Math.sin( 2* Math.PI/360*(startDegree+270))+this.getLayoutParams().width/2); //计算时角度要换成弧度
startArcY = (float) (circleBounds.height()/2* Math.cos(2 * Math.PI / 360 * (startDegree+270)) + this.getLayoutParams().height/2);
}
/**
* Parse the attributes passed to the view from the XML
* @param a the attributes to parse
*/
private void parseAttributes(TypedArray a) {
barWidth = (int) a.getDimension(R.styleable.ProgressWheel_barWidth_progress,
barWidth);
rimWidth = (int) a.getDimension(R.styleable.ProgressWheel_rimWidth_progress,
rimWidth);
spinSpeed = (int) a.getInteger(R.styleable.ProgressWheel_spinSpeed_progress,
spinSpeed);
delayMillis = (int) a.getInteger(R.styleable.ProgressWheel_delayMillis_progress,
delayMillis);
if(delayMillis<0) {
delayMillis = 0;
}
barColor = a.getColor(R.styleable.ProgressWheel_barColor_progress, barColor);
barLength = (int) a.getDimension(R.styleable.ProgressWheel_barLength_progress,
barLength);
textSize = (int) a.getDimension(R.styleable.ProgressWheel_textSize_progress,
textSize);
textColor = (int) a.getColor(R.styleable.ProgressWheel_textColor_progress,
textColor);
setText(a.getString(R.styleable.ProgressWheel_text_progress));
rimColor = (int) a.getColor(R.styleable.ProgressWheel_rimColor_progress,
rimColor);
spinRimColor = (int) a.getColor(R.styleable.ProgressWheel_spinRimColor_progress,
spinRimColor);
circleColor = (int) a.getColor(R.styleable.ProgressWheel_circleColor_progress, circleColor);
spinCircleColor = (int) a.getColor(R.styleable.ProgressWheel_spinCircleColor_progress, spinCircleColor);
barDegree = (int) a.getInteger(R.styleable.ProgressWheel_barDegree_progress,-1);
}
//----------------------------------
//Animation stuff
//----------------------------------
protected void onDraw(Canvas canvas) {
super.onDraw(canvas);
//Draw the rim
if(isSpinning()){
canvas.drawArc(circleBounds, 360, 360, false, spinRimPaint);
}else {
canvas.drawArc(circleBounds, 360, 360, false, rimPaint);
}
//Draw the bar
if(isSpinning) {
if(barDegree != -1) { //按度数
canvas.drawArc(circleBounds, progress - 90, barDegree, false,
barPaint);
//结束度数
double t = (progress - 90+barDegree + 270);
//起始点坐标
float startX = (float) (-circleBounds.width()/2* Math.sin( 2* Math.PI/360*(progress - 90+270))+this.getLayoutParams().width/2); //计算时角度要换成弧度
float startY = (float) (circleBounds.height()/2* Math.cos(2 * Math.PI / 360 * (progress - 90 + 270)) + this.getLayoutParams().height/2);
//结束点坐标
float endX = (float) (-circleBounds.width() / 2 * Math.sin(2 * Math.PI / 360 * t) + this.getLayoutParams().width / 2);
float endY = (float) (circleBounds.height() / 2 * Math.cos(2 * Math.PI / 360 * t) + this.getLayoutParams().height / 2);
//计算两点间距离
float tmpR = (float) Math.sqrt((endX-startX)*(endX-startX)+(endY-startY)*(endY-startY));
// canvas.drawCircle(startX, startY, tmpR, barPaint); //画圆弧起始圆
// canvas.drawCircle(x, y, tmpR, barPaint); //画圆弧起始圆
//确定圆心点
double tmp = (progress - 90 + barDegree)+(360-barDegree)/2+270;
float x2 = (float) (-circleBounds.width() / 2 * Math.sin(2 * Math.PI / 360 * tmp) + this.getLayoutParams().width / 2);
float y2 = (float) (circleBounds.height() / 2 * Math.cos(2 * Math.PI / 360 * tmp) + this.getLayoutParams().height / 2);
canvas.drawCircle(x2, y2, tmpR, spinCirclePaint); //画圆
}else{ //按长度
canvas.drawArc(circleBounds, progress - 90, barLength, false,
barPaint);
//结束度数
double t = (progress - 90+barLength + 270);
//起始点坐标
float startX = (float) (-circleBounds.width()/2* Math.sin( 2* Math.PI/360*(progress - 90+270))+this.getLayoutParams().width/2); //计算时角度要换成弧度
float startY = (float) (circleBounds.height()/2* Math.cos(2 * Math.PI / 360 * (progress - 90 + 270)) + this.getLayoutParams().height/2);
//结束点坐标
float endX = (float) (-circleBounds.width() / 2 * Math.sin(2 * Math.PI / 360 * t) + this.getLayoutParams().width / 2);
float endY = (float) (circleBounds.height() / 2 * Math.cos(2 * Math.PI / 360 * t) + this.getLayoutParams().height / 2);
//计算两点间距离
float tmpR = (float) Math.sqrt((endX-startX)*(endX-startX)+(endY-startY)*(endY-startY));
// canvas.drawCircle(startX, startY, tmpR, barPaint); //画圆弧起始圆
// canvas.drawCircle(x, y, tmpR, barPaint); //画圆弧起始圆
//确定圆心点
double tmp = (progress - 90 + barLength)+(360-barLength)/2+270;
float x2 = (float) (-circleBounds.width() / 2 * Math.sin(2 * Math.PI / 360 * tmp) + this.getLayoutParams().width / 2);
float y2 = (float) (circleBounds.height() / 2 * Math.cos(2 * Math.PI / 360 * tmp) + this.getLayoutParams().height / 2);
canvas.drawCircle(x2, y2, tmpR, spinCirclePaint); //画圆
}
} else {
canvas.drawArc(circleBounds, startDegree, progress, false, barPaint); // -90 从顶上开始
double t = progress+startDegree+270;
if(progress != 0) {
canvas.drawCircle(startArcX, startArcY, arcR, barCirclePaint); //画圆弧起始圆
float x = (float) (-circleBounds.width() / 2 * Math.sin(2 * Math.PI / 360 * t) + this.getLayoutParams().width / 2);
float y = (float) (circleBounds.height() / 2 * Math.cos(2 * Math.PI / 360 * t) + this.getLayoutParams().height / 2);
canvas.drawCircle(x, y, arcR, barCirclePaint); //画圆弧结束圆
}
}
//Draw the inner circle
canvas.drawCircle((circleBounds.width()/2) + rimWidth + paddingLeft,
(circleBounds.height()/2) + rimWidth + paddingTop,
circleRadius,
circlePaint);
//Draw the text (attempts to center it horizontally and vertically)
int offsetNum = 0;
for(String s : splitText) {
float offset = textPaint.measureText(s) / 2;
canvas.drawText(s, this.getWidth() / 2 - offset,
this.getHeight() / 2 + (textSize*(offsetNum))
- ((splitText.length-1)*(textSize/2)), textPaint);
offsetNum++;
}
}
/**
* Reset the count (in increment mode)
*/
public void resetCount() {
progress = 0;
setText("0%");
invalidate();
}
/**
* Turn off spin mode
*/
public void stopSpinning() {
spinHandler.sendEmptyMessageDelayed(1,200);
}
/**
* Puts the view on spin mode
*/
public void spin() {
isSpinning = true;
spinHandler.sendEmptyMessage(0);
}
public boolean isSpinning(){
return isSpinning;
}
/**
* Increment the progress by 1 (of 360)
*/
public void incrementProgress() {
isSpinning = false;
progress++;
setText(Math.round(((float)progress/360)*100) + "%");
spinHandler.sendEmptyMessage(0);
}
/**
* Set the progress to a specific value
*/
public void setProgress(int i) {
isSpinning = false;
progress=i;
spinHandler.sendEmptyMessage(0);
}
//----------------------------------
//Getters + setters
//----------------------------------
/**
* Set the text in the progress bar
* Doesn't invalidate the view
* @param text the text to show ('\n' constitutes a new line)
*/
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;
}
public int getTextSize() {
return textSize;
}
public void setTextSize(int textSize) {
this.textSize = 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;
}
public int getCircleColor() {
return circleColor;
}
public void setCircleColor(int circleColor) {
this.circleColor = circleColor;
}
public int getRimColor() {
return rimColor;
}
public void setRimColor(int rimColor) {
this.rimColor = rimColor;
}
public int getSpinRimColor() {
return spinRimColor;
}
public void setSpinRimColor(int spinRimColor) {
this.spinRimColor = spinRimColor;
}
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;
}
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;
}
public int getDelayMillis() {
return delayMillis;
}
public void setDelayMillis(int delayMillis) {
this.delayMillis = delayMillis;
}
}