无聊又来写自定义View了,2020南京一直不下雪,那就自己写一个,先上图
直接上代码
public class SnowView extends View {
private Paint snowPaint;
private Random random = new Random();
private List<Snow> snows = new ArrayList<>();
private Handler handler = new Handler();
public SnowView(Context context, @Nullable AttributeSet attrs) {
super(context, attrs);
snowPaint = new Paint(Paint.ANTI_ALIAS_FLAG);
snowPaint.setColor(Color.WHITE);
snowPaint.setStyle(Paint.Style.STROKE);
}
@Override
protected void onSizeChanged(int w, int h, int oldw, int oldh) {
super.onSizeChanged(w, h, oldw, oldh);
snows.clear();
for (int i = 0; i < 100; i++) {
snows.add(genASnow());
}
}
/**
* 生成一朵雪花
*
* @return
*/
private Snow genASnow() {
Snow snow = new Snow();
snow.setDegree(random.nextInt(60));
int radius = 10 + random.nextInt(10);//随机半径(雪花的大小)
snow.setRadius(radius);
snow.setWidth((int) (radius * 0.2));//雪花的粗细根据半径来计算
snow.setSpeed((int) (2 + Math.abs((radius - 20)) * 0.5));//雪花的速度:基础速度+随机值(根据雪花大小决定)
int red = random.nextInt(255);//随机一个颜色
int green = random.nextInt(255);
int blue = random.nextInt(255);
snow.setColor(Color.rgb(red, green, blue));
Point startPoint = new Point(random.nextInt(getWidth()), -random.nextInt(getHeight()));//雪花的起始点,也是第1个贝塞尔控制点
snow.setPointStart(startPoint);
snow.setPointControl(new Point(random.nextInt(getWidth()), random.nextInt(getHeight())));//雪花的起始点,也是第2个贝塞尔控制点
snow.setPointEnd(new Point(random.nextInt(getWidth()), getHeight() + random.nextInt(getHeight())));//雪花的起始点,也是第3个贝塞尔控制点
snow.setX(startPoint.x);
snow.setY(startPoint.y);
return snow;
}
@Override
protected void onDraw(Canvas canvas) {
super.onDraw(canvas);
canvas.drawColor(Color.BLACK);
//先绘制
for (int i = 0; i < snows.size(); i++) {
Snow snow = snows.get(i);
drawASnow(canvas, snow);
}
//进行Y轴的变换和角度的变换
for (int i = 0; i < snows.size(); i++) {
Snow snow = snows.get(i);
int y = snow.getY();
int d = snow.getDegree();
d = d + 1;
y += snow.getSpeed();
if (y >= snow.getPointEnd().y) {//当Y值在屏幕下方的时候重置到上方
y = -random.nextInt(getHeight());
}
snow.setY(y);
snow.setDegree(d);
}
handler.postDelayed(new Runnable() {
@Override
public void run() {
invalidate();
}
}, 10);
}
private void drawASnow(Canvas canvas, Snow snow) {
int snowX = snow.getX();
int snowY = snow.getY();
int snowRadius = snow.getRadius();
canvas.save();
snowPaint.setColor(snow.getColor());
snowPaint.setStrokeWidth(snow.getWidth());
canvas.rotate(snow.getDegree(), snowX, snowY);
for (int i = 0; i < 6; i++) {
//通过旋转的方式绘制雪花的6条边
int lineStartX = snowX;
int lineEndX = snowX + snowRadius;
int lineStartY = snowY;
int lineEndY = snowY;
//开始绘制雪花的分杈,分杈有两个与边的角度是60度,上边一个下边一个,分叉点的比例设置为0.6,分叉的长度设置为雪花半径的0.4
int line1StartX = (int) (lineStartX + snowRadius * 0.6);
double degree60R = Math.toRadians(60);
int line1EndX = (int) (line1StartX + Math.cos(degree60R) * snowRadius * 0.4);
int line1StartY = lineStartY;
int line1EndY = (int) (line1StartY - Math.sin(degree60R) * snowRadius * 0.4);
int line2StartX = (int) (lineStartX + snowRadius * 0.6);
int line2EndX = (int) (line1StartX + Math.cos(degree60R) * snowRadius * 0.4);
int line2StartY = lineStartY;
int line2EndY = (int) (line1StartY + Math.sin(degree60R) * snowRadius * 0.4);
//其实一朵雪花就是简单的直线绘制起来的
canvas.rotate(60, snowX, snowY);
canvas.drawLine(lineStartX, lineStartY, lineEndX, lineEndY, snowPaint);
canvas.drawLine(line1StartX, line1StartY, line1EndX, line1EndY, snowPaint);
canvas.drawLine(line2StartX, line2StartY, line2EndX, line2EndY, snowPaint);
}
canvas.restore();
}
private static class Snow {
private int width;
private int x;
private int y;
private int degree;
private int color;
private int radius;
private int speed;
//加入控制点的目的是为了实现贝塞尔路径的效果,因为雪花从天上飘下来肯定不会只直直的下来的,加上曲线路径会逼真一些
private Point pointStart;
private Point pointControl;
private Point pointEnd;
public Point getPointStart() {
return pointStart;
}
public void setPointStart(Point pointStart) {
this.pointStart = pointStart;
}
public Point getPointControl() {
return pointControl;
}
public void setPointControl(Point pointControl) {
this.pointControl = pointControl;
}
public Point getPointEnd() {
return pointEnd;
}
public void setPointEnd(Point pointEnd) {
this.pointEnd = pointEnd;
}
public int getSpeed() {
return speed;
}
public void setSpeed(int speed) {
this.speed = speed;
}
public int getRadius() {
return radius;
}
public void setRadius(int radius) {
this.radius = radius;
}
public int getWidth() {
return width;
}
public void setWidth(int width) {
this.width = width;
}
public int getX() {
float tick = y * 1.0f / (pointEnd.y - pointStart.y);
return BezierUtil.CalculateBezierPointForQuadratic(tick, pointStart, pointControl, pointEnd).x;
}
public void setX(int x) {
this.x = x;
}
public int getY() {
return y;
}
public void setY(int y) {
this.y = y;
}
public int getDegree() {
return degree;
}
public void setDegree(int degree) {
this.degree = degree;
}
public int getColor() {
return color;
}
public void setColor(int color) {
this.color = color;
}
}
public static class BezierUtil {
/**
* B(t) = (1 - t)^2 * P0 + 2t * (1 - t) * P1 + t^2 * P2, t ∈ [0,1]
*
* @param t 曲线长度比例
* @param p0 起始点
* @param p1 控制点
* @param p2 终止点
* @return t对应的点
*/
public static Point CalculateBezierPointForQuadratic(float t, Point p0, Point p1, Point p2) {
Point point = new Point();
float temp = 1 - t;
point.x = (int) (temp * temp * p0.x + 2 * t * temp * p1.x + t * t * p2.x);
point.y = (int) (temp * temp * p0.y + 2 * t * temp * p1.y + t * t * p2.y);
return point;
}
/**
* B(t) = P0 * (1-t)^3 + 3 * P1 * t * (1-t)^2 + 3 * P2 * t^2 * (1-t) + P3 * t^3, t ∈ [0,1]
*
* @param t 曲线长度比例
* @param p0 起始点
* @param p1 控制点1
* @param p2 控制点2
* @param p3 终止点
* @return t对应的点
*/
public static Point CalculateBezierPointForCubic(float t, Point p0, Point p1, Point p2, Point p3) {
Point point = new Point();
float temp = 1 - t;
point.x = (int) (p0.x * temp * temp * temp + 3 * p1.x * t * temp * temp + 3 * p2.x * t * t * temp + p3.x * t * t * t);
point.y = (int) (p0.y * temp * temp * temp + 3 * p1.y * t * temp * temp + 3 * p2.y * t * t * temp + p3.y * t * t * t);
return point;
}
}
}