Android 可滾動圓形進度條 滑塊和進度在進度條上面跟著滾動

Android 可滾動圓形進度條 滑塊和進度在進度條上面跟著滾動。package com.example.test;

import android.content.Context;

import android.content.res.TypedArray;

import android.graphics.Canvas;

import android.graphics.Color;

import android.graphics.Matrix;

import android.graphics.Paint;

import android.graphics.PointF;

import android.graphics.RectF;

import android.graphics.drawable.Drawable;

import android.util.AttributeSet;

import android.view.MotionEvent;

import android.view.View;

public class MusicProgressBar extends View{

/**

* 畫筆對象的引用

*/

private Paint paint;

/**

* 圓環的顏色

*/

private int roundColor;

/**

* 圓環進度的顏色

*/

private int roundProgressColor;

/**

* 圓環的寬度

*/

private float roundWidth;

/**

* 最大進度

*/

private int max;

/**

* 當前進度

*/

private int progress;

/**

* 中間進度百分比的字符串的顏色

*/

private int textColor;

/**

* 中間進度百分比的字符串的字體

*/

private float textSize;

/**

* 點的半徑

*/

private float pointRadius;

/**

* 空心點的寬度

*/

private float pointWidth;

private Drawable mThumb, mThumbPress;

public MusicProgressBar(Context context) {

this(context, null);

}

public MusicProgressBar(Context context, AttributeSet attrs) {

this(context, attrs, 0);

}

public MusicProgressBar(Context context, AttributeSet attrs, int defStyle) {

super(context, attrs, defStyle);

paint = new Paint();

//TypedArray mTypedArray = context.obtainStyledAttributes(attrs, R.styleable.RoundProgressBar);

//獲取自定義屬性和默認值

roundColor =Color.RED;// mTypedArray.getColor(R.styleable.RoundProgressBar_roundColor, Color.RED);

roundProgressColor =Color.GREEN;// mTypedArray.getColor(R.styleable.RoundProgressBar_roundProgressColor, Color.GREEN);

roundWidth =3;// mTypedArray.getDimension(R.styleable.RoundProgressBar_roundWidth, 3);

textColor =Color.BLACK;// mTypedArray.getColor(R.styleable.RoundProgressBar_textColor, Color.GREEN);

textSize =8;// mTypedArray.getDimension(R.styleable.RoundProgressBar_textSize, 15);

max =100;// mTypedArray.getInteger(R.styleable.RoundProgressBar_imageMax, 100);

pointRadius =3;// mTypedArray.getDimension(R.styleable.RoundProgressBar_pointRadius, 3);

pointWidth =2;// mTypedArray.getDimension(R.styleable.RoundProgressBar_pointWidth, 2);

// mTypedArray.recycle();

// 加載拖動圖標

mThumb = getResources().getDrawable(R.drawable.ic_launcher);// 圓點圖片

int thumbHalfheight = mThumb.getIntrinsicHeight() / 2;

int thumbHalfWidth = mThumb.getIntrinsicWidth() / 2;

mThumb.setBounds(-thumbHalfWidth, -thumbHalfheight, thumbHalfWidth, thumbHalfheight);

mThumbPress = getResources().getDrawable(R.drawable.ic_launcher);// 圓點圖片

thumbHalfheight = mThumbPress.getIntrinsicHeight() / 2;

thumbHalfWidth = mThumbPress.getIntrinsicWidth() / 2;

mThumbPress.setBounds(-thumbHalfWidth, -thumbHalfheight, thumbHalfWidth, thumbHalfheight);

paddingOuterThumb = thumbHalfheight*2;

}

@Override

public void onDraw(Canvas canvas) {

/**

* 畫最外層的大圓環

*/

paint.setColor(roundColor); //設置圓環的顏色

paint.setStyle(Paint.Style.STROKE); //設置空心

paint.setStrokeWidth(roundWidth); //設置圓環的寬度

paint.setAntiAlias(true); //消除鋸齒

canvas.drawCircle(centerX, centerY, radius, paint); //畫出圓環

/**

* 畫文字

*/

paint.setStrokeWidth(0);

paint.setColor(textColor);

paint.setTextSize(textSize);

// paint.setTypeface(Typeface.DEFAULT); //設置字體

String textTime = getTimeText(progress);

float textWidth = paint.measureText(textTime); //測量字體寬度,我們需要根據字體的寬度設置在圓環中間

// canvas.drawText(textTime, centerX – textWidth / 2, centerY + textSize/2, paint);

/**

* 畫圓弧 ,畫圓環的進度

*/

paint.setStrokeWidth(roundWidth); //設置圓環的寬度

paint.setColor(roundProgressColor); //設置進度的顏色

RectF oval = new RectF(centerX – radius, centerY – radius, centerX + radius, centerY + radius); //用於定義的圓弧的形狀和大小的界限

paint.setStyle(Paint.Style.STROKE);

canvas.drawArc(oval, 270, 360 * progress / max, false, paint); //根據進度畫圓弧

// 畫圓上的兩個點

paint.setStrokeWidth(pointWidth);

PointF startPoint = ChartUtil.calcArcEndPointXY(centerX, centerY, radius, 0, 270);

canvas.drawCircle(startPoint.x, startPoint.y, pointRadius, paint);

//paint.setStyle(Paint.Style.FILL_AND_STROKE);

PointF progressPoint = ChartUtil.calcArcEndPointXY(centerX, centerY, radius, 360 * progress / max, 270);

//canvas.drawCircle(progressPoint.x, progressPoint.y, pointRadius, paint);

// 畫Thumb

canvas.save();

canvas.translate((float)(progressPoint.x+15*Math.sin(360 * progress / max*0.0174533f)), (float)(progressPoint.y-15*Math.cos(360 * progress / max*0.0174533f)));

canvas.rotate(360 * progress / max);

if (downOnArc) {

mThumbPress.draw(canvas);

} else {

mThumb.draw(canvas);

}

canvas.restore();

paint.setStrokeWidth(0);

paint.setColor(textColor);

paint.setTextSize(textSize);

// paint.setTypeface(Typeface.DEFAULT); //設置字體

// String textTime = getTimeText(progress);

// float textWidth = paint.measureText(textTime);

progress-=5;

canvas.rotate(360*(progress+5) / max, (float)(progressPoint.x+26f*Math.sin(360 * progress / max*0.0174533f)),(float)(progressPoint.y-26f*Math.cos(360 * progress / max*0.0174533f)));

canvas.drawText(textTime,(float)(progressPoint.x+26f*Math.sin(360 * progress / max*0.0174533f)),(float)(progressPoint.y-26f*Math.cos(360 * progress / max*0.0174533f)), paint);

canvas.rotate(-360*(progress+5) / max, progressPoint.x, progressPoint.y);

progress+=5;

}

private boolean downOnArc = false;

@Override

public boolean onTouchEvent(MotionEvent event) {

int action = event.getAction();

int x = (int) event.getX();

int y = (int) event.getY();

switch (action) {

case MotionEvent.ACTION_DOWN:

if (isTouchArc(x, y)) {

downOnArc = true;

updateArc(x, y);

return true;

}

break;

case MotionEvent.ACTION_MOVE:

if (downOnArc) {

updateArc(x, y);

return true;

}

break;

case MotionEvent.ACTION_UP:

downOnArc = false;

invalidate();

if (changeListener != null) {

changeListener.onProgressChangeEnd(max, progress);

}

break;

}

return super.onTouchEvent(event);

}

private int centerX, centerY;

private int radius;

private int paddingOuterThumb;

@Override

protected void onSizeChanged(int width, int height, int oldw, int oldh) {

centerX = width / 2;

centerY = height / 2;

int minCenter = Math.min(centerX, centerY);

radius = (int) (minCenter – roundWidth/2 – paddingOuterThumb); //圓環的半徑

minValidateTouchArcRadius = (int) (radius – paddingOuterThumb*1.5f);

maxValidateTouchArcRadius = (int) (radius + paddingOuterThumb*1.5f);

super.onSizeChanged(width, height, oldw, oldh);

}

// 根據點的位置,更新進度

private void updateArc(int x, int y) {

int cx = x – getWidth() / 2;

int cy = y – getHeight() / 2;

// 計算角度,得出(-1->1)之間的數據,等同於(-180°->180°)

double angle = Math.atan2(cy, cx)/Math.PI;

// 將角度轉換成(0->2)之間的值,然後加上90°的偏移量

angle = ((2 + angle)%2 + (90/180f))%2;

// 用(0->2)之間的角度值乘以總進度,等於當前進度

progress = (int) (angle * max/2);

if (changeListener != null) {

changeListener.onProgressChange(max, progress);

}

invalidate();

}

private int minValidateTouchArcRadius; // 最小有效點擊半徑

private int maxValidateTouchArcRadius; // 最大有效點擊半徑

// 判斷是否按在圓邊上

private boolean isTouchArc(int x, int y) {

double d = getTouchRadius(x, y);

if (d >= minValidateTouchArcRadius && d <= maxValidateTouchArcRadius) {

return true;

}

return false;

}

// 計算某點到圓點的距離

private double getTouchRadius(int x, int y) {

int cx = x – getWidth() / 2;

int cy = y – getHeight() / 2;

return Math.hypot(cx, cy);

}

private String getTimeText(int progress) {

int minute = progress / 60;

int second = progress % 60;

String result = (minute < 10 ? "0" : "") + minute + ":" + (second < 10 ? "0" : "") + second;

return result;

}

public synchronized int getMax() {

return max;

}

/**

* 設置進度的最大值

* @param max

*/

public synchronized void setMax(int max) {

if(max < 0){

throw new IllegalArgumentException("max not less than 0");

}

this.max = max;

}

/**

* 獲取進度.需要同步

* @return

*/

public synchronized int getProgress() {

return progress;

}

/**

* 設置進度,此為線程安全控件,由於考慮多線的問題,需要同步

* 刷新界面調用postInvalidate()能在非UI線程刷新

* @param progress

*/

public synchronized void setProgress(int progress) {

if(progress < 0){

throw new IllegalArgumentException("progress not less than 0");

}

if(progress > max){

progress = max;

}

if(progress <= max){

this.progress = progress;

postInvalidate();

}

}

public int getCricleColor() {

return roundColor;

}

public void setCricleColor(int cricleColor) {

this.roundColor = cricleColor;

}

public int getCricleProgressColor() {

return roundProgressColor;

}

public void setCricleProgressColor(int cricleProgressColor) {

this.roundProgressColor = cricleProgressColor;

}

public float getRoundWidth() {

return roundWidth;

}

public void setRoundWidth(float roundWidth) {

this.roundWidth = roundWidth;

}

public static class ChartUtil {

/**

* 依圓心坐標,半徑,扇形角度,計算出扇形終射線與圓弧交叉點的xy坐標

* @param cirX

* @param cirY

* @param radius

* @param cirAngle

* @return

*/

public static PointF calcArcEndPointXY(float cirX, float cirY, float radius, float cirAngle){

float posX = 0.0f;

float posY = 0.0f;

//將角度轉換為弧度

float arcAngle = (float) (Math.PI * cirAngle / 180.0);

if (cirAngle < 90)

{

posX = cirX + (float)(Math.cos(arcAngle)) * radius;

posY = cirY + (float)(Math.sin(arcAngle)) * radius;

}

else if (cirAngle == 90)

{

posX = cirX;

posY = cirY + radius;

}

else if (cirAngle > 90 && cirAngle < 180)

{

arcAngle = (float) (Math.PI * (180 – cirAngle) / 180.0);

posX = cirX – (float)(Math.cos(arcAngle)) * radius;

posY = cirY + (float)(Math.sin(arcAngle)) * radius;

}

else if (cirAngle == 180)

{

posX = cirX – radius;

posY = cirY;

}

else if (cirAngle > 180 && cirAngle < 270)

{

arcAngle = (float) (Math.PI * (cirAngle – 180) / 180.0);

posX = cirX – (float)(Math.cos(arcAngle)) * radius;

posY = cirY – (float)(Math.sin(arcAngle)) * radius;

}

else if (cirAngle == 270)

{

posX = cirX;

posY = cirY – radius;

}

else

{

arcAngle = (float) (Math.PI * (360 – cirAngle) / 180.0);

posX = cirX + (float)(Math.cos(arcAngle)) * radius;

posY = cirY – (float)(Math.sin(arcAngle)) * radius;

}

return new PointF(posX, posY);

}

public static PointF calcArcEndPointXY(float cirX, float cirY, float radius, float cirAngle, float orginAngle){

cirAngle = (orginAngle + cirAngle) % 360;

return calcArcEndPointXY(cirX, cirY, radius, cirAngle);

}

}

private OnProgressChangeListener changeListener;

public void setChangeListener(OnProgressChangeListener changeListener) {

this.changeListener = changeListener;

}

public interface OnProgressChangeListener {

void onProgressChange(int duration, int progress);

void onProgressChangeEnd(int duration, int progress);

}

}

發佈留言

發佈留言必須填寫的電子郵件地址不會公開。 必填欄位標示為 *