效果图:
图中效果分为3个部分:污染轨迹动画,太阳轨迹动画,太阳闪烁动画,下面分析如何实现太阳轨迹动画和太阳闪烁动画.
思路:
1.画出太阳轨迹背景
2.使用动画描绘太阳运动轨迹
3.根据太阳运动轨迹运动的角度得到太阳圆心的坐标并画出太阳(三角函数)
4.画出太阳闪烁的动画(三角函数,这里注释里详细注释)
关键代码:
public class WeatherView extends View {
/**
* 污染进度
*/
private int progressPolluton;
/**
* 太阳轨迹进度
*/
private int progressSunPath;
/**
* 太阳每条光线闪烁进度
*/
private int progressSun;
/**
* 太阳光线条数
*/
private int sunshineCount = 10;
/**
* 每个模块的上下间距
*/
private float marginVertical = ScreenUtils.dp2Px(20);
/**
* 污染指数圆半径
*/
private float radiusPollution = dp2Px(70);
/**
* 太阳半径
*/
private float radiusSun = dp2Px(10);
/**
* 太阳轨迹半径
*/
private float radiusSunPath = dp2Px(140);
/**
* 画笔
*/
private Paint paint = new Paint();
private RectF rectF = new RectF();
public WeatherView(Context context) {
this(context, null);
}
public WeatherView(Context context, @Nullable AttributeSet attrs) {
this(context, attrs, 0);
}
public WeatherView(Context context, @Nullable AttributeSet attrs, int defStyleAttr) {
super(context, attrs, defStyleAttr);
pollutionAnimation();
sunPathAnimation();
sunShineAnimation();
}
private void pollutionAnimation() {//污染指数
ValueAnimator animator1 = ValueAnimator.ofInt(400).setDuration(2000);
animator1.addUpdateListener(new ValueAnimator.AnimatorUpdateListener() {
@Override
public void onAnimationUpdate(ValueAnimator animation) {
progressPolluton = (int) animation.getAnimatedValue();
invalidate();
}
});
animator1.start();
}
private void sunPathAnimation() {//太阳轨迹
ValueAnimator animator2 = ValueAnimator.ofInt(0, 180).setDuration(3000);
animator2.addUpdateListener(new ValueAnimator.AnimatorUpdateListener() {
@Override
public void onAnimationUpdate(ValueAnimator animation) {
progressSunPath = (int) animation.getAnimatedValue();
invalidate();
}
});
animator2.start();
}
private void sunShineAnimation() {//太阳
ValueAnimator animator3 = ValueAnimator.ofInt(0, 24).setDuration(2000);
animator3.setInterpolator(new LinearInterpolator());
animator3.addUpdateListener(new ValueAnimator.AnimatorUpdateListener() {
@Override
public void onAnimationUpdate(ValueAnimator animation) {
progressSun = (int) animation.getAnimatedValue();
invalidate();
}
});
animator3.addListener(new Animator.AnimatorListener() {
@Override
public void onAnimationStart(Animator animation) {
}
@Override
public void onAnimationEnd(Animator animation) {
sunShineAnimation();
}
@Override
public void onAnimationCancel(Animator animation) {
}
@Override
public void onAnimationRepeat(Animator animation) {
}
});
animator3.start();
}
@Override
protected void onDraw(Canvas canvas) {
int widthCenter = getWidth() / 2;
drawPullotionIndex(canvas, widthCenter);
drawSunPath(canvas, widthCenter);
drawSun(canvas, widthCenter);
}
private void drawPullotionIndex(Canvas canvas, int widthCenter) {
paint.reset();
paint.setAntiAlias(true);
paint.setColor(Color.parseColor("#888888"));
paint.setStyle(Paint.Style.STROKE);
paint.setStrokeWidth(ScreenUtils.dp2Px(5));
paint.setStrokeCap(Paint.Cap.ROUND);
rectF.set(widthCenter - radiusPollution, marginVertical, widthCenter + radiusPollution, marginVertical + radiusPollution * 2);
canvas.drawArc(rectF, 150, 240, false, paint);//画出污染指数背景
paint.reset();
paint.setAntiAlias(true);
paint.setColor(Color.parseColor("#E91E63"));
paint.setStyle(Paint.Style.STROKE);
paint.setStrokeWidth(ScreenUtils.dp2Px(5));
paint.setStrokeCap(Paint.Cap.ROUND);
if (progressPolluton != 0) {
canvas.drawArc(rectF, 150, progressPolluton * (240f / 500), false, paint);
}
paint.reset();
paint.setAntiAlias(true);
paint.setTextSize(ScreenUtils.sp2Px(12));
paint.setTextAlign(Paint.Align.CENTER);
canvas.drawText("重度污染", widthCenter, marginVertical + radiusPollution, paint);
paint.setTextSize(ScreenUtils.sp2Px(14));
canvas.drawText(String.valueOf(progressPolluton), widthCenter, marginVertical + radiusPollution + paint.getFontSpacing(), paint);
int leftPollutionX = (int) (widthCenter + radiusPollution * Math.cos(150 * Math.PI / 180));//污染起始值横坐标
int leftPollutionY = (int) (marginVertical + radiusPollution + radiusPollution * Math.sin(150 * Math.PI / 180));//污染起始值纵坐标
canvas.drawText("0", leftPollutionX, leftPollutionY + ScreenUtils.dp2Px(16), paint);
int rightPollutionX = (int) (widthCenter + radiusPollution * Math.cos(30 * Math.PI / 180));//污染最大值横坐标
int rightPollutionY = (int) (marginVertical + radiusPollution + radiusPollution * Math.sin(30 * Math.PI / 180));//污染最大值纵坐标
canvas.drawText("500", rightPollutionX, rightPollutionY + ScreenUtils.dp2Px(16), paint);
}
private void drawSunPath(Canvas canvas, int widthCenter) {
PathEffect pathEffect = new DashPathEffect(new float[]{10, 10}, 10);
paint.reset();
paint.setAntiAlias(true);
paint.setStyle(Paint.Style.STROKE);
paint.setColor(Color.parseColor("#888888"));
paint.setPathEffect(pathEffect);
rectF.set(widthCenter - radiusSunPath, marginVertical + 2 * radiusPollution + 100, widthCenter + radiusSunPath, marginVertical + 2 * radiusPollution + 100 + 2 * radiusSunPath);
canvas.drawArc(rectF, 180, 180, false, paint);
paint.reset();
paint.setAntiAlias(true);
paint.setStyle(Paint.Style.STROKE);
paint.setColor(Color.parseColor("#E91E63"));
paint.setPathEffect(pathEffect);
canvas.drawArc(rectF, 180, progressSunPath, false, paint);
paint.reset();
paint.setAntiAlias(true);
canvas.drawLine(0, marginVertical + 2 * radiusPollution + 100 + radiusSunPath, getWidth(), marginVertical + 2 * radiusPollution + 100 + radiusSunPath, paint);
paint.setTextSize(ScreenUtils.dp2Px(14));
paint.setTextAlign(Paint.Align.CENTER);
canvas.drawText("5:30", widthCenter - radiusSunPath, marginVertical + 2 * radiusPollution + 100 + radiusSunPath + ScreenUtils.dp2Px(16), paint);
canvas.drawText("18:30", widthCenter + radiusSunPath, marginVertical + 2 * radiusPollution + 100 + radiusSunPath + ScreenUtils.dp2Px(16), paint);
}
private void drawSun(Canvas canvas, int widthCenter) {
paint.reset();
paint.setAntiAlias(true);
paint.setColor(Color.parseColor("#FFA82C"));
float cX = (float) (widthCenter + radiusSunPath * Math.cos((progressSunPath + 180) * Math.PI / 180));
float cY = (float) (marginVertical + 2 * radiusPollution + 100 + radiusSunPath + radiusSunPath * Math.sin((progressSunPath + 180) * Math.PI / 180));
canvas.drawCircle(cX, cY, radiusSun, paint);
int perAngle = 360 / sunshineCount;
paint.setStrokeWidth(4);
//绘制太阳光线需要知道线的起始和结束坐标,通过太阳轨迹运动轨迹可以得到太阳的圆心坐标,再以该圆心坐标
//找到两个不同半径的圆(当然,并不用画出这两个圆,我们只是需要它们上面的点用来画光线)
for (int i = 0; i < sunshineCount; i++) {//画太阳光芒
int angle = perAngle * i;
int inX = (int) (cX + (radiusSun + 5) * Math.cos(angle * Math.PI / 180));//光线起始x坐标
int inY = (int) (cY + (radiusSun + 5) * Math.sin(angle * Math.PI / 180));//光线起始y坐标
int outX = (int) (cX + (radiusSun + progressSun) * Math.cos(angle * Math.PI / 180));//光线结束x坐标
int outY = (int) (cY + (radiusSun + progressSun) * Math.sin(angle * Math.PI / 180));//光线结束y坐标
canvas.drawLine(inX, inY, outX, outY, paint);
}
}
public void startAnimation() {
pollutionAnimation();
sunPathAnimation();
}