目录
一、效果图(代码已更新完成)
二、自定义view仪表盘源码获取
https://download.csdn.net/download/shi450561200/87452564
三、代码工具
1、代码工具使用
a.首先在xml布局中引入,如下:
<?xml version="1.0" encoding="utf-8"?>
<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
android:layout_width="match_parent"
android:layout_height="match_parent"
xmlns:app="http://schemas.android.com/apk/res-auto"
android:orientation="vertical">
<RelativeLayout
android:layout_width="match_parent"
android:layout_height="wrap_content">
<LinearLayout
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:gravity="center|top"
android:orientation="horizontal">
<com.xiaoya.myapplication.chart.utils.ViewRtCarDashboard
android:id="@+id/dashboard_view_2"
android:layout_width="@dimen/dp_160"
android:layout_height="wrap_content"
android:layout_marginLeft="@dimen/dp_5"
android:layout_marginTop="@dimen/dp_26" />
<LinearLayout
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_alignEnd="@+id/dashboard_view_2"
android:layout_marginTop="@dimen/dp_80"
android:gravity="center"
android:orientation="vertical">
<TextView
android:id="@+id/dash_tv1"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:text="0V"
android:textColor="@color/color_grey_9"
android:textSize="@dimen/sp_10" />
<ImageView
android:layout_width="@dimen/dp_20"
android:layout_height="@dimen/dp_12"
android:layout_marginTop="@dimen/dp_5"
android:src="@color/circle" />
<TextView
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_marginTop="@dimen/dp_5"
android:text="蓄电池"
android:textColor="@color/text_c"
android:textSize="@dimen/sp_10" />
</LinearLayout>
<com.xiaoya.myapplication.chart.utils.DashboardSpeedViewCar
android:id="@+id/dashboard_view_1"
android:layout_width="@dimen/dp_160"
android:layout_height="wrap_content"
android:layout_marginTop="@dimen/dp_26"
android:layout_marginRight="@dimen/dp_5" />
</LinearLayout>
</RelativeLayout>
<LinearLayout
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:orientation="horizontal">
<RelativeLayout
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:layout_centerHorizontal="true"
android:layout_gravity="center"
android:gravity="center"
android:orientation="horizontal">
<com.xiaoya.myapplication.chart.utils.ViewCarPressDashboard
android:id="@+id/ahead_press"
android:layout_width="wrap_content"
android:layout_height="@dimen/dp_60"
android:layout_alignParentLeft="true"
android:layout_marginTop="@dimen/dp_8" />
<LinearLayout
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_centerHorizontal="true"
android:layout_marginTop="@dimen/dp_20"
android:orientation="vertical">
<TextView
android:id="@+id/tv_tmp_one"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:text="29℃"
android:textColor="@color/color_grey_9"
android:textSize="@dimen/sp_12" />
<ImageView
android:id="@+id/im_tmp_one"
android:layout_width="@dimen/dp_13"
android:layout_height="@dimen/dp_42"
android:layout_gravity="center"
android:layout_marginTop="@dimen/dp_4"
android:layout_marginBottom="@dimen/dp_6"
android:scaleType="fitCenter"
android:src="@color/circle" />
<TextView
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:text="水温1"
android:textColor="@color/color_grey_9"
android:textSize="@dimen/sp_12" />
</LinearLayout>
<com.xiaoya.myapplication.chart.utils.AfterCarPressDashboard
android:id="@+id/after_press"
android:layout_width="wrap_content"
android:layout_height="@dimen/dp_60"
android:layout_alignParentRight="true"
android:layout_marginTop="@dimen/dp_8" />
</RelativeLayout>
</LinearLayout>
<com.xiaoya.myapplication.chart.utils.SocCircleProgress
android:id="@+id/circle_progress_bar1"
android:layout_width="@dimen/dp_80"
android:layout_height="@dimen/dp_80"
android:layout_gravity="center_horizontal"
android:layout_marginTop="@dimen/dp_8"
android:layout_marginRight="@dimen/dp_46"
app:antiAlias="true"
app:arcWidth="5dp"
app:bgArcColor="@color/text_c"
app:bgArcWidth="5dp"
app:hint=""
app:hintSize="14sp"
app:maxValue="100"
app:startAngle="180"
app:sweepAngle="360"
app:unit="soc"
app:unitColor="@color/color_grey_9"
app:unitSize="12sp"
app:value="0"
app:valueSize="@dimen/sp_20" />
</LinearLayout>
b. 在activity 或者fragment中使用即可
package com.xiaoya.myapplication.chart;
import android.os.Bundle;
import android.view.View;
import android.widget.ImageView;
import android.widget.TextView;
import androidx.annotation.Nullable;
import androidx.appcompat.app.AppCompatActivity;
import com.xiaoya.myapplication.R;
import com.xiaoya.myapplication.chart.utils.AfterCarPressDashboard;
import com.xiaoya.myapplication.chart.utils.DashboardSpeedViewCar;
import com.xiaoya.myapplication.chart.utils.SocCircleProgress;
import com.xiaoya.myapplication.chart.utils.ViewCarPressDashboard;
import com.xiaoya.myapplication.chart.utils.ViewRtCarDashboard;
import java.text.DecimalFormat;
public class ChartPage extends AppCompatActivity implements View.OnClickListener {
//车速转速
private DashboardSpeedViewCar dashboardView1;
private TextView dashTv1;
private ViewRtCarDashboard dashboardView2;
//胎压
private ViewCarPressDashboard aheadPress;
private TextView tvTmpOne;
private ImageView imTmpOne;
private AfterCarPressDashboard afterPress;
//圆形进度条
private SocCircleProgress circleProgressBar1;
@Override
protected void onCreate(@Nullable Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.chart_layout);
initView();
}
/**
* 初始化
* 布局
*/
private void initView() {
dashboardView1 = findViewById(R.id.dashboard_view_1);
dashTv1 = findViewById(R.id.dash_tv1);
dashboardView2 = findViewById(R.id.dashboard_view_2);
aheadPress = findViewById(R.id.ahead_press);
tvTmpOne = findViewById(R.id.tv_tmp_one);
imTmpOne = findViewById(R.id.im_tmp_one);
afterPress = findViewById(R.id.after_press);
circleProgressBar1 = findViewById(R.id.circle_progress_bar1);
dashboardView1.setOnClickListener(this);
dashboardView2.setOnClickListener(this);
aheadPress.setOnClickListener(this);
afterPress.setOnClickListener(this);
circleProgressBar1.setOnClickListener(this);
intData();
}
/**
* 初始化
* 数据
*/
private void intData() {
//车速
dashboardView1.setCreditValueWithAnim(90);
//转速
dashboardView2.setCreditValueWithAnim(5);
//胎压
aheadPress.setCreditValueWithAnim(getAirPress(2), 0);
//胎压
afterPress.setCreditValueWithAnim(getAirPress(6), 0);
//soc进度条
circleProgressBar1.setValue(AccValue(50));//soc进度条
}
/**
* 气压值
* 处理
*/
private DecimalFormat df = new DecimalFormat("0.00");
private double getAirPress(double value) {
double y = 0.001;
if (value < 0) {
y = 0;
} else if (value >= 12) {
y = 4.1;//为了使进度条充满,故在最大值基础上加0.1等于4.1
} else if (value >= 0 && value < 12) {
y = value / 3.00;
}
y = Double.parseDouble(df.format(y));
return y;
}
/**
* soc数据校验
*/
private int AccValue(int x) {
int y = 0;
if (x > 100) {
y = 100;
} else if (x <= 0) {
y = 0;
} else if (x >= 0 && x <= 100) {
y = x / 1;
}
return y;
}
@Override
public void onClick(View view) {
}
@Override
protected void onDestroy() {
super.onDestroy();
}
}
2、转速仪表盘代码
package com.xiaoya.myapplication.chart.utils;
import android.animation.Animator;
import android.animation.AnimatorListenerAdapter;
import android.animation.AnimatorSet;
import android.animation.ArgbEvaluator;
import android.animation.ObjectAnimator;
import android.animation.ValueAnimator;
import android.annotation.SuppressLint;
import android.content.Context;
import android.content.res.Resources;
import android.graphics.Canvas;
import android.graphics.Color;
import android.graphics.Matrix;
import android.graphics.Paint;
import android.graphics.Path;
import android.graphics.RadialGradient;
import android.graphics.Rect;
import android.graphics.RectF;
import android.graphics.Shader;
import android.graphics.SweepGradient;
import android.util.AttributeSet;
import android.util.TypedValue;
import android.view.View;
import androidx.core.content.ContextCompat;
import com.xiaoya.myapplication.R;
import java.text.SimpleDateFormat;
public class ViewRtCarDashboard extends View {
private int mRadius; // 画布边缘半径(去除padding后的半径)
private int mStartAngle = 150; // 起始角度
private int mSweepAngle = 240; // 绘制角度
private int mMin = 0; // 最小值
private int mMax = 120; // 最大值
private int mSection = 12; // 值域(mMax-mMin)等分份数
private int mPortion = 3; // 一个mSection等分份数
private String mHeaderText = ""; // 表头
private int mCreditValue = 0; // 速度
private int mSolidCreditValue = mCreditValue; // 速度值(设定后不变)
private double speed = 0;
private int mSparkleWidth; // 亮点宽度
private int mProgressWidth; // 进度圆弧宽度
private float mLength1; // 刻度顶部相对边缘的长度
private int mCalibrationWidth; // 刻度圆弧宽度
private float mLength2; // 刻度读数顶部相对边缘的长度
private int mPadding;
private float mCenterX, mCenterY; // 圆心坐标
private Paint mPaint;
private RectF mRectFProgressArc;
private RectF mRectFCalibrationFArc;
private RectF mRectFTextArc;
private Path mPath;
private Rect mRectText;
private String[] mTexts;
private int mBackgroundColor;
private int[] mBgColors;
/**
* 由于真实的仪表盘界面速度值不是线性排布,所以播放动画时若以值为参考,则会出现忽慢忽快
* 的情况(开始以为是卡顿)。因此,先计算出最终到达角度,以扫过的角度为线性参考,动画就流畅了
*/
private boolean isAnimFinish = true;
private float mAngleWhenAnim;
public ViewRtCarDashboard(Context context) {
this(context, null);
}
public ViewRtCarDashboard(Context context, AttributeSet attrs) {
this(context, attrs, 0);
}
public ViewRtCarDashboard(Context context, AttributeSet attrs, int defStyleAttr) {
super(context, attrs, defStyleAttr);
init();
}
private void init() {
mSparkleWidth = dp2px(5);
mProgressWidth = dp2px(3);
mCalibrationWidth = dp2px(1);
mPaint = new Paint();
mPaint.setAntiAlias(true);
mPaint.setStrokeCap(Paint.Cap.ROUND);
mRectFProgressArc = new RectF();
mRectFCalibrationFArc = new RectF();
mRectFTextArc = new RectF();
mPath = new Path();
mRectText = new Rect();
mTexts = new String[]{"0", "1", "2", "3", "4", "5", "6", "7", "8", "9", "10", "11", "12"};
mBgColors = new int[]{ContextCompat.getColor(getContext(), R.color.transparent_bd)};
mBackgroundColor = mBgColors[0];
}
@Override
protected void onMeasure(int widthMeasureSpec, int heightMeasureSpec) {
super.onMeasure(widthMeasureSpec, heightMeasureSpec);
mPadding = Math.max(
Math.max(getPaddingLeft(), getPaddingTop()),
Math.max(getPaddingRight(), getPaddingBottom())
);
setPadding(mPadding, mPadding, mPadding, mPadding);
mLength1 = mPadding + mSparkleWidth / 2f + dp2px(8);
mLength2 = mLength1 + mCalibrationWidth + dp2px(1) + dp2px(5);
int width = resolveSize(dp2px(200), widthMeasureSpec);
mRadius = (width - mPadding * 2) / 2;
setMeasuredDimension(width, width - dp2px(30));
mCenterX = mCenterY = getMeasuredWidth() / 2f;
mRectFProgressArc.set(
mPadding + mSparkleWidth / 2f,
mPadding + mSparkleWidth / 2f,
getMeasuredWidth() - mPadding - mSparkleWidth / 2f,
getMeasuredWidth() - mPadding - mSparkleWidth / 2f
);
mRectFCalibrationFArc.set(
mLength1 + mCalibrationWidth / 2f,
mLength1 + mCalibrationWidth / 2f,
getMeasuredWidth() - mLength1 - mCalibrationWidth / 2f,
getMeasuredWidth() - mLength1 - mCalibrationWidth / 2f
);
mPaint.setTextSize(sp2px(10));
mPaint.getTextBounds("0", 0, "0".length(), mRectText);
mRectFTextArc.set(
mLength2 + mRectText.height(),
mLength2 + mRectText.height(),
getMeasuredWidth() - mLength2 - mRectText.height(),
getMeasuredWidth() - mLength2 - mRectText.height()
);
}
@Override
protected void onDraw(Canvas canvas) {
super.onDraw(canvas);
canvas.drawColor(mBackgroundColor);
/**
* 画进度圆弧背景
*/
// mPaint.setStrokeCap(Paint.Cap.ROUND);
mPaint.setStyle(Paint.Style.STROKE);
mPaint.setStrokeWidth(mProgressWidth);
mPaint.setColor(Color.parseColor("#ffCCCCCC"));
// mPaint.setAlpha(80);
canvas.drawArc(mRectFProgressArc, mStartAngle + 1, mSweepAngle - 2, false, mPaint);
// mPaint.setAlpha(255);
if (isAnimFinish) {
/**
* 画进度圆弧(起始到速度值)
*/
mPaint.setShader(generateSweepGradient());
mPaint.setColor(Color.parseColor("#ff333333"));
canvas.drawArc(mRectFProgressArc, mStartAngle + 1,
calculateRelativeAngleWithValue(mCreditValue) - 2, false, mPaint);
/**
* 画进度圆弧值指示亮点
*/
float[] point = getCoordinatePoint(
mRadius - mSparkleWidth / 2f,
mStartAngle + calculateRelativeAngleWithValue(mCreditValue)
);
mPaint.setStyle(Paint.Style.FILL);
mPaint.setColor(Color.parseColor("#ff333333"));
mPaint.setShader(generateRadialGradient(point[0], point[1]));
canvas.drawCircle(point[0], point[1], mSparkleWidth / 2f, mPaint);
} else {
/**
* 画进度圆弧(起始到速度值)
*/
mPaint.setShader(generateSweepGradient());
mPaint.setColor(Color.parseColor("#ff333333"));
canvas.drawArc(mRectFProgressArc, mStartAngle + 1,
mAngleWhenAnim - mStartAngle - 2, false, mPaint);
/**
* 画进度圆弧值指示亮点
*/
float[] point = getCoordinatePoint(
mRadius - mSparkleWidth / 2f,
mAngleWhenAnim
);
mPaint.setColor(Color.parseColor("#ff333333"));
mPaint.setStyle(Paint.Style.FILL);
mPaint.setShader(generateRadialGradient(point[0], point[1]));
canvas.drawCircle(point[0], point[1], mSparkleWidth / 2f, mPaint);
}
/**
* 画刻度圆弧
*/
mPaint.setShader(null);
mPaint.setStyle(Paint.Style.STROKE);
mPaint.setColor(Color.WHITE);
mPaint.setColor(Color.parseColor("#ff333333"));
// mPaint.setAlpha(80);
mPaint.setStrokeCap(Paint.Cap.SQUARE);
mPaint.setStrokeWidth(mCalibrationWidth);
canvas.drawArc(mRectFCalibrationFArc, 150, 240, false, mPaint);
/**
* 画长刻度
* 画好起始角度的一条刻度后通过canvas绕着原点旋转来画剩下的长刻度
*/
mPaint.setStrokeCap(Paint.Cap.ROUND);
mPaint.setStrokeWidth(dp2px(2));
mPaint.setColor(Color.parseColor("#ff333333"));
// mPaint.setAlpha(120);
float x0 = mCenterX;
float y0 = mPadding + mLength1 + dp2px(2);//控制刻度长短
float x1 = mCenterX;
float y1 = y0 + mCalibrationWidth;
// 逆时针到开始处
canvas.save();
canvas.drawLine(x0, y0, x1, y1, mPaint);
float degree = mSweepAngle / mSection;
for (int i = 0; i < mSection / 2; i++) {
canvas.rotate(-degree, mCenterX, mCenterY);
canvas.drawLine(x0, y0, x1, y1, mPaint);
}
canvas.restore();
// 顺时针到结尾处
canvas.save();
for (int i = 0; i < mSection / 2; i++) {
canvas.rotate(degree, mCenterX, mCenterY);
canvas.drawLine(x0, y0, x1, y1, mPaint);
}
canvas.restore();
/**
* 画短刻度
* 同样采用canvas的旋转原理
*/
mPaint.setStrokeWidth(dp2px(1));
mPaint.setAlpha(0);
float x2 = mCenterX;
float y2 = y0 + mCalibrationWidth - dp2px(4);//控制刻度长短
// 逆时针到开始处
canvas.save();
canvas.drawLine(x0, y0, x2, y2, mPaint);
degree = mSweepAngle / (mSection * mPortion);
for (int i = 0; i < (mSection * mPortion) / 2; i++) {
canvas.rotate(-degree, mCenterX, mCenterY);
canvas.drawLine(x0, y0, x2, y2, mPaint);
}
canvas.restore();
// 顺时针到结尾处
canvas.save();
for (int i = 0; i < (mSection * mPortion) / 2; i++) {
canvas.rotate(degree, mCenterX, mCenterY);
canvas.drawLine(x0, y0, x2, y2, mPaint);
}
canvas.restore();
/**
* 画长刻度读数
* 添加一个圆弧path,文字沿着path绘制
*/
mPaint.setTextSize(sp2px(8));
mPaint.setTextAlign(Paint.Align.LEFT);
mPaint.setStyle(Paint.Style.FILL);
mPaint.setColor(Color.parseColor("#ff007AFF"));
// mPaint.setAlpha(255);
for (int i = 0; i < mTexts.length; i++) {
mPaint.getTextBounds(mTexts[i], 0, mTexts[i].length(), mRectText);
// 粗略把文字的宽度视为圆心角2*θ对应的弧长,利用弧长公式得到θ,下面用于修正角度
float θ = (float) (180 * mRectText.width() / 2 /
(Math.PI * (mRadius - mLength2 - mRectText.height())));
mPath.reset();
mPath.addArc(
mRectFTextArc,
mStartAngle + i * (mSweepAngle / mSection) - θ, // 正起始角度减去θ使文字居中对准长刻度
mSweepAngle
);
canvas.drawTextOnPath(mTexts[i], mPath, 0, 0, mPaint);
}
/**
* 画实时度数值
*/
mPaint.setAlpha(255);
mPaint.setTextSize(sp2px(18));
mPaint.setTextAlign(Paint.Align.CENTER);
mPaint.setColor(Color.parseColor("#ff333333"));
String value = String.valueOf(speed);
canvas.drawText(value, mCenterX, mCenterY - dp2px(10), mPaint);
/**
* 画车速数单位
*/
mPaint.setAlpha(255);
mPaint.setTextSize(sp2px(12));
mPaint.setColor(Color.parseColor("#ff999999"));
canvas.drawText(calculateCreditDescription(), mCenterX, mCenterY + dp2px(10), mPaint);
/**
* 画车速描述
*/
mPaint.setAlpha(160);
mPaint.setTextSize(sp2px(12));
mPaint.setColor(Color.parseColor("#ff666666"));
canvas.drawText(getFormatTimeStr(), mCenterX, mCenterY + dp2px(30), mPaint);
mPaint.setColor(Color.parseColor("#00ffffff"));
}
private int dp2px(int dp) {
return (int) TypedValue.applyDimension(TypedValue.COMPLEX_UNIT_DIP, dp,
Resources.getSystem().getDisplayMetrics());
}
private int sp2px(int sp) {
return (int) TypedValue.applyDimension(TypedValue.COMPLEX_UNIT_SP, sp,
Resources.getSystem().getDisplayMetrics());
}
private SweepGradient generateSweepGradient() {
SweepGradient sweepGradient = new SweepGradient(mCenterX, mCenterY,
new int[]{Color.argb(200, 0, 122, 255), Color.argb(200, 0, 122, 255)},
new float[]{0, calculateRelativeAngleWithValue(mCreditValue) / 360}
);
Matrix matrix = new Matrix();
matrix.setRotate(mStartAngle - 1, mCenterX, mCenterY);
sweepGradient.setLocalMatrix(matrix);
return sweepGradient;
}
private RadialGradient generateRadialGradient(float x, float y) {
return new RadialGradient(x, y, mSparkleWidth / 2f,
new int[]{Color.argb(255, 255, 255, 255), Color.argb(80, 255, 255, 255)},
new float[]{0.4f, 1},
Shader.TileMode.CLAMP
);
}
private float[] getCoordinatePoint(float radius, float angle) {
float[] point = new float[2];
double arcAngle = Math.toRadians(angle); //将角度转换为弧度
if (angle < 90) {
point[0] = (float) (mCenterX + Math.cos(arcAngle) * radius);
point[1] = (float) (mCenterY + Math.sin(arcAngle) * radius);
} else if (angle == 90) {
point[0] = mCenterX;
point[1] = mCenterY + radius;
} else if (angle > 90 && angle < 180) {
arcAngle = Math.PI * (180 - angle) / 180.0;
point[0] = (float) (mCenterX - Math.cos(arcAngle) * radius);
point[1] = (float) (mCenterY + Math.sin(arcAngle) * radius);
} else if (angle == 180) {
point[0] = mCenterX - radius;
point[1] = mCenterY;
} else if (angle > 180 && angle < 270) {
arcAngle = Math.PI * (angle - 180) / 180.0;
point[0] = (float) (mCenterX - Math.cos(arcAngle) * radius);
point[1] = (float) (mCenterY - Math.sin(arcAngle) * radius);
} else if (angle == 270) {
point[0] = mCenterX;
point[1] = mCenterY - radius;
} else {
arcAngle = Math.PI * (360 - angle) / 180.0;
point[0] = (float) (mCenterX + Math.cos(arcAngle) * radius);
point[1] = (float) (mCenterY - Math.sin(arcAngle) * radius);
}
return point;
}
/**
* 相对起始角度计算速度所对应的角度大小
*/
private float calculateRelativeAngleWithValue(int value) {
float degreePerSection = 1f * mSweepAngle / mSection;
return value * 2;
}
/**
* 车速单位
*/
private String calculateCreditDescription() {
return "x1000/min";
}
private SimpleDateFormat mDateFormat;
private String getFormatTimeStr() {
return "转速";
}
public int getCreditValue() {
return mCreditValue;
}
/**
* 设置车速
*
* @param creditValue
*/
public void setCreditValue(double creditValue) {
if (mSolidCreditValue == creditValue || creditValue < mMin || creditValue > mMax) {
return;
}
speed = creditValue;
mSolidCreditValue = (int) creditValue;
mCreditValue = (int) creditValue;
postInvalidate();
}
/**
* 设置速度值并播放动画
*
* @param creditValue 速度值
*/
public void setCreditValueWithAnim(double creditValue) {
if (creditValue < mMin || creditValue > mMax || !isAnimFinish) {
return;
}
speed = creditValue;
mSolidCreditValue = (int) (creditValue);
int data=(int) (creditValue*10);
ValueAnimator creditValueAnimator = ValueAnimator.ofInt(0, data);
creditValueAnimator.addUpdateListener(new ValueAnimator.AnimatorUpdateListener() {
@Override
public void onAnimationUpdate(ValueAnimator animation) {
mCreditValue = (int) animation.getAnimatedValue();
postInvalidate();
}
});
// 计算最终值对应的角度,以扫过的角度的线性变化来播放动画
float degree = calculateRelativeAngleWithValue(data);
ValueAnimator degreeValueAnimator = ValueAnimator.ofFloat(mStartAngle, mStartAngle + degree);
degreeValueAnimator.addUpdateListener(new ValueAnimator.AnimatorUpdateListener() {
@Override
public void onAnimationUpdate(ValueAnimator animation) {
mAngleWhenAnim = (float) animation.getAnimatedValue();
}
});
@SuppressLint("ObjectAnimatorBinding") ObjectAnimator colorAnimator = ObjectAnimator.ofInt(this, "mBackgroundColor", mBgColors[0], mBgColors[0]);
long delay = 1000;
colorAnimator.setEvaluator(new ArgbEvaluator());
colorAnimator.addUpdateListener(new ValueAnimator.AnimatorUpdateListener() {
@Override
public void onAnimationUpdate(ValueAnimator animation) {
mBackgroundColor = (int) animation.getAnimatedValue();
}
});
AnimatorSet animatorSet = new AnimatorSet();
animatorSet
.setDuration(delay)
.playTogether(creditValueAnimator, degreeValueAnimator, colorAnimator);
animatorSet.addListener(new AnimatorListenerAdapter() {
@Override
public void onAnimationStart(Animator animation) {
super.onAnimationStart(animation);
isAnimFinish = false;
}
@Override
public void onAnimationEnd(Animator animation) {
super.onAnimationEnd(animation);
isAnimFinish = true;
}
@Override
public void onAnimationCancel(Animator animation) {
super.onAnimationCancel(animation);
isAnimFinish = true;
}
});
animatorSet.start();
}
}
3、车速仪表盘代码
package com.xiaoya.myapplication.chart.utils;
import android.animation.Animator;
import android.animation.AnimatorListenerAdapter;
import android.animation.AnimatorSet;
import android.animation.ArgbEvaluator;
import android.animation.ObjectAnimator;
import android.animation.ValueAnimator;
import android.annotation.SuppressLint;
import android.content.Context;
import android.content.res.Resources;
import android.graphics.Canvas;
import android.graphics.Color;
import android.graphics.Matrix;
import android.graphics.Paint;
import android.graphics.Path;
import android.graphics.RadialGradient;
import android.graphics.Rect;
import android.graphics.RectF;
import android.graphics.Shader;
import android.graphics.SweepGradient;
import android.util.AttributeSet;
import android.util.Log;
import android.util.TypedValue;
import android.view.View;
import androidx.core.content.ContextCompat;
import com.xiaoya.myapplication.R;
import java.text.SimpleDateFormat;
public class DashboardSpeedViewCar extends View {
private int mRadius; // 画布边缘半径(去除padding后的半径)
private int mStartAngle = 150; // 起始角度
private int mSweepAngle = 240; // 绘制角度
private int mMin = 0; // 最小值
private int mMax = 120; // 最大值
private int mSection = 12; // 值域(mMax-mMin)等分份数
private int mPortion = 3; // 一个mSection等分份数
private String mHeaderText = ""; // 表头
private int mCreditValue = 0; // 速度
private int mSolidCreditValue = mCreditValue; // 速度值(设定后不变)
private double speed = 0;
private int mSparkleWidth; // 亮点宽度
private int mProgressWidth; // 进度圆弧宽度
private float mLength1; // 刻度顶部相对边缘的长度
private int mCalibrationWidth; // 刻度圆弧宽度
private float mLength2; // 刻度读数顶部相对边缘的长度
private int mPadding;
private float mCenterX, mCenterY; // 圆心坐标
private Paint mPaint;
private RectF mRectFProgressArc;
private RectF mRectFCalibrationFArc;
private RectF mRectFTextArc;
private Path mPath;
private Rect mRectText;
private String[] mTexts;
private int mBackgroundColor;
private int[] mBgColors;
/**
* 由于真实的仪表盘界面速度值不是线性排布,所以播放动画时若以值为参考,则会出现忽慢忽快
* 的情况(开始以为是卡顿)。因此,先计算出最终到达角度,以扫过的角度为线性参考,动画就流畅了
*/
private boolean isAnimFinish = true;
private float mAngleWhenAnim;
public DashboardSpeedViewCar(Context context) {
this(context, null);
}
public DashboardSpeedViewCar(Context context, AttributeSet attrs) {
this(context, attrs, 0);
}
public DashboardSpeedViewCar(Context context, AttributeSet attrs, int defStyleAttr) {
super(context, attrs, defStyleAttr);
init();
}
private void init() {
mSparkleWidth = dp2px(5);
mProgressWidth = dp2px(3);
mCalibrationWidth = dp2px(1);
mPaint = new Paint();
mPaint.setAntiAlias(true);
mPaint.setStrokeCap(Paint.Cap.ROUND);
mRectFProgressArc = new RectF();
mRectFCalibrationFArc = new RectF();
mRectFTextArc = new RectF();
mPath = new Path();
mRectText = new Rect();
mTexts = new String[]{"0", "10", "20", "30", "40", "50", "60", "70", "80", "90", "100", "110", "120"};
mBgColors = new int[]{ContextCompat.getColor(getContext(), R.color.transparent_bd)};
mBackgroundColor = mBgColors[0];
}
@Override
protected void onMeasure(int widthMeasureSpec, int heightMeasureSpec) {
super.onMeasure(widthMeasureSpec, heightMeasureSpec);
mPadding = Math.max(
Math.max(getPaddingLeft(), getPaddingTop()),
Math.max(getPaddingRight(), getPaddingBottom())
);
setPadding(mPadding, mPadding, mPadding, mPadding);
mLength1 = mPadding + mSparkleWidth / 2f + dp2px(8);
mLength2 = mLength1 + mCalibrationWidth + dp2px(1) + dp2px(5);
int width = resolveSize(dp2px(200), widthMeasureSpec);
mRadius = (width - mPadding * 2) / 2;
setMeasuredDimension(width, width - dp2px(30));
mCenterX = mCenterY = getMeasuredWidth() / 2f;
mRectFProgressArc.set(
mPadding + mSparkleWidth / 2f,
mPadding + mSparkleWidth / 2f,
getMeasuredWidth() - mPadding - mSparkleWidth / 2f,
getMeasuredWidth() - mPadding - mSparkleWidth / 2f
);
mRectFCalibrationFArc.set(
mLength1 + mCalibrationWidth / 2f,
mLength1 + mCalibrationWidth / 2f,
getMeasuredWidth() - mLength1 - mCalibrationWidth / 2f,
getMeasuredWidth() - mLength1 - mCalibrationWidth / 2f
);
mPaint.setTextSize(sp2px(10));
mPaint.getTextBounds("0", 0, "0".length(), mRectText);
mRectFTextArc.set(
mLength2 + mRectText.height(),
mLength2 + mRectText.height(),
getMeasuredWidth() - mLength2 - mRectText.height(),
getMeasuredWidth() - mLength2 - mRectText.height()
);
}
@Override
protected void onDraw(Canvas canvas) {
super.onDraw(canvas);
canvas.drawColor(mBackgroundColor);
/**
* 画进度圆弧背景
*/
// mPaint.setStrokeCap(Paint.Cap.ROUND);
mPaint.setStyle(Paint.Style.STROKE);
mPaint.setStrokeWidth(mProgressWidth);
mPaint.setColor(Color.parseColor("#ffCCCCCC"));
// mPaint.setAlpha(80);
canvas.drawArc(mRectFProgressArc, mStartAngle + 1, mSweepAngle - 2, false, mPaint);
// mPaint.setAlpha(255);
if (isAnimFinish) {
/**
* 画进度圆弧(起始到速度值)
*/
mPaint.setShader(generateSweepGradient());
mPaint.setColor(Color.parseColor("#ff333333"));
canvas.drawArc(mRectFProgressArc, mStartAngle + 1,
calculateRelativeAngleWithValue(mCreditValue) - 2, false, mPaint);
/**
* 画进度圆弧值指示亮点
*/
float[] point = getCoordinatePoint(
mRadius - mSparkleWidth / 2f,
mStartAngle + calculateRelativeAngleWithValue(mCreditValue)
);
mPaint.setStyle(Paint.Style.FILL);
mPaint.setColor(Color.parseColor("#ff333333"));
mPaint.setShader(generateRadialGradient(point[0], point[1]));
canvas.drawCircle(point[0], point[1], mSparkleWidth / 2f, mPaint);
} else {
/**
* 画进度圆弧(起始到速度值)
*/
mPaint.setShader(generateSweepGradient());
mPaint.setColor(Color.parseColor("#ff333333"));
canvas.drawArc(mRectFProgressArc, mStartAngle + 1,
mAngleWhenAnim - mStartAngle - 2, false, mPaint);
/**
* 画进度圆弧值指示亮点
*/
float[] point = getCoordinatePoint(
mRadius - mSparkleWidth / 2f,
mAngleWhenAnim
);
mPaint.setColor(Color.parseColor("#ff333333"));
mPaint.setStyle(Paint.Style.FILL);
mPaint.setShader(generateRadialGradient(point[0], point[1]));
canvas.drawCircle(point[0], point[1], mSparkleWidth / 2f, mPaint);
}
/**
* 画刻度圆弧
*/
mPaint.setShader(null);
mPaint.setStyle(Paint.Style.STROKE);
mPaint.setColor(Color.WHITE);
mPaint.setColor(Color.parseColor("#ff333333"));
// mPaint.setAlpha(80);
mPaint.setStrokeCap(Paint.Cap.SQUARE);
mPaint.setStrokeWidth(mCalibrationWidth);
canvas.drawArc(mRectFCalibrationFArc, 150, 240, false, mPaint);
/**
* 画长刻度
* 画好起始角度的一条刻度后通过canvas绕着原点旋转来画剩下的长刻度
*/
mPaint.setStrokeCap(Paint.Cap.ROUND);
mPaint.setStrokeWidth(dp2px(2));
mPaint.setColor(Color.parseColor("#ff333333"));
// mPaint.setAlpha(120);
float x0 = mCenterX;
float y0 = mPadding + mLength1 + dp2px(2);//控制刻度长短
float x1 = mCenterX;
float y1 = y0 + mCalibrationWidth;
// 逆时针到开始处
canvas.save();
canvas.drawLine(x0, y0, x1, y1, mPaint);
float degree = mSweepAngle / mSection;
for (int i = 0; i < mSection / 2; i++) {
canvas.rotate(-degree, mCenterX, mCenterY);
canvas.drawLine(x0, y0, x1, y1, mPaint);
}
canvas.restore();
// 顺时针到结尾处
canvas.save();
for (int i = 0; i < mSection / 2; i++) {
canvas.rotate(degree, mCenterX, mCenterY);
canvas.drawLine(x0, y0, x1, y1, mPaint);
}
canvas.restore();
/**
* 画短刻度
* 同样采用canvas的旋转原理
*/
mPaint.setStrokeWidth(dp2px(1));
mPaint.setAlpha(0);
float x2 = mCenterX;
float y2 = y0 + mCalibrationWidth - dp2px(4);//控制刻度长短
// 逆时针到开始处
canvas.save();
canvas.drawLine(x0, y0, x2, y2, mPaint);
degree = mSweepAngle / (mSection * mPortion);
for (int i = 0; i < (mSection * mPortion) / 2; i++) {
canvas.rotate(-degree, mCenterX, mCenterY);
canvas.drawLine(x0, y0, x2, y2, mPaint);
}
canvas.restore();
// 顺时针到结尾处
canvas.save();
for (int i = 0; i < (mSection * mPortion) / 2; i++) {
canvas.rotate(degree, mCenterX, mCenterY);
canvas.drawLine(x0, y0, x2, y2, mPaint);
}
canvas.restore();
/**
* 画长刻度读数
* 添加一个圆弧path,文字沿着path绘制
*/
mPaint.setTextSize(sp2px(8));
mPaint.setTextAlign(Paint.Align.LEFT);
mPaint.setStyle(Paint.Style.FILL);
mPaint.setColor(Color.parseColor("#ff007AFF"));
// mPaint.setAlpha(255);
for (int i = 0; i < mTexts.length; i++) {
mPaint.getTextBounds(mTexts[i], 0, mTexts[i].length(), mRectText);
// 粗略把文字的宽度视为圆心角2*θ对应的弧长,利用弧长公式得到θ,下面用于修正角度
float θ = (float) (180 * mRectText.width() / 2 /
(Math.PI * (mRadius - mLength2 - mRectText.height())));
mPath.reset();
mPath.addArc(
mRectFTextArc,
mStartAngle + i * (mSweepAngle / mSection) - θ, // 正起始角度减去θ使文字居中对准长刻度
mSweepAngle
);
canvas.drawTextOnPath(mTexts[i], mPath, 0, 0, mPaint);
}
/**
* 画实时度数值
*/
mPaint.setAlpha(255);
mPaint.setTextSize(sp2px(18));
mPaint.setTextAlign(Paint.Align.CENTER);
mPaint.setColor(Color.parseColor("#ff333333"));
String value = String.valueOf(speed);
canvas.drawText(value, mCenterX, mCenterY - dp2px(10), mPaint);
/**
* 画车速数单位
*/
mPaint.setAlpha(255);
mPaint.setTextSize(sp2px(12));
mPaint.setColor(Color.parseColor("#ff999999"));
canvas.drawText(calculateCreditDescription(), mCenterX, mCenterY+ dp2px(10), mPaint);
/**
* 画车速描述
*/
mPaint.setAlpha(160);
mPaint.setTextSize(sp2px(12));
mPaint.setColor(Color.parseColor("#ff666666"));
canvas.drawText(getFormatTimeStr(), mCenterX, mCenterY + dp2px(30), mPaint);
mPaint.setColor(Color.parseColor("#00ffffff"));
}
private int dp2px(int dp) {
return (int) TypedValue.applyDimension(TypedValue.COMPLEX_UNIT_DIP, dp,
Resources.getSystem().getDisplayMetrics());
}
private int sp2px(int sp) {
return (int) TypedValue.applyDimension(TypedValue.COMPLEX_UNIT_SP, sp,
Resources.getSystem().getDisplayMetrics());
}
private SweepGradient generateSweepGradient() {
SweepGradient sweepGradient = new SweepGradient(mCenterX, mCenterY,
new int[]{Color.argb(200, 0, 122, 255), Color.argb(200, 0, 122, 255)},
new float[]{0, calculateRelativeAngleWithValue(mCreditValue) / 360}
);
Matrix matrix = new Matrix();
matrix.setRotate(mStartAngle - 1, mCenterX, mCenterY);
sweepGradient.setLocalMatrix(matrix);
return sweepGradient;
}
private RadialGradient generateRadialGradient(float x, float y) {
return new RadialGradient(x, y, mSparkleWidth / 2f,
new int[]{Color.argb(255, 255, 255, 255), Color.argb(80, 255, 255, 255)},
new float[]{0.4f, 1},
Shader.TileMode.CLAMP
);
}
private float[] getCoordinatePoint(float radius, float angle) {
float[] point = new float[2];
double arcAngle = Math.toRadians(angle); //将角度转换为弧度
if (angle < 90) {
point[0] = (float) (mCenterX + Math.cos(arcAngle) * radius);
point[1] = (float) (mCenterY + Math.sin(arcAngle) * radius);
} else if (angle == 90) {
point[0] = mCenterX;
point[1] = mCenterY + radius;
} else if (angle > 90 && angle < 180) {
arcAngle = Math.PI * (180 - angle) / 180.0;
point[0] = (float) (mCenterX - Math.cos(arcAngle) * radius);
point[1] = (float) (mCenterY + Math.sin(arcAngle) * radius);
} else if (angle == 180) {
point[0] = mCenterX - radius;
point[1] = mCenterY;
} else if (angle > 180 && angle < 270) {
arcAngle = Math.PI * (angle - 180) / 180.0;
point[0] = (float) (mCenterX - Math.cos(arcAngle) * radius);
point[1] = (float) (mCenterY - Math.sin(arcAngle) * radius);
} else if (angle == 270) {
point[0] = mCenterX;
point[1] = mCenterY - radius;
} else {
arcAngle = Math.PI * (360 - angle) / 180.0;
point[0] = (float) (mCenterX + Math.cos(arcAngle) * radius);
point[1] = (float) (mCenterY - Math.sin(arcAngle) * radius);
}
return point;
}
/**
* 相对起始角度计算速度所对应的角度大小
*/
private float calculateRelativeAngleWithValue(int value) {
float degreePerSection = 1f * mSweepAngle / mSection;
return value * 2;
}
/**
* 车速单位
*/
private String calculateCreditDescription() {
return "km/h";
}
private SimpleDateFormat mDateFormat;
private String getFormatTimeStr() {
return "车速";
}
public int getCreditValue() {
return mCreditValue;
}
/**
* 设置车速
*
* @param creditValue
*/
public void setCreditValue(double creditValue) {
if (mSolidCreditValue == creditValue || creditValue < mMin || creditValue > mMax) {
return;
}
speed = creditValue;
mSolidCreditValue = (int) creditValue;
mCreditValue = (int) creditValue;
postInvalidate();
}
/**
* 设置速度值并播放动画
*
* @param creditValue 速度值
*/
public void setCreditValueWithAnim(double creditValue) {
Log.d("TAG", "setCreditValueWithAnim: 11====="+mMin);
Log.d("TAG", "setCreditValueWithAnim: 22====="+mMax);
if (creditValue < mMin || creditValue > mMax || !isAnimFinish) {
return;
}
speed = creditValue;
mSolidCreditValue = (int) creditValue;
ValueAnimator creditValueAnimator = ValueAnimator.ofInt(0, mSolidCreditValue);
creditValueAnimator.addUpdateListener(new ValueAnimator.AnimatorUpdateListener() {
@Override
public void onAnimationUpdate(ValueAnimator animation) {
mCreditValue = (int) animation.getAnimatedValue();
postInvalidate();
}
});
// 计算最终值对应的角度,以扫过的角度的线性变化来播放动画
float degree = calculateRelativeAngleWithValue(mSolidCreditValue);
ValueAnimator degreeValueAnimator = ValueAnimator.ofFloat(mStartAngle, mStartAngle + degree);
degreeValueAnimator.addUpdateListener(new ValueAnimator.AnimatorUpdateListener() {
@Override
public void onAnimationUpdate(ValueAnimator animation) {
mAngleWhenAnim = (float) animation.getAnimatedValue();
}
});
@SuppressLint("ObjectAnimatorBinding") ObjectAnimator colorAnimator = ObjectAnimator.ofInt(this, "mBackgroundColor", mBgColors[0], mBgColors[0]);
long delay = 1000;
colorAnimator.setEvaluator(new ArgbEvaluator());
colorAnimator.addUpdateListener(new ValueAnimator.AnimatorUpdateListener() {
@Override
public void onAnimationUpdate(ValueAnimator animation) {
mBackgroundColor = (int) animation.getAnimatedValue();
}
});
AnimatorSet animatorSet = new AnimatorSet();
animatorSet
.setDuration(delay)
.playTogether(creditValueAnimator, degreeValueAnimator, colorAnimator);
animatorSet.addListener(new AnimatorListenerAdapter() {
@Override
public void onAnimationStart(Animator animation) {
super.onAnimationStart(animation);
isAnimFinish = false;
}
@Override
public void onAnimationEnd(Animator animation) {
super.onAnimationEnd(animation);
isAnimFinish = true;
}
@Override
public void onAnimationCancel(Animator animation) {
super.onAnimationCancel(animation);
isAnimFinish = true;
}
});
animatorSet.start();
}
}
4、胎压仪表盘
package com.xiaoya.myapplication.chart.utils;
import android.animation.Animator;
import android.animation.AnimatorListenerAdapter;
import android.animation.AnimatorSet;
import android.animation.ArgbEvaluator;
import android.animation.ObjectAnimator;
import android.animation.ValueAnimator;
import android.annotation.SuppressLint;
import android.content.Context;
import android.content.res.Resources;
import android.graphics.Canvas;
import android.graphics.Color;
import android.graphics.Matrix;
import android.graphics.Paint;
import android.graphics.Path;
import android.graphics.RadialGradient;
import android.graphics.Rect;
import android.graphics.RectF;
import android.graphics.Shader;
import android.graphics.SweepGradient;
import android.util.AttributeSet;
import android.util.TypedValue;
import android.view.View;
import androidx.core.content.ContextCompat;
import com.xiaoya.myapplication.R;
import java.text.SimpleDateFormat;
public class ViewCarPressDashboard extends View {
private int mRadius; // 画布边缘半径(去除padding后的半径)
private int mStartAngle = 150; // 起始角度
private int mSweepAngle = 240; // 绘制角度
private int mMin = 0; // 最小值
private int mMax = 120; // 最大值
private int mSection = 12; // 值域(mMax-mMin)等分份数
private int mPortion = 3; // 一个mSection等分份数
private String mHeaderText = ""; // 表头
private int mCreditValue = 0; // 速度
private int mSolidCreditValue = mCreditValue; // 速度值(设定后不变)
private double speed = 0;
private int mSparkleWidth; // 亮点宽度
private int mProgressWidth; // 进度圆弧宽度
private float mLength1; // 刻度顶部相对边缘的长度
private int mCalibrationWidth; // 刻度圆弧宽度
private float mLength2; // 刻度读数顶部相对边缘的长度
private int mPadding;
private float mCenterX, mCenterY; // 圆心坐标
private Paint mPaint;
private RectF mRectFProgressArc;
private RectF mRectFCalibrationFArc;
private RectF mRectFTextArc;
private Path mPath;
private Rect mRectText;
private String[] mTexts;
private int mBackgroundColor;
private int[] mBgColors;
/**
* 由于真实的仪表盘界面速度值不是线性排布,所以播放动画时若以值为参考,则会出现忽慢忽快
* 的情况(开始以为是卡顿)。因此,先计算出最终到达角度,以扫过的角度为线性参考,动画就流畅了
*/
private boolean isAnimFinish = true;
private float mAngleWhenAnim;
public ViewCarPressDashboard(Context context) {
this(context, null);
}
public ViewCarPressDashboard(Context context, AttributeSet attrs) {
this(context, attrs, 0);
}
public ViewCarPressDashboard(Context context, AttributeSet attrs, int defStyleAttr) {
super(context, attrs, defStyleAttr);
init();
}
private void init() {
mSparkleWidth = dp2px(5);
mProgressWidth = dp2px(3);
mCalibrationWidth = dp2px(1);
mPaint = new Paint();
mPaint.setAntiAlias(true);
mPaint.setStrokeCap(Paint.Cap.ROUND);
mRectFProgressArc = new RectF();
mRectFCalibrationFArc = new RectF();
mRectFTextArc = new RectF();
mPath = new Path();
mRectText = new Rect();
mTexts = new String[]{"", "", "", "", "0", "", "6", "", "12", "", "", "", ""};
mBgColors = new int[]{ContextCompat.getColor(getContext(), R.color.transparent_bd)};
mBackgroundColor = mBgColors[0];
}
@Override
protected void onMeasure(int widthMeasureSpec, int heightMeasureSpec) {
super.onMeasure(widthMeasureSpec, heightMeasureSpec);
mPadding = Math.max(
Math.max(getPaddingLeft(), getPaddingTop()),
Math.max(getPaddingRight(), getPaddingBottom())
);
setPadding(mPadding, mPadding, mPadding, mPadding);
mLength1 = mPadding + mSparkleWidth / 2f + dp2px(8);
mLength2 = mLength1 + mCalibrationWidth + dp2px(1) + dp2px(5);
int width = resolveSize(dp2px(180), widthMeasureSpec);
mRadius = (width - mPadding * 2) / 2;
setMeasuredDimension(width, width - dp2px(30));
mCenterX = mCenterY = getMeasuredWidth() / 2f;
mRectFProgressArc.set(
mLength1 + mCalibrationWidth / 2f,
mLength1 + mCalibrationWidth / 2f,
getMeasuredWidth() - mLength1 - mCalibrationWidth / 2f,
getMeasuredWidth() - mLength1 - mCalibrationWidth / 2f
);
mRectFCalibrationFArc.set(
mLength1 + mCalibrationWidth / 2f,
mLength1 + mCalibrationWidth / 2f,
getMeasuredWidth() - mLength1 - mCalibrationWidth / 2f,
getMeasuredWidth() - mLength1 - mCalibrationWidth / 2f
);
mPaint.setTextSize(sp2px(10));
mPaint.getTextBounds("0", 0, "0".length(), mRectText);
mRectFTextArc.set(
mLength2 + mRectText.height(),
mLength2 + mRectText.height(),
getMeasuredWidth() - mLength2 - mRectText.height(),
getMeasuredWidth() - mLength2 - mRectText.height()
);
}
@Override
protected void onDraw(Canvas canvas) {
super.onDraw(canvas);
canvas.drawColor(mBackgroundColor);
/**
* 画进度圆弧背景
*/
// mPaint.setStrokeCap(Paint.Cap.ROUND);
mPaint.setStyle(Paint.Style.STROKE);
mPaint.setStrokeWidth(mProgressWidth);
mPaint.setColor(Color.parseColor("#ffCCCCCC"));
// mPaint.setAlpha(80);
canvas.drawArc(mRectFProgressArc, -130, 80, false, mPaint);
// mPaint.setAlpha(255);
if (isAnimFinish) {
/**
* 画进度圆弧(起始到速度值)
*/
mPaint.setShader(generateSweepGradient());
mPaint.setColor(Color.parseColor("#ff333333"));
canvas.drawArc(mRectFProgressArc, -130,
calculateRelativeAngleWithValue(mCreditValue) - 2, false, mPaint);
/**
* 画进度圆弧值指示亮点
*/
float[] point = getCoordinatePoint(
mRadius - mSparkleWidth / 2f,
mStartAngle + calculateRelativeAngleWithValue(mCreditValue)
);
mPaint.setStyle(Paint.Style.FILL);
mPaint.setColor(Color.parseColor("#ff333333"));
mPaint.setShader(generateRadialGradient(point[0], point[1]));
canvas.drawCircle(point[0], point[1], mSparkleWidth / 2f, mPaint);
} else {
/**
* 画进度圆弧值指示亮点
*/
mPaint.setShader(generateSweepGradient());
mPaint.setColor(Color.parseColor("#ff333333"));
canvas.drawArc(mRectFProgressArc, -130,
mAngleWhenAnim - mStartAngle - 2, false, mPaint);
/**
* 画值指示亮点
*/
float[] point = getCoordinatePoint(
mRadius - mSparkleWidth / 2f,
mAngleWhenAnim
);
mPaint.setColor(Color.parseColor("#ff333333"));
mPaint.setStyle(Paint.Style.FILL);
mPaint.setShader(generateRadialGradient(point[0], point[1]));
canvas.drawCircle(point[0], point[1], mSparkleWidth / 2f, mPaint);
}
/**
* 画刻度圆弧
*/
mPaint.setShader(null);
mPaint.setStyle(Paint.Style.STROKE);
mPaint.setColor(Color.parseColor("#00ffffff"));
// mPaint.setAlpha(80);
mPaint.setStrokeCap(Paint.Cap.SQUARE);
mPaint.setStrokeWidth(mCalibrationWidth);
canvas.drawArc(mRectFCalibrationFArc, -130, 80, false, mPaint);
/**
* 画长刻度
* 画好起始角度的一条刻度后通过canvas绕着原点旋转来画剩下的长刻度
*/
mPaint.setStrokeCap(Paint.Cap.ROUND);
mPaint.setStrokeWidth(dp2px(2));
mPaint.setColor(Color.parseColor("#ff666666"));
// mPaint.setAlpha(120);
float x0 = mCenterX;
float y0 = mPadding + mLength1 + dp2px(3);//控制刻度与进度条距离
float x1 = mCenterX;
float y1 = y0 + mCalibrationWidth + dp2px(2);//控制刻度长短
// 逆时针到开始处
canvas.save();
canvas.drawLine(x0, y0, x1, y1, mPaint);
float degree = mSweepAngle / mSection;
for (int i = 0; i < mSection / 2; i++) {
if (i >= 4 && i <= 6) {
canvas.rotate(-degree, mCenterX, mCenterY);
canvas.drawLine(x0, y0, x1, y1, mPaint);
}
}
canvas.restore();
// 顺时针到结尾处
canvas.save();
for (int i = 0; i < mSection / 2; i++) {
if (i >= 4 && i <= 6) {
canvas.rotate(degree, mCenterX, mCenterY);
canvas.drawLine(x0, y0, x1, y1, mPaint);
}
}
canvas.restore();
/**
* 画短刻度
* 同样采用canvas的旋转原理
*/
mPaint.setStrokeWidth(dp2px(1));
mPaint.setAlpha(0);
float x2 = mCenterX;
float y2 = y0 + mCalibrationWidth - dp2px(4);//控制刻度长短
// 逆时针到开始处
canvas.save();
canvas.drawLine(x0, y0, x2, y2, mPaint);
degree = mSweepAngle / (mSection * mPortion);
for (int i = 0; i < (mSection * mPortion) / 2; i++) {
canvas.rotate(-degree, mCenterX, mCenterY);
canvas.drawLine(x0, y0, x2, y2, mPaint);
}
canvas.restore();
// 顺时针到结尾处
canvas.save();
for (int i = 0; i < (mSection * mPortion) / 2; i++) {
canvas.rotate(degree, mCenterX, mCenterY);
canvas.drawLine(x0, y0, x2, y2, mPaint);
}
canvas.restore();
/**
* 画长刻度读数
* 添加一个圆弧path,文字沿着path绘制
*/
mPaint.setTextSize(sp2px(10));
mPaint.setTextAlign(Paint.Align.LEFT);
mPaint.setStyle(Paint.Style.FILL);
mPaint.setColor(Color.parseColor("#ff007AFF"));
// mPaint.setAlpha(255);
for (int i = 0; i < mTexts.length; i++) {
mPaint.getTextBounds(mTexts[i], 0, mTexts[i].length(), mRectText);
// 粗略把文字的宽度视为圆心角2*θ对应的弧长,利用弧长公式得到θ,下面用于修正角度
float θ = (float) (180 * mRectText.width() / 2 /
(Math.PI * (mRadius - mLength2 - mRectText.height())));
mPath.reset();
mPath.addArc(
mRectFTextArc,
mStartAngle + i * (mSweepAngle / mSection) - θ, // 正起始角度减去θ使文字居中对准长刻度
mSweepAngle
);
canvas.drawTextOnPath(mTexts[i], mPath, 0, 20, mPaint);
}
/**
* 画实读数值
*/
mPaint.setAlpha(255);
mPaint.setTextSize(sp2px(18));
mPaint.setTextAlign(Paint.Align.CENTER);
mPaint.setColor(Color.parseColor("#ff333333"));
String value = String.valueOf(speed);
canvas.drawText(value, mCenterX, mCenterY - dp2px(36), mPaint);
/**
* 画车速数单位
*/
mPaint.setAlpha(255);
mPaint.setTextSize(sp2px(12));
mPaint.setColor(Color.parseColor("#ff999999"));
canvas.drawText(calculateCreditDescription(), mCenterX, mCenterY - dp2px(20), mPaint);
/**
* 画车速描述
*/
mPaint.setAlpha(160);
mPaint.setTextSize(sp2px(12));
mPaint.setColor(Color.parseColor("#ff666666"));
canvas.drawText(getFormatTimeStr(), mCenterX, mCenterY - dp2px(5), mPaint);
mPaint.setColor(Color.parseColor("#00ffffff"));
}
private int dp2px(int dp) {
return (int) TypedValue.applyDimension(TypedValue.COMPLEX_UNIT_DIP, dp,
Resources.getSystem().getDisplayMetrics());
}
private int sp2px(int sp) {
return (int) TypedValue.applyDimension(TypedValue.COMPLEX_UNIT_SP, sp,
Resources.getSystem().getDisplayMetrics());
}
private SweepGradient generateSweepGradient() {
SweepGradient sweepGradient = new SweepGradient(mCenterX, mCenterY,
new int[]{Color.argb(200, 0, 122, 255), Color.argb(200, 0, 122, 255)},
new float[]{0, calculateRelativeAngleWithValue(mCreditValue) / 360}
);
Matrix matrix = new Matrix();
matrix.setRotate(mStartAngle - 1, mCenterX, mCenterY);
sweepGradient.setLocalMatrix(matrix);
return sweepGradient;
}
private RadialGradient generateRadialGradient(float x, float y) {
return new RadialGradient(x, y, mSparkleWidth / 2f,
new int[]{Color.argb(255, 255, 255, 255), Color.argb(80, 255, 255, 255)},
new float[]{0.4f, 1},
Shader.TileMode.CLAMP
);
}
private float[] getCoordinatePoint(float radius, float angle) {
float[] point = new float[2];
double arcAngle = Math.toRadians(angle); //将角度转换为弧度
if (angle < 90) {
point[0] = (float) (mCenterX + Math.cos(arcAngle) * radius);
point[1] = (float) (mCenterY + Math.sin(arcAngle) * radius);
} else if (angle == 90) {
point[0] = mCenterX;
point[1] = mCenterY + radius;
} else if (angle > 90 && angle < 180) {
arcAngle = Math.PI * (180 - angle) / 180.0;
point[0] = (float) (mCenterX - Math.cos(arcAngle) * radius);
point[1] = (float) (mCenterY + Math.sin(arcAngle) * radius);
} else if (angle == 180) {
point[0] = mCenterX - radius;
point[1] = mCenterY;
} else if (angle > 180 && angle < 270) {
arcAngle = Math.PI * (angle - 180) / 180.0;
point[0] = (float) (mCenterX - Math.cos(arcAngle) * radius);
point[1] = (float) (mCenterY - Math.sin(arcAngle) * radius);
} else if (angle == 270) {
point[0] = mCenterX;
point[1] = mCenterY - radius;
} else {
arcAngle = Math.PI * (360 - angle) / 180.0;
point[0] = (float) (mCenterX + Math.cos(arcAngle) * radius);
point[1] = (float) (mCenterY - Math.sin(arcAngle) * radius);
}
return point;
}
/**
* 相对起始角度计算速度所对应的角度大小
*/
private float calculateRelativeAngleWithValue(int value) {
float degreePerSection = 1f * mSweepAngle / mSection;
return value * 2;
}
/**
* 车速单位
*/
private String calculateCreditDescription() {
return "x0.1Mpa";
}
private SimpleDateFormat mDateFormat;
private String getFormatTimeStr() {
return "前气压";
}
public int getCreditValue() {
return mCreditValue;
}
/**
* 设置车速
*
* @param creditValue
*/
public void setCreditValue(double creditValue) {
if (mSolidCreditValue == creditValue || creditValue < mMin || creditValue > mMax) {
return;
}
speed = creditValue;
mSolidCreditValue = (int) creditValue;
mCreditValue = (int) creditValue;
postInvalidate();
}
/**
* 设置速度值并播放动画
*
* @param creditValue 速度值
*/
public void setCreditValueWithAnim(double creditValue,double speedData) {
if (creditValue < mMin || creditValue > mMax || !isAnimFinish) {
return;
}
speed = speedData;
mSolidCreditValue = (int) (creditValue);
int data = (int) (creditValue * 10);
ValueAnimator creditValueAnimator = ValueAnimator.ofInt(0, data);
creditValueAnimator.addUpdateListener(new ValueAnimator.AnimatorUpdateListener() {
@Override
public void onAnimationUpdate(ValueAnimator animation) {
mCreditValue = (int) animation.getAnimatedValue();
postInvalidate();
}
});
// 计算最终值对应的角度,以扫过的角度的线性变化来播放动画
float degree = calculateRelativeAngleWithValue(data);
ValueAnimator degreeValueAnimator = ValueAnimator.ofFloat(mStartAngle, mStartAngle + degree);
degreeValueAnimator.addUpdateListener(new ValueAnimator.AnimatorUpdateListener() {
@Override
public void onAnimationUpdate(ValueAnimator animation) {
mAngleWhenAnim = (float) animation.getAnimatedValue();
}
});
@SuppressLint("ObjectAnimatorBinding") ObjectAnimator colorAnimator = ObjectAnimator.ofInt(this, "mBackgroundColor", mBgColors[0], mBgColors[0]);
long delay = 1000;
colorAnimator.setEvaluator(new ArgbEvaluator());
colorAnimator.addUpdateListener(new ValueAnimator.AnimatorUpdateListener() {
@Override
public void onAnimationUpdate(ValueAnimator animation) {
mBackgroundColor = (int) animation.getAnimatedValue();
}
});
AnimatorSet animatorSet = new AnimatorSet();
animatorSet
.setDuration(delay)
.playTogether(creditValueAnimator, degreeValueAnimator, colorAnimator);
animatorSet.addListener(new AnimatorListenerAdapter() {
@Override
public void onAnimationStart(Animator animation) {
super.onAnimationStart(animation);
isAnimFinish = false;
}
@Override
public void onAnimationEnd(Animator animation) {
super.onAnimationEnd(animation);
isAnimFinish = true;
}
@Override
public void onAnimationCancel(Animator animation) {
super.onAnimationCancel(animation);
isAnimFinish = true;
}
});
animatorSet.start();
}
}
5、soc进度条
package com.xiaoya.myapplication.chart.utils;
import android.animation.ValueAnimator;
import android.content.Context;
import android.content.res.Resources;
import android.content.res.TypedArray;
import android.graphics.Canvas;
import android.graphics.Color;
import android.graphics.Paint;
import android.graphics.Point;
import android.graphics.RectF;
import android.graphics.SweepGradient;
import android.graphics.Typeface;
import android.text.TextPaint;
import android.util.AttributeSet;
import android.util.Log;
import android.view.View;
import com.xiaoya.myapplication.R;
public class SocCircleProgress extends View {
private static final String TAG = SocCircleProgress.class.getSimpleName();
private Context mContext;
//默认大小
private int mDefaultSize;
//是否开启抗锯齿
private boolean antiAlias;
//绘制提示
private TextPaint mHintPaint;
private CharSequence mHint;
private int mHintColor;
private float mHintSize;
private float mHintOffset;
//绘制单位
private TextPaint mUnitPaint;
private CharSequence mUnit;
private int mUnitColor;
private float mUnitSize;
private float mUnitOffset;
//绘制数值
private TextPaint mValuePaint;
private float mValue;
private float mMaxValue;
private float mValueOffset;
private int mPrecision;
private String mPrecisionFormat;
private int mValueColor;
private float mValueSize;
//绘制圆弧
private Paint mArcPaint;
private float mArcWidth;
private float mStartAngle, mSweepAngle;
private RectF mRectF;
//渐变的颜色是360度,如果只显示270,那么则会缺失部分颜色
private SweepGradient mSweepGradient;
private int[] mGradientColors = {Color.GREEN, Color.GREEN, Color.GREEN};
// private int[] mGradientColors = {Color.GREEN, Color.YELLOW, Color.RED};
//当前进度,[0.0f,1.0f]
private float mPercent;
//动画时间
private long mAnimTime;
//属性动画
private ValueAnimator mAnimator;
//绘制背景圆弧
private Paint mBgArcPaint;
private int mBgArcColor;
private float mBgArcWidth;
//圆心坐标,半径
private Point mCenterPoint;
private float mRadius;
private float mTextOffsetPercentInRadius;
public SocCircleProgress(Context context, AttributeSet attrs) {
super(context, attrs);
init(context, attrs);
}
private void init(Context context, AttributeSet attrs) {
mContext = context;
mDefaultSize = MiscUtil.dipToPx(mContext, 150);
mAnimator = new ValueAnimator();
mRectF = new RectF();
mCenterPoint = new Point();
initAttrs(attrs);
initPaint();
setValue(mValue);
}
private void initAttrs(AttributeSet attrs) {
TypedArray typedArray = mContext.obtainStyledAttributes(attrs, R.styleable.CircleProgressBar);
antiAlias = typedArray.getBoolean(R.styleable.CircleProgressBar_antiAlias, Constant.ANTI_ALIAS);
mHint = typedArray.getString(R.styleable.CircleProgressBar_hint);
mHintColor = typedArray.getColor(R.styleable.CircleProgressBar_hintColor, Color.BLACK);
mHintSize = typedArray.getDimension(R.styleable.CircleProgressBar_hintSize, Constant.DEFAULT_HINT_SIZE);
mValue = typedArray.getFloat(R.styleable.CircleProgressBar_value, Constant.DEFAULT_VALUE);
mMaxValue = typedArray.getFloat(R.styleable.CircleProgressBar_maxValue, Constant.DEFAULT_MAX_VALUE);
//内容数值精度格式
mPrecision = typedArray.getInt(R.styleable.CircleProgressBar_precision, 0);
mPrecisionFormat = MiscUtil.getPrecisionFormat(mPrecision);
mValueColor = typedArray.getColor(R.styleable.CircleProgressBar_valueColor, Color.BLACK);
mValueSize = typedArray.getDimension(R.styleable.CircleProgressBar_valueSize, Constant.DEFAULT_VALUE_SIZE);
mUnit = typedArray.getString(R.styleable.CircleProgressBar_unit);
mUnitColor = typedArray.getColor(R.styleable.CircleProgressBar_unitColor, Color.BLACK);
mUnitSize = typedArray.getDimension(R.styleable.CircleProgressBar_unitSize, Constant.DEFAULT_UNIT_SIZE);
mArcWidth = typedArray.getDimension(R.styleable.CircleProgressBar_arcWidth, Constant.DEFAULT_ARC_WIDTH);
mStartAngle = typedArray.getFloat(R.styleable.CircleProgressBar_startAngle, Constant.DEFAULT_START_ANGLE);
mSweepAngle = typedArray.getFloat(R.styleable.CircleProgressBar_sweepAngle, Constant.DEFAULT_SWEEP_ANGLE);
mBgArcColor = typedArray.getColor(R.styleable.CircleProgressBar_bgArcColor, Color.WHITE);
mBgArcWidth = typedArray.getDimension(R.styleable.CircleProgressBar_bgArcWidth, Constant.DEFAULT_ARC_WIDTH);
mTextOffsetPercentInRadius = typedArray.getFloat(R.styleable.CircleProgressBar_textOffsetPercentInRadius, 0.33f);
//mPercent = typedArray.getFloat(R.styleable.CircleProgressBar_percent, 0);
mAnimTime = typedArray.getInt(R.styleable.CircleProgressBar_animTime, Constant.DEFAULT_ANIM_TIME);
int gradientArcColors = typedArray.getResourceId(R.styleable.CircleProgressBar_arcColors, 0);
if (gradientArcColors != 0) {
try {
int[] gradientColors = getResources().getIntArray(gradientArcColors);
if (gradientColors.length == 0) {//如果渐变色为数组为0,则尝试以单色读取色值
int color = getResources().getColor(gradientArcColors);
mGradientColors = new int[2];
mGradientColors[0] = color;
mGradientColors[1] = color;
} else if (gradientColors.length == 1) {//如果渐变数组只有一种颜色,默认设为两种相同颜色
mGradientColors = new int[2];
mGradientColors[0] = gradientColors[0];
mGradientColors[1] = gradientColors[0];
} else {
mGradientColors = gradientColors;
}
} catch (Resources.NotFoundException e) {
throw new Resources.NotFoundException("the give resource not found.");
}
}
typedArray.recycle();
}
private void initPaint() {
mHintPaint = new TextPaint();
// 设置抗锯齿,会消耗较大资源,绘制图形速度会变慢。
mHintPaint.setAntiAlias(antiAlias);
// 设置绘制文字大小
mHintPaint.setTextSize(mHintSize);
// 设置画笔颜色
mHintPaint.setColor(mHintColor);
// 从中间向两边绘制,不需要再次计算文字
mHintPaint.setTextAlign(Paint.Align.CENTER);
mValuePaint = new TextPaint();
mValuePaint.setAntiAlias(antiAlias);
mValuePaint.setTextSize(mValueSize);
mValuePaint.setColor(mValueColor);
// 设置Typeface对象,即字体风格,包括粗体,斜体以及衬线体,非衬线体等
mValuePaint.setTypeface(Typeface.DEFAULT_BOLD);
mValuePaint.setTextAlign(Paint.Align.CENTER);
mUnitPaint = new TextPaint();
mUnitPaint.setAntiAlias(antiAlias);
mUnitPaint.setTextSize(mUnitSize);
mUnitPaint.setColor(mUnitColor);
mUnitPaint.setTextAlign(Paint.Align.CENTER);
mArcPaint = new Paint();
mArcPaint.setAntiAlias(antiAlias);
// 设置画笔的样式,为FILL,FILL_OR_STROKE,或STROKE
mArcPaint.setStyle(Paint.Style.STROKE);
// 设置画笔粗细
mArcPaint.setStrokeWidth(mArcWidth);
// 当画笔样式为STROKE或FILL_OR_STROKE时,设置笔刷的图形样式,如圆形样式
// Cap.ROUND,或方形样式 Cap.SQUARE
mArcPaint.setStrokeCap(Paint.Cap.ROUND);
mBgArcPaint = new Paint();
mBgArcPaint.setAntiAlias(antiAlias);
mBgArcPaint.setColor(mBgArcColor);
mBgArcPaint.setStyle(Paint.Style.STROKE);
mBgArcPaint.setStrokeWidth(mBgArcWidth);
mBgArcPaint.setStrokeCap(Paint.Cap.ROUND);
}
@Override
protected void onMeasure(int widthMeasureSpec, int heightMeasureSpec) {
super.onMeasure(widthMeasureSpec, heightMeasureSpec);
setMeasuredDimension(MiscUtil.measure(widthMeasureSpec, mDefaultSize),
MiscUtil.measure(heightMeasureSpec, mDefaultSize));
}
@Override
protected void onSizeChanged(int w, int h, int oldw, int oldh) {
super.onSizeChanged(w, h, oldw, oldh);
Log.d(TAG, "onSizeChanged: w = " + w + "; h = " + h + "; oldw = " + oldw + "; oldh = " + oldh);
//求圆弧和背景圆弧的最大宽度
float maxArcWidth = Math.max(mArcWidth, mBgArcWidth);
//求最小值作为实际值
int minSize = Math.min(w - getPaddingLeft() - getPaddingRight() - 2 * (int) maxArcWidth,
h - getPaddingTop() - getPaddingBottom() - 2 * (int) maxArcWidth);
//减去圆弧的宽度,否则会造成部分圆弧绘制在外围
mRadius = minSize / 2;
//获取圆的相关参数
mCenterPoint.x = w / 2;
mCenterPoint.y = h / 2;
//绘制圆弧的边界
mRectF.left = mCenterPoint.x - mRadius - maxArcWidth / 2;
mRectF.top = mCenterPoint.y - mRadius - maxArcWidth / 2;
mRectF.right = mCenterPoint.x + mRadius + maxArcWidth / 2;
mRectF.bottom = mCenterPoint.y + mRadius + maxArcWidth / 2;
//计算文字绘制时的 baseline
//由于文字的baseline、descent、ascent等属性只与textSize和typeface有关,所以此时可以直接计算
//若value、hint、unit由同一个画笔绘制或者需要动态设置文字的大小,则需要在每次更新后再次计算
mValueOffset = mCenterPoint.y + getBaselineOffsetFromY(mValuePaint);
mHintOffset = mCenterPoint.y - mRadius * mTextOffsetPercentInRadius + getBaselineOffsetFromY(mHintPaint);
mUnitOffset = mCenterPoint.y + mRadius * mTextOffsetPercentInRadius + getBaselineOffsetFromY(mUnitPaint);
updateArcPaint();
Log.d(TAG, "onSizeChanged: 控件大小 = " + "(" + w + ", " + h + ")"
+ "圆心坐标 = " + mCenterPoint.toString()
+ ";圆半径 = " + mRadius
+ ";圆的外接矩形 = " + mRectF.toString());
}
private float getBaselineOffsetFromY(Paint paint) {
return MiscUtil.measureTextHeight(paint) / 2;
}
@Override
protected void onDraw(Canvas canvas) {
super.onDraw(canvas);
drawText(canvas);
drawArc(canvas);
}
/**
* 绘制内容文字
*
* @param canvas
*/
private void drawText(Canvas canvas) {
// 计算文字宽度,由于Paint已设置为居中绘制,故此处不需要重新计算
// float textWidth = mValuePaint.measureText(mValue.toString());
// float x = mCenterPoint.x - textWidth / 2;
canvas.drawText(String.format(mPrecisionFormat, mValue) + "%", mCenterPoint.x, mValueOffset - 15, mValuePaint);
if (mHint != null) {
canvas.drawText(mHint.toString(), mCenterPoint.x, mHintOffset, mHintPaint);
}
if (mUnit != null) {
canvas.drawText(mUnit.toString(), mCenterPoint.x, mUnitOffset + 10, mUnitPaint);
}
}
private void drawArc(Canvas canvas) {
// 绘制背景圆弧
// 从进度圆弧结束的地方开始重新绘制,优化性能
canvas.save();
float currentAngle = mSweepAngle * mPercent;
canvas.rotate(mStartAngle, mCenterPoint.x, mCenterPoint.y);
canvas.drawArc(mRectF, currentAngle, mSweepAngle - currentAngle + 2, false, mBgArcPaint);
// 第一个参数 oval 为 RectF 类型,即圆弧显示区域
// startAngle 和 sweepAngle 均为 float 类型,分别表示圆弧起始角度和圆弧度数
// 3点钟方向为0度,顺时针递增
// 如果 startAngle < 0 或者 > 360,则相当于 startAngle % 360
// useCenter:如果为True时,在绘制圆弧时将圆心包括在内,通常用来绘制扇形
canvas.drawArc(mRectF, 2, currentAngle, false, mArcPaint);
canvas.restore();
}
/**
* 更新圆弧画笔
*/
private void updateArcPaint() {
// 设置渐变
mSweepGradient = new SweepGradient(mCenterPoint.x, mCenterPoint.y, mGradientColors, null);
mArcPaint.setShader(mSweepGradient);
}
public boolean isAntiAlias() {
return antiAlias;
}
public CharSequence getHint() {
return mHint;
}
public void setHint(CharSequence hint) {
mHint = hint;
}
public CharSequence getUnit() {
return mUnit;
}
public void setUnit(CharSequence unit) {
mUnit = unit;
}
public float getValue() {
return mValue;
}
/**
* 设置当前值
*
* @param value
*/
public void setValue(float value) {
if (value > mMaxValue) {
value = mMaxValue;
}
float start = mPercent;
float end = value / mMaxValue;
startAnimator(start, end, mAnimTime);
}
private void startAnimator(float start, float end, long animTime) {
mAnimator = ValueAnimator.ofFloat(start, end);
mAnimator.setDuration(animTime);
mAnimator.addUpdateListener(new ValueAnimator.AnimatorUpdateListener() {
@Override
public void onAnimationUpdate(ValueAnimator animation) {
mPercent = (float) animation.getAnimatedValue();
mValue = mPercent * mMaxValue;
// if (BuildConfig.DEBUG) {
// Log.d(TAG, "onAnimationUpdate: percent = " + mPercent
// + ";currentAngle = " + (mSweepAngle * mPercent)
// + ";value = " + mValue);
// }
invalidate();
}
});
mAnimator.start();
}
/**
* 获取最大值
*
* @return
*/
public float getMaxValue() {
return mMaxValue;
}
/**
* 设置最大值
*
* @param maxValue
*/
public void setMaxValue(float maxValue) {
mMaxValue = maxValue;
}
/**
* 获取精度
*
* @return
*/
public int getPrecision() {
return mPrecision;
}
public void setPrecision(int precision) {
mPrecision = precision;
mPrecisionFormat = MiscUtil.getPrecisionFormat(precision);
}
public int[] getGradientColors() {
return mGradientColors;
}
/**
* 设置渐变
*
* @param gradientColors
*/
public void setGradientColors(int[] gradientColors) {
mGradientColors = gradientColors;
updateArcPaint();
}
public long getAnimTime() {
return mAnimTime;
}
public void setAnimTime(long animTime) {
mAnimTime = animTime;
}
/**
* 重置
*/
public void reset() {
startAnimator(mPercent, 0.0f, 1000L);
}
@Override
protected void onDetachedFromWindow() {
super.onDetachedFromWindow();
//释放资源
}
}