Periscope点赞效果,红心乱飞

本文介绍了一种使用Android自定义RelativeLayout实现动态加载ImageView,并结合ObjectAnimator和ValueAnimator创建爱心移动动画的方法。该方案通过贝塞尔曲线计算移动路径,实现平滑过渡。

先来个效果图



这个动画效果主要几个关注点是:

一、自定义的RelativeLayout中动态加载ImageView

二、刚开始的三个还没移动的动画效果anpha和scaleX,scaleY ,用ObjectAnimator加载

三、红心移动效果,运用了ValueAnimator的TypeEvalutors(估值器) 和addUpdateListener监听,在TypeEvalutors获取了贝塞尔曲线运动时所随机移动的每一个PointF,

然后在监听中获取这个PointF,赋值给imageView的对象,这样就实现了imageView的对象不的x,y坐标不停的改变,实现移动效果

四、最后,在动画结束后remove(imageView)移除对象


详细代码:

先来个layout的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"
    android:paddingBottom="@dimen/activity_vertical_margin"
    android:paddingLeft="@dimen/activity_horizontal_margin"
    android:paddingRight="@dimen/activity_horizontal_margin"
    android:paddingTop="@dimen/activity_vertical_margin"
    tools:context=".MainActivity" >


    <Button
        android:id="@+id/button1"
        android:layout_width="wrap_content"
        android:layout_height="wrap_content"
        android:layout_alignParentLeft="true"
        android:layout_alignParentTop="true"
        android:text="开始" 
        android:onClick="startClick"/>
    
    <com.example.test_periscopedemo.MyLayout 
         android:id="@+id/myLayout"
        android:layout_width="300dp"
        android:layout_height="300dp"
        android:layout_below="@id/button1"
        android:background="@android:color/holo_green_light"
        />


</RelativeLayout>
 

主Activity:


package com.example.test_periscopedemo;

import android.os.Bundle;
import android.app.Activity;
import android.view.Menu;
import android.view.View;

public class MainActivity extends Activity {
	MyLayout myLayout;
    @Override
    protected void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        setContentView(R.layout.activity_main);
        myLayout = (MyLayout) findViewById(R.id.myLayout);
    }


    public void startClick(View view){
    	myLayout.addLayout();
    }
    
}

自定义的 RelativeLayout,这个主要的实现类


package com.example.test_periscopedemo;

import java.util.Random;

import android.animation.Animator;
import android.animation.AnimatorListenerAdapter;
import android.animation.AnimatorSet;
import android.animation.ObjectAnimator;
import android.animation.ValueAnimator;
import android.animation.ValueAnimator.AnimatorUpdateListener;
import android.content.Context;
import android.graphics.BitmapFactory;
import android.graphics.PointF;
import android.graphics.drawable.Drawable;
import android.util.AttributeSet;
import android.view.View;
import android.view.animation.AnimationSet;
import android.widget.ImageView;
import android.widget.RelativeLayout;

public class MyLayout extends RelativeLayout{
	
	Random random;
	Drawable blue,red,yellow;
	LayoutParams lp;
	int dWidth,dHeight;
	int mWidth,mHeight;
	
	Drawable[] drawables = new Drawable[3];

	public MyLayout(Context context, AttributeSet attrs) {
		super(context, attrs);
		
		init();
	}
	@Override
	protected void onMeasure(int widthMeasureSpec, int heightMeasureSpec) {
		super.onMeasure(widthMeasureSpec, heightMeasureSpec);
		mWidth = getMeasuredWidth();
		mHeight = getMeasuredHeight();
	}

	private void init() {
		
		dWidth = BitmapFactory.decodeResource(getResources(), R.drawable.pl_blue).getWidth();
		dHeight = BitmapFactory.decodeResource(getResources(), R.drawable.pl_blue).getHeight();
		
		random = new Random();
		blue = getResources().getDrawable(R.drawable.pl_blue);
		red = getResources().getDrawable(R.drawable.pl_red);
		yellow = getResources().getDrawable(R.drawable.pl_yellow);
		
		drawables[0] = blue;
		drawables[1] = red;
		drawables[2] = yellow;
		
		lp = new LayoutParams(dWidth, dHeight);
		lp.addRule(CENTER_HORIZONTAL, TRUE); // 这里的TRUE 要注意 不是true
		lp.addRule(ALIGN_PARENT_BOTTOM, TRUE);
	}
	public void addLayout(){
		ImageView iv =new ImageView(getContext());
		iv.setLayoutParams(lp);		
		addView(iv);		
		iv.setImageDrawable(drawables[random.nextInt(3)]);
		getAnimator(iv);
	}
	
	
	private void getAnimator(final View iv){	
		ValueAnimator va = ValueAnimator.ofObject(new BezierEvaluator(new PointF(mWidth/2-dWidth/2,mHeight-dHeight), getPointF()), new PointF(
				(mWidth - dWidth) / 2, mHeight - dHeight),
				new PointF(random.nextInt(getWidth()), 0));// 随机		
		va.addUpdateListener(new AnimatorUpdateListener() {
			@Override
			public void onAnimationUpdate(ValueAnimator animation) {
				PointF pointF = (PointF) animation.getAnimatedValue();
				iv.setX(pointF.x);
				iv.setY(pointF.y);
				
				// 这里偷个懒,顺便做一个alpha动画,这样alpha渐变也完成啦
				iv.setAlpha(1 - animation.getAnimatedFraction());
			}
		});		
	
		
		
		ObjectAnimator alpha = ObjectAnimator.ofFloat(iv, "alpha", 0.2f,1.0f);		
		ObjectAnimator scaleX = ObjectAnimator.ofFloat(iv, "scaleX", 0.2f,1.0f);
		ObjectAnimator scaleY = ObjectAnimator.ofFloat(iv, "scaleY", 0.2f,1.0f);
		
		AnimatorSet set = new AnimatorSet();
		set.setDuration(3000);
		set.playTogether(alpha,scaleX,scaleY);
		set.play(va).after(alpha);
		set.setTarget(iv);
		set.addListener(new AnimatorListenerAdapter(){
			@Override
			public void onAnimationEnd(Animator animation) {
				super.onAnimationEnd(animation);
				System.out.println("结束");
				removeView(iv);
			}
		});
		set.start();
	}
	
	private PointF getPointF(){
		
		PointF p = new PointF();
		p.x = random.nextInt(mWidth);
		p.y = 50;
		return p;
	}
	
	

}


自定义的TypeEvaluator类,根据属性的开始、结束值与TimeInterpolation计算出的因子计算出当前时间的属性值,即在动画执行的不同的时间,返回不同的值,就在这利用贝塞尔曲线工式,获取一个随机移动的点的坐标做为返回值

package com.example.test_periscopedemo;

import android.animation.TypeEvaluator;
import android.graphics.PointF;

//我们自定义一个BezierEvaluator 实现 TypeEvaluator
//由于我们view的移动需要控制x y 所以就传入PointF 作为参数,是不是感觉完全契合??
public class BezierEvaluator implements TypeEvaluator<PointF> {


  private PointF pointF1;//途径的两个点 
  private PointF pointF2;
  public BezierEvaluator(PointF pointF1,PointF pointF2){
      this.pointF1 = pointF1;
      this.pointF2 = pointF2;
      System.out.println("p1="+pointF1);
      System.out.println("p2="+pointF2);
  }
  @Override
  public PointF evaluate(float time, PointF startValue,
                         PointF endValue) {

      float timeLeft = 1.0f - time;
      PointF point = new PointF();//结果

      PointF point0 = (PointF)startValue;//起点

      PointF point3 = (PointF)endValue;//终点
      System.out.println("time"+time);
      System.out.println("p0="+point0);
      System.out.println("p3="+point3);
      //代入公式 
      point.x = timeLeft * timeLeft * timeLeft * (point0.x)
              + 3 * timeLeft * timeLeft * time * (pointF1.x)
              + 3 * timeLeft * time * time * (pointF2.x)
              + time * time * time * (point3.x);

      point.y = timeLeft * timeLeft * timeLeft * (point0.y)
              + 3 * timeLeft * timeLeft * time * (pointF1.y)
              + 3 * timeLeft * time * time * (pointF2.y)
              + time * time * time * (point3.y);
      return point;
  }
}


全部完成



评论
成就一亿技术人!
拼手气红包6.0元
还能输入1000个字符
 
红包 添加红包
表情包 插入表情
 条评论被折叠 查看
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值