爱的贝塞尔曲线之属性动画之美

本文介绍了如何在Android应用中实现动态的心形动画,并通过定制布局进行展示。首先,我们自定义了一个`HeartView`类,用于绘制心形并设置颜色;接着,创建了一个`LoveLayout`类作为心形的容器,利用贝塞尔曲线动态生成心形轨迹;最后,在`MainActivity`中通过定时器不断调用`addHeart()`方法,实现心形的连续动态显示。

摘要生成于 C知道 ,由 DeepSeek-R1 满血版支持, 前往体验 >

先放上一张效果

看完开始撸代码

1.先写一个HeartView继承View(ImageView)用于绘制❤形的View

public class HeartView extends ImageView {

	private Paint paint;
	private Bitmap mBitmap;
	private Random ran;
	private Bitmap mBorder;

	public HeartView(Context context, AttributeSet attrs, int defStyleAttr) {
		super(context, attrs, defStyleAttr);
		init();
	}

	public HeartView(Context context, AttributeSet attrs) {
		this(context, attrs,0);
	}

	public HeartView(Context context) {
		this(context,null);
	}

	private void init() {
		paint = new Paint(Paint.ANTI_ALIAS_FLAG);
		mBitmap = BitmapFactory.decodeResource(getResources(), R.drawable.heart);//拿到心形 和心形边界的Bitmap对象
		mBorder = BitmapFactory.decodeResource(getResources(), R.drawable.heart_border);
		ran = new Random();
	}
	@Override
	protected void onDraw(Canvas canvas) {
		//给view设置一个颜色过滤器
		int mColor=getColor();
		canvas.drawBitmap(mBorder, 0, 0, paint);//先把❤的边界背景画出来
		paint.setColorFilter(new PorterDuffColorFilter(mColor,PorterDuff.Mode.SRC_ATOP));
		canvas.drawBitmap(mBitmap, 0, 0, paint);//画❤
		paint.setColorFilter(null);//重置ColorFilter
	}

	private int getColor() {
		return Color.rgb( ran.nextInt(255), ran.nextInt(255),ran.nextInt(255));
	}
}
上面的代码就是使用了一个ColorFilter来给每个画出来的View上色

2.写一个父布局来盛放产生的❤

public class LoveLayout extends RelativeLayout {


	private int heartWidth;
	private int heartHeight;
	private int mWidth;
	private int mHeight;
	private Random r;


	public LoveLayout(Context context, AttributeSet attrs, int defStyleAttr) {
		super(context, attrs, defStyleAttr);
		init();
	}


	public LoveLayout(Context context, AttributeSet attrs) {
		this(context, attrs,0);
	}

	public LoveLayout(Context context) {
		this(context,null);
	}


	private void init() {
		heartWidth = getResources().getDrawable(R.drawable.heart).getIntrinsicWidth();
		heartHeight = getResources().getDrawable(R.drawable.heart).getIntrinsicHeight();
		r = new Random();
	}

	@Override
	protected void onSizeChanged(int w, int h, int oldw, int oldh) {
		mWidth=w;
		mHeight=h;
	}

	@SuppressLint("NewApi")
	public void addHeart() {
		//这几个PointF不能设成全局的变量,否则每个动画都引用的是一个对象的指向,就会造成轨迹混乱
		PointF p0 = new PointF(mWidth/2-heartWidth/2, mHeight-heartHeight);
		PointF p1 = new PointF(r.nextInt(mWidth), r.nextInt(mHeight/2)+mHeight/2);
		PointF p2 = new PointF(r.nextInt(mWidth), r.nextInt(mHeight/2));
		PointF p3 = new PointF(r.nextInt(mWidth), 0);
		HeartView heart=new HeartView(getContext());
		RelativeLayout.LayoutParams params = new RelativeLayout.LayoutParams(heartWidth, heartHeight);//创建一个布局参数对象
		params.leftMargin=mWidth/2-heartWidth/2;//设置参数底部居中
		params.topMargin=mHeight-heartHeight;
		setAnima(heart,p0,p1,p2,p3);//设置贝塞尔曲线轨迹
		addView(heart,params);//添加到自定义的RelativeLayout
		
	}


	private void setAnima(final View view,final PointF p0,final PointF p1,final PointF p2,final PointF p3) {

		final ValueAnimator mAnimator=ValueAnimator.ofFloat(0,1.0f);//值动画
		mAnimator.setDuration(3000);
		view.setTag(mAnimator);
		mAnimator.addUpdateListener(new ValueAnimator.AnimatorUpdateListener() {
			
			@Override
			public void onAnimationUpdate(ValueAnimator animation) {
					if((ValueAnimator) view.getTag()==animation){
						float t = (Float) animation.getAnimatedValue();
						float floatx=p0.x*(1-t)*(1-t)*(1-t)+3*p1.x*t*(1-t)*(1-t)+3*p2.x*t*t*(1-t)+p3.x*t*t*t;
						float floaty=p0.y*(1-t)*(1-t)*(1-t)+3*p1.y*t*(1-t)*(1-t)+3*p2.y*t*t*(1-t)+p3.y*t*t*t;
						view.setX(floatx);
						view.setY(floaty);
						view.setAlpha(t>0.8?(1-t)*4:1);
					}
					
			}
		});
		if((ValueAnimator) view.getTag()==mAnimator){
				mAnimator.setTarget(view);
				mAnimator.start();
				mAnimator.addListener(new AnimatorListenerAdapter() {

					@Override
					public void onAnimationEnd(Animator animation) {
							removeView(view);
							mAnimator.cancel();
					}
					
				});
		}

	}
	
}
这里new出来一个view之后添加到LoveLayout里面,给其设置属性动画,使用ValueAnimator设置updateListener来改变view属性

其中view的路径坐标是根据贝塞尔曲线方程式算出来的,具体方程可自行百度

3.最后就是在MainActivity里面使用定时器不断调用就可以

public class MainActivity extends ActionBarActivity {
	private LoveLayout mLoveLayout;
	private Timer timer;
	@Override
	protected void onCreate(Bundle savedInstanceState) {
		super.onCreate(savedInstanceState);
		setContentView(R.layout.activity_main);
		mLoveLayout=(LoveLayout) findViewById(R.id.love_layout);
	}


	public void add(View v){
		timer = new Timer();
		timer.scheduleAtFixedRate(new TimerTask() {
			
			@Override
			public void run() {
				mLoveLayout.post(new Runnable() {//post一个Runnable对象到主线程更新UI
					@Override
					public void run() {
						mLoveLayout.addHeart();
					}
				});
			}
		}, 100, 200);
	}
	@Override
	protected void onDestroy() {
		timer.cancel();
		super.onDestroy();
	}

}
View.post(Runnable())可以post一个Runnable代码块到主线程执行

activity_main.xml

<RelativeLayout xmlns:android="http://schemas.android.com/apk/res/android"
    xmlns:tools="http://schemas.android.com/tools"
    android:layout_width="match_parent"
    android:layout_height="match_parent" >

    <com.example.lovebizerheart.view.LoveLayout
        android:id="@+id/love_layout"
        android:layout_width="match_parent"
        android:layout_height="match_parent" >
    </com.example.lovebizerheart.view.LoveLayout>

    <Button
        android:onClick="add"
        android:id="@+id/button1"
        android:layout_width="wrap_content"
        android:layout_height="wrap_content"
        android:layout_alignParentLeft="true"
        android:layout_alignParentTop="true"
        android:layout_marginLeft="17dp"
        android:layout_marginTop="19dp"
        android:minHeight="35dp"
        android:minWidth="60dp"
        android:padding="0dp"
        android:text="添加" />

</RelativeLayout>

最后奉上源码: 点击下载




评论 5
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值