参考资源: 心形线方程 android 控件动画效果实现
效果图
实现过程
1. 绘制心形并为其添加抖动动画效果
绘制心形采用的是参数方程: 如果需要在xml 中使用该View, 设置心形的颜色, 还需要自定义属性: src/main/res/values/attrs.xml :
<?xml version="1.0" encoding="utf-8"?>
<resources>
<declare-styleable name="HeartView">
<attr name="heart_color" format="color" />
</declare-styleable>
</resources>
使用自定义属性, 需要在View中去解析, View解析自定义属性一般是在第二种构造函数中.
public class HeartView extends View {
private int mWidth = 50;
private int mHeight = 50;
private int heartColor = Color.parseColor("#db5860");
public HeartView(Context context) {
super(context);
}
public HeartView(Context context, @Nullable AttributeSet attrs) {
super(context, attrs);
if (context != null) {
TypedArray ta = context.obtainStyledAttributes(attrs, R.styleable.HeartView);
heartColor = ta.getColor(R.styleable.HeartView_heart_color, Color.parseColor("#db5860"));
ta.recycle();
}
}
public HeartView(Context context, @Nullable AttributeSet attrs, int defStyleAttr) {
super(context, attrs, defStyleAttr);
}
public HeartView(Context context, @Nullable AttributeSet attrs, int defStyleAttr, int defStyleRes) {
super(context, attrs, defStyleAttr, defStyleRes);
}
@Override
public void draw(Canvas canvas) {
super.draw(canvas);
mHeight = getHeight();
mWidth = getWidth();
Paint paint = new Paint();
paint.setAntiAlias(true);
paint.setColor(heartColor);
Path path = new Path();
float x = 0, y = 0;
path.moveTo((float) mWidth / 2, (float) mHeight / 2);
for (int t = 0; t < 10000; t++) {
x = (float) getWidth() * 1 / 50 * (float) (16 * Math.pow(Math.sin(t), 3));
y =
(float) getWidth() * 1 / 50 * (float) (13 * Math.cos(t) - 5 * Math.cos(2 * t) - 2 * Math.cos(3 * t) - Math.cos(4 * t));
path.lineTo(x + (float) mWidth / 2, -y + (float) mHeight / 2);
}
canvas.drawPath(path, paint);
}
private void drawCoordinateSystem(Canvas canvas) {
Paint paint = new Paint();
paint.setStrokeWidth(5);
paint.setColor(Color.parseColor("#263055"));
canvas.drawLine(0, (float) mHeight / 2, mWidth, (float) mHeight / 2, paint);
canvas.drawLine((float) mWidth / 2, 0, (float) mWidth / 2, mHeight, paint);
Path path = new Path();
path.addCircle((float) mWidth / 2, (float) mHeight / 2, 10, Path.Direction.CW);
path.close();
canvas.drawPath(path, paint);
}
@Override
public boolean onTouchEvent(MotionEvent event) {
HearTrembleAni hearTrembleAni = new HearTrembleAni();
hearTrembleAni.setDuration(1000);
hearTrembleAni.setRepeatCount(2);
this.startAnimation(hearTrembleAni);
return true;
}
static class HearTrembleAni extends Animation {
@Override
protected void applyTransformation(float interpolatedTime, Transformation t) {
t.getMatrix().setTranslate(
(float) Math.sin(interpolatedTime * 150) * 2,
(float) Math.sin(interpolatedTime * 150) * 2
);
super.applyTransformation(interpolatedTime, t);
}
}
public void setHeartColor(int heartColor) {
this.heartColor = heartColor;
}
}
心形的绘制效果如下:
2. 自定义Layout
在点击位置添加心形, 同时开启线程控制下落.
public class HeartLayout extends FrameLayout {
ExecutorService mExecutorService = new ScheduledThreadPoolExecutor(30);
public HeartLayout(@NonNull Context context) {
super(context);
}
public HeartLayout(@NonNull Context context, @Nullable AttributeSet attrs) {
super(context, attrs);
}
public HeartLayout(@NonNull Context context, @Nullable AttributeSet attrs, int defStyleAttr) {
super(context, attrs, defStyleAttr);
}
public HeartLayout(@NonNull Context context, @Nullable AttributeSet attrs, int defStyleAttr, int defStyleRes) {
super(context, attrs, defStyleAttr, defStyleRes);
}
@Override
public boolean onTouchEvent(MotionEvent event) {
float rawX = event.getX();
float rawY = event.getRawY();
drawHeart(rawX, rawY);
switch (event.getAction()) {
case MotionEvent.ACTION_DOWN:
drawHeart(rawX, rawY);
break;
default:
break;
}
return super.onTouchEvent(event);
}
private void drawHeart(float rawX, float rawY) {
HeartView heartView = new HeartView(this.getContext());
LayoutParams layoutParams = new LayoutParams(50, 50);
layoutParams.topMargin = (int) rawY - 50;
layoutParams.leftMargin = (int) rawX - 25;
addView(heartView, layoutParams);
mExecutorService.execute(() -> {
for (int i = 0; i < getHeight(); i += 5) {
try {
Thread.sleep(50);
} catch (InterruptedException e) {
e.printStackTrace();
}
heartView.setTranslationY(i);
}
});
}
}
结束Peace
|