先放上一张效果
看完开始撸代码
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>
最后奉上源码: 点击下载