自定义ProgressBar-油表,汽车速度表控件
程序员文章站
2022-03-03 22:03:37
一、写在前面的话今天做了个控件,觉得还挺漂亮,就拿出来给大家分享一下,主要为了给大家介绍一下ValueAnimator这个动画,确实也没啥好说的,网上一堆教程,还是说说说画这个动画好了。二、需求分析刚拿到需求,我们分析一下。控件有两道圆弧,第一道上面有数据段,第二段是具体数值的指示段。数值变换的时候,需要平滑过渡,不能突兀。三、开始画。1、画圆弧 private void drawArc(Canvas canvas) { mBgPaint.setStrok.....
一、写在前面的话
今天做了个控件,觉得还挺漂亮,就拿出来给大家分享一下,主要为了给大家介绍一下ValueAnimator这个动画,确实也没啥好说的,网上一堆教程,还是说说说画这个动画好了。
二、需求分析
刚拿到需求,我们分析一下。控件有两道圆弧,第一道上面有数据段,第二段是具体数值的指示段。
数值变换的时候,需要平滑过渡,不能突兀。
三、开始画。
1、画圆弧
private void drawArc(Canvas canvas) {
mBgPaint.setStrokeWidth(mProgressSize);
canvas.drawArc(mProgressSize * lineSize, mProgressSize * lineSize, mWidth - mProgressSize * lineSize, mHeight - mProgressSize * lineSize, 150, 240, false, mBgPaint);
//画推荐度数
canvas.drawArc(mProgressSize * lineSize, mProgressSize * lineSize, mWidth - mProgressSize * lineSize, mHeight - mProgressSize * lineSize, 150 + lowProgress * 240 / 100, (highProgress - lowProgress) * 240 / 100, false, mProgressPaint);
//画推荐度数的值
int r = mWidth / 2 - mProgressSize;
int x = getArcX(150 + lowProgress * 240 / 100, r);
int y = getArcY(150 + lowProgress * 240 / 100, r);
int x2 = getArcX(150 + highProgress * 240 / 100, r);
int y2 = getArcY(150 + highProgress * 240 / 100, r);
if (x < mWidth / 2) {
x = x - 13;
} else {
y = y + 7;
x = x - 3;
}
if (x2 < mWidth / 2) {
x2 = x2 - 13;
} else {
x2 = x2 - 3;
y2 = y2 + 7;
}
canvas.drawText(stringLowProgress, x, y, mTextPaint);
canvas.drawText(stringHighProgress, x2, y2, mTextPaint);
mBgPaint.setStrokeWidth(mProgressSize / 3);
canvas.drawArc(mProgressSize * offset, mProgressSize * offset, mWidth - mProgressSize * offset, mHeight - mProgressSize * offset, 150, 240, false, mBgPaint);
}
为什么会/100呢,因为这里我把进度放大了一百倍,为了值的变化更流畅。
2、画刻度
@RequiresApi(api = Build.VERSION_CODES.LOLLIPOP)
private void drawLine(Canvas canvas) {
canvas.drawArc(mProgressSize * lineSize, mProgressSize * lineSize, mWidth - mProgressSize * lineSize, mHeight - mProgressSize * lineSize, 150 + 6 - 1, 2, false, mWhitePaint);
for (int i = 1; i < 10; i++) {
canvas.drawArc(mProgressSize * lineSize, mProgressSize * lineSize, mWidth - mProgressSize * lineSize, mHeight - mProgressSize * lineSize, 150 + 24 * i - 1, 2, false, mWhitePaint);
}
canvas.drawArc(mProgressSize * lineSize, mProgressSize * lineSize, mWidth - mProgressSize * lineSize, mHeight - mProgressSize * lineSize, 30 - 6 - 1, 2, false, mWhitePaint);
}
这里的刻度我们画一小段白色的圆弧,大家可以根据自己的要求改一下。
3、画指针
private void drawProgress(Canvas canvas) {
//计算当前的角度
int angle = 150 + mProgress * 240 / mMaxProgress;
int r = mWidth / 2 - mProgressSize * offset;
int cx = getArcX(angle, r);
int cy = getArcY(angle, r);
canvas.drawCircle(cx, cy, 4, mProgressPaint);
//圆外一点的坐标
int x = getArcX(angle, r + 35);
int y = getArcY(angle, r + 35);
//控制点1
int co1x = getArcX(angle - 4, r + 5);
int co1y = getArcY(angle - 4, r + 5);
//控制点2
int co2x = getArcX(angle + 4, r + 5);
int co2y = getArcY(angle + 4, r + 5);
path.reset();
path.moveTo(co1x, co1y);
path.lineTo(co2x, co2y);
path.lineTo(x, y);
path.lineTo(co1x, co1y);
canvas.drawPath(path, mSmallWhitePaint);
canvas.drawCircle(cx, cy, 1, mWhitePaint);
}
4、指针动画
// /**
// * 进度显示动画效果初始化
// */
private void initValueAnimator() {
valueAnimator = new ValueAnimator();
valueAnimator.setInterpolator(interpolator);
valueAnimator.addUpdateListener(new ValueAnimatorListenerImp());
}
/**
* 动画开始 更新进度显示
*/
private class ValueAnimatorListenerImp implements ValueAnimator.AnimatorUpdateListener {
@Override
public void onAnimationUpdate(ValueAnimator valueAnimator) {
int value = Math.round((Float) valueAnimator.getAnimatedValue());
mProgress = value;
// if (valueChangeListener != null) {
// valueChangeListener.onValueChange(value);
// }
postInvalidate();
}
}
//进度条的值会放大一百倍,为了使改变动画的帧率更高
public void setProgress(int progress) {
if (System.currentTimeMillis() - lastTime < duration) {
return;
}
lastTime = System.currentTimeMillis();
// this.mProgress = progress * 100;
if (valueAnimator != null) {
valueAnimator.setFloatValues(mProgress, progress * 100);
valueAnimator.setDuration(duration);
valueAnimator.start();
}
}
5、布局文件
<androidx.cardview.widget.CardView
android:layout_width="@dimen/dp_180"
android:layout_height="@dimen/dp_180"
android:layout_margin="@dimen/text_margin"
android:background="@color/white"
android:elevation="@dimen/dp_5"
app:cardCornerRadius="@dimen/dp_90"
tools:ignore="UnusedAttribute">
<RelativeLayout
android:layout_width="match_parent"
android:layout_height="match_parent">
<com.jack.heart.test.widget.SmartCircleProgress
android:id="@+id/scp_o2"
android:layout_width="match_parent"
android:layout_height="match_parent"
app:color="@color/color_a"
app:highProgress="100"
app:lowProgress="80"
app:progressColor="@color/color_a_progress"
app:stringHighProgress="99"
app:stringLowProgress="80" />
<TextView
android:id="@+id/tv_o2_values"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_centerInParent="true"
android:text="--"
android:textColor="@color/color_a_progress"
android:textSize="@dimen/sp_30"
android:textStyle="bold" />
<TextView
android:id="@+id/tv_o2_pce"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_below="@+id/tv_o2_values"
android:layout_centerHorizontal="true"
android:text="L/100km"
android:textColor="@color/color_text_hint"
android:textSize="@dimen/text_3" />
<TextView
android:id="@+id/tv_o2_name"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_below="@+id/tv_o2_pce"
android:layout_centerHorizontal="true"
android:text="百公里耗油量"
android:textColor="@color/color_text_black1"
android:textSize="@dimen/text_3" />
<ImageView
android:layout_width="@dimen/dp_20"
android:layout_height="@dimen/dp_20"
android:visibility="gone"
android:layout_below="@+id/tv_o2_name"
android:layout_centerHorizontal="true"
android:background="@mipmap/measure_icon_trend_spo2" />
</RelativeLayout>
</androidx.cardview.widget.CardView>
用CardView做外面的阴影效果,因为很简单,就没有封装了。
四、结束
说完了,因为内容比较简单,有点词穷了,所以基本都是贴代码了。最后会附上源码的,直接copy就可以用了,大家如果有啥想开发的控件都可以私信我,过年回家敲着玩。
import android.animation.ValueAnimator;
import android.annotation.SuppressLint;
import android.content.Context;
import android.content.res.TypedArray;
import android.graphics.Canvas;
import android.graphics.Color;
import android.graphics.Paint;
import android.graphics.Path;
import android.os.Build;
import android.util.AttributeSet;
import android.util.Log;
import android.view.View;
import android.view.animation.AccelerateDecelerateInterpolator;
import android.view.animation.DecelerateInterpolator;
import android.view.animation.Interpolator;
import androidx.annotation.Nullable;
import androidx.annotation.RequiresApi;
public class SmartCircleProgress extends View {
private int mWidth = 0;
private int mHeight = 0;
private Paint mBgPaint;
private Paint mProgressPaint;
private Paint mWhitePaint;
private Paint mSmallWhitePaint;
private Paint mTextPaint;
private int mProgressSize = 25;
private int mProgress = 0;
private int begin = 0;
private int mMaxProgress = 100;
//最外层圆弧相对边距的距离的比
private int lineSize = 2;
//最内层圆弧相对边距的距离的比
private int offset = 5;
private Path path;
private int duration = 400;
private int type = 0;
private Interpolator interpolator = new DecelerateInterpolator();
private ValueAnimator valueAnimator;
private long lastTime = System.currentTimeMillis();
private int highProgress = 0;
private int lowProgress = 0;
private String stringHighProgress = "";
private String stringLowProgress = "";
public SmartCircleProgress(Context context) {
super(context);
init(context, null);
}
public SmartCircleProgress(Context context, @Nullable AttributeSet attrs) {
super(context, attrs);
init(context, attrs);
}
public SmartCircleProgress(Context context, @Nullable AttributeSet attrs, int defStyleAttr) {
super(context, attrs, defStyleAttr);
init(context, attrs);
}
@SuppressLint("Recycle")
private void init(Context context, AttributeSet attrs) {
TypedArray s = null;
if (attrs != null) {
s = context.obtainStyledAttributes(attrs, R.styleable.SmartCircleProgress);//从xml那传来的一组值
}
mBgPaint = new Paint();
mBgPaint.setColor(getResources().getColor(R.color.color_a));
mBgPaint.setDither(true);
mBgPaint.setStyle(Paint.Style.STROKE);
mBgPaint.setFlags(Paint.ANTI_ALIAS_FLAG);
mBgPaint.setAntiAlias(true);
mBgPaint.setStrokeCap(Paint.Cap.ROUND);
mBgPaint.setStrokeWidth(mProgressSize);
mTextPaint = new Paint();
mTextPaint.setColor(getResources().getColor(R.color.color_a));
mTextPaint.setFlags(Paint.ANTI_ALIAS_FLAG);
mTextPaint.setAntiAlias(true);
mTextPaint.setTextSize(20);
mWhitePaint = new Paint();
mWhitePaint.setColor(Color.WHITE);
mWhitePaint.setDither(true);
mWhitePaint.setStyle(Paint.Style.STROKE);
mWhitePaint.setFlags(Paint.ANTI_ALIAS_FLAG);
mWhitePaint.setAntiAlias(true);
mWhitePaint.setStrokeWidth(mProgressSize);
mSmallWhitePaint = new Paint();
mSmallWhitePaint.setColor(getResources().getColor(R.color.color_a_progress));
mSmallWhitePaint.setDither(true);
mSmallWhitePaint.setStyle(Paint.Style.FILL);
mSmallWhitePaint.setFlags(Paint.ANTI_ALIAS_FLAG);
mSmallWhitePaint.setAntiAlias(true);
mSmallWhitePaint.setStrokeWidth(1);
mProgressPaint = new Paint();
mProgressPaint.setColor(getResources().getColor(R.color.color_a_progress));
mProgressPaint.setDither(true);
mProgressPaint.setStyle(Paint.Style.STROKE);
mProgressPaint.setFlags(Paint.ANTI_ALIAS_FLAG);
mProgressPaint.setAntiAlias(true);
mProgressPaint.setStrokeCap(Paint.Cap.ROUND);
mProgressPaint.setStrokeWidth(mProgressSize);
path = new Path();
if (s != null) {
//背景画笔
mBgPaint.setColor(s.getColor(R.styleable.SmartCircleProgress_color, getResources().getColor(R.color.color_a)));
//小细线画笔(箭头)
mSmallWhitePaint.setColor(s.getColor(R.styleable.SmartCircleProgress_progressColor, getResources().getColor(R.color.color_a_progress)));
//进度画笔
mProgressPaint.setColor(s.getColor(R.styleable.SmartCircleProgress_progressColor, getResources().getColor(R.color.color_a_progress)));
mTextPaint.setColor(s.getColor(R.styleable.SmartCircleProgress_progressColor, getResources().getColor(R.color.color_a_progress)));
lowProgress = s.getInteger(R.styleable.SmartCircleProgress_lowProgress, 0);
highProgress = s.getInteger(R.styleable.SmartCircleProgress_highProgress, 0);
stringHighProgress = s.getString(R.styleable.SmartCircleProgress_stringHighProgress);
stringLowProgress = s.getString(R.styleable.SmartCircleProgress_stringLowProgress);
}
initValueAnimator();
}
@Override
protected void onSizeChanged(int w, int h, int oldw, int oldh) {
super.onSizeChanged(w, h, oldw, oldh);
mWidth = w;
mHeight = h;
// mProgressSize = w / 18;
// mBgPaint.setStrokeWidth(mProgressSize);
// mProgressPaint.setStrokeWidth(mProgressSize);
}
@RequiresApi(api = Build.VERSION_CODES.LOLLIPOP)
@Override
protected void onDraw(Canvas canvas) {
super.onDraw(canvas);
drawArc(canvas);
drawLine(canvas);
drawProgress(canvas);
}
@RequiresApi(api = Build.VERSION_CODES.LOLLIPOP)
private void drawLine(Canvas canvas) {
canvas.drawArc(mProgressSize * lineSize, mProgressSize * lineSize, mWidth - mProgressSize * lineSize, mHeight - mProgressSize * lineSize, 150 + 6 - 1, 2, false, mWhitePaint);
for (int i = 1; i < 10; i++) {
canvas.drawArc(mProgressSize * lineSize, mProgressSize * lineSize, mWidth - mProgressSize * lineSize, mHeight - mProgressSize * lineSize, 150 + 24 * i - 1, 2, false, mWhitePaint);
}
canvas.drawArc(mProgressSize * lineSize, mProgressSize * lineSize, mWidth - mProgressSize * lineSize, mHeight - mProgressSize * lineSize, 30 - 6 - 1, 2, false, mWhitePaint);
}
public int getProgress() {
return mProgress / 100;
}
//进度条的值会放大一百倍,为了使改变动画的帧率更高
public void setProgress(int progress) {
if (System.currentTimeMillis() - lastTime < duration) {
return;
}
lastTime = System.currentTimeMillis();
// this.mProgress = progress * 100;
if (valueAnimator != null) {
valueAnimator.setFloatValues(mProgress, progress * 100);
valueAnimator.setDuration(duration);
valueAnimator.start();
}
}
public int getMaxProgress() {
return mMaxProgress / 100;
}
public void setMaxProgress(int mMaxProgress) {
this.mMaxProgress = mMaxProgress * 100;
postInvalidate();
}
@RequiresApi(api = Build.VERSION_CODES.LOLLIPOP)
private void drawArc(Canvas canvas) {
mBgPaint.setStrokeWidth(mProgressSize);
canvas.drawArc(mProgressSize * lineSize, mProgressSize * lineSize, mWidth - mProgressSize * lineSize, mHeight - mProgressSize * lineSize, 150, 240, false, mBgPaint);
//画推荐度数
canvas.drawArc(mProgressSize * lineSize, mProgressSize * lineSize, mWidth - mProgressSize * lineSize, mHeight - mProgressSize * lineSize, 150 + lowProgress * 240 / 100, (highProgress - lowProgress) * 240 / 100, false, mProgressPaint);
//画推荐度数的值
int r = mWidth / 2 - mProgressSize;
int x = getArcX(150 + lowProgress * 240 / 100, r);
int y = getArcY(150 + lowProgress * 240 / 100, r);
int x2 = getArcX(150 + highProgress * 240 / 100, r);
int y2 = getArcY(150 + highProgress * 240 / 100, r);
if (x < mWidth / 2) {
x = x - 13;
} else {
y = y + 7;
x = x - 3;
}
if (x2 < mWidth / 2) {
x2 = x2 - 13;
} else {
x2 = x2 - 3;
y2 = y2 + 7;
}
canvas.drawText(stringLowProgress, x, y, mTextPaint);
canvas.drawText(stringHighProgress, x2, y2, mTextPaint);
mBgPaint.setStrokeWidth(mProgressSize / 3);
canvas.drawArc(mProgressSize * offset, mProgressSize * offset, mWidth - mProgressSize * offset, mHeight - mProgressSize * offset, 150, 240, false, mBgPaint);
}
private int getArcX(int angle, int r) {
return (int) (mWidth / 2 + r * Math.cos(angle * Math.PI / 180));
}
private int getArcY(int angle, int r) {
return (int) (mWidth / 2 + r * Math.sin(angle * Math.PI / 180));
}
private void drawProgress(Canvas canvas) {
//计算当前的角度
int angle = 150 + mProgress * 240 / mMaxProgress;
int r = mWidth / 2 - mProgressSize * offset;
int cx = getArcX(angle, r);
int cy = getArcY(angle, r);
canvas.drawCircle(cx, cy, 4, mProgressPaint);
//圆外一点的坐标
int x = getArcX(angle, r + 35);
int y = getArcY(angle, r + 35);
//控制点1
int co1x = getArcX(angle - 4, r + 5);
int co1y = getArcY(angle - 4, r + 5);
//控制点2
int co2x = getArcX(angle + 4, r + 5);
int co2y = getArcY(angle + 4, r + 5);
path.reset();
path.moveTo(co1x, co1y);
path.lineTo(co2x, co2y);
path.lineTo(x, y);
path.lineTo(co1x, co1y);
canvas.drawPath(path, mSmallWhitePaint);
canvas.drawCircle(cx, cy, 1, mWhitePaint);
}
// /**
// * 进度显示动画效果初始化
// */
private void initValueAnimator() {
valueAnimator = new ValueAnimator();
valueAnimator.setInterpolator(interpolator);
valueAnimator.addUpdateListener(new ValueAnimatorListenerImp());
}
/**
* 动画开始 更新进度显示
*/
private class ValueAnimatorListenerImp implements ValueAnimator.AnimatorUpdateListener {
@Override
public void onAnimationUpdate(ValueAnimator valueAnimator) {
int value = Math.round((Float) valueAnimator.getAnimatedValue());
mProgress = value;
// if (valueChangeListener != null) {
// valueChangeListener.onValueChange(value);
// }
postInvalidate();
}
}
}
attrs.xml
<declare-styleable name="SmartCircleProgress">
<attr name="textColor" format="color"></attr>
<attr name="color" format="color"></attr>
<attr name="progressColor" format="color"></attr>
<attr name="max" format="integer"></attr>
<attr name="lowProgress" format="integer"></attr>
<attr name="highProgress" format="integer"></attr>
<attr name="stringLowProgress" format="string"></attr>
<attr name="stringHighProgress" format="string"></attr>
</declare-styleable>
本文地址:https://blog.csdn.net/qwe749082787/article/details/112801141