贝萨尔曲线实现Periscope心形点赞动画效果

先来个效果图



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

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

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

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

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

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


详细代码:

先来个layout的xml

  1. <RelativeLayout xmlns:android=“http://schemas.android.com/apk/res/android”  
  2.     xmlns:tools=”http://schemas.android.com/tools”  
  3.     android:layout_width=”match_parent”  
  4.     android:layout_height=”match_parent”  
  5.     android:paddingBottom=”@dimen/activity_vertical_margin”  
  6.     android:paddingLeft=”@dimen/activity_horizontal_margin”  
  7.     android:paddingRight=”@dimen/activity_horizontal_margin”  
  8.     android:paddingTop=”@dimen/activity_vertical_margin”  
  9.     tools:context=”.MainActivity” >  
  10.   
  11.   
  12.     <Button  
  13.         android:id=”@+id/button1”  
  14.         android:layout_width=”wrap_content”  
  15.         android:layout_height=”wrap_content”  
  16.         android:layout_alignParentLeft=”true”  
  17.         android:layout_alignParentTop=”true”  
  18.         android:text=”开始”   
  19.         android:onClick=”startClick”/>  
  20.       
  21.     <com.example.test_periscopedemo.MyLayout   
  22.          android:id=”@+id/myLayout”  
  23.         android:layout_width=”300dp”  
  24.         android:layout_height=”300dp”  
  25.         android:layout_below=”@id/button1”  
  26.         android:background=”@android:color/holo_green_light”  
  27.         />  
  28.   
  29.   
  30. </RelativeLayout>  
  31.    
<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:


  1. package com.example.test_periscopedemo;  
  2.   
  3. import android.os.Bundle;  
  4. import android.app.Activity;  
  5. import android.view.Menu;  
  6. import android.view.View;  
  7.   
  8. public class MainActivity extends Activity {  
  9.     MyLayout myLayout;  
  10.     @Override  
  11.     protected void onCreate(Bundle savedInstanceState) {  
  12.         super.onCreate(savedInstanceState);  
  13.         setContentView(R.layout.activity_main);  
  14.         myLayout = (MyLayout) findViewById(R.id.myLayout);  
  15.     }  
  16.   
  17.   
  18.     public void startClick(View view){  
  19.         myLayout.addLayout();  
  20.     }  
  21.       
  22. }  
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,这个主要的实现类


  1. package com.example.test_periscopedemo;  
  2.   
  3. import java.util.Random;  
  4.   
  5. import android.animation.Animator;  
  6. import android.animation.AnimatorListenerAdapter;  
  7. import android.animation.AnimatorSet;  
  8. import android.animation.ObjectAnimator;  
  9. import android.animation.ValueAnimator;  
  10. import android.animation.ValueAnimator.AnimatorUpdateListener;  
  11. import android.content.Context;  
  12. import android.graphics.BitmapFactory;  
  13. import android.graphics.PointF;  
  14. import android.graphics.drawable.Drawable;  
  15. import android.util.AttributeSet;  
  16. import android.view.View;  
  17. import android.view.animation.AnimationSet;  
  18. import android.widget.ImageView;  
  19. import android.widget.RelativeLayout;  
  20.   
  21. public class MyLayout extends RelativeLayout{  
  22.       
  23.     Random random;  
  24.     Drawable blue,red,yellow;  
  25.     LayoutParams lp;  
  26.     int dWidth,dHeight;  
  27.     int mWidth,mHeight;  
  28.       
  29.     Drawable[] drawables = new Drawable[3];  
  30.   
  31.     public MyLayout(Context context, AttributeSet attrs) {  
  32.         super(context, attrs);  
  33.           
  34.         init();  
  35.     }  
  36.     @Override  
  37.     protected void onMeasure(int widthMeasureSpec, int heightMeasureSpec) {  
  38.         super.onMeasure(widthMeasureSpec, heightMeasureSpec);  
  39.         mWidth = getMeasuredWidth();  
  40.         mHeight = getMeasuredHeight();  
  41.     }  
  42.   
  43.     private void init() {  
  44.           
  45.         dWidth = BitmapFactory.decodeResource(getResources(), R.drawable.pl_blue).getWidth();  
  46.         dHeight = BitmapFactory.decodeResource(getResources(), R.drawable.pl_blue).getHeight();  
  47.           
  48.         random = new Random();  
  49.         blue = getResources().getDrawable(R.drawable.pl_blue);  
  50.         red = getResources().getDrawable(R.drawable.pl_red);  
  51.         yellow = getResources().getDrawable(R.drawable.pl_yellow);  
  52.           
  53.         drawables[0] = blue;  
  54.         drawables[1] = red;  
  55.         drawables[2] = yellow;  
  56.           
  57.         lp = new LayoutParams(dWidth, dHeight);  
  58.         lp.addRule(CENTER_HORIZONTAL, TRUE); // 这里的TRUE 要注意 不是true  
  59.         lp.addRule(ALIGN_PARENT_BOTTOM, TRUE);  
  60.     }  
  61.     public void addLayout(){  
  62.         ImageView iv =new ImageView(getContext());  
  63.         iv.setLayoutParams(lp);       
  64.         addView(iv);          
  65.         iv.setImageDrawable(drawables[random.nextInt(3)]);  
  66.         getAnimator(iv);  
  67.     }  
  68.       
  69.       
  70.     private void getAnimator(final View iv){      
  71.         ValueAnimator va = ValueAnimator.ofObject(new BezierEvaluator(new PointF(mWidth/2-dWidth/2,mHeight-dHeight), getPointF()), new PointF(  
  72.                 (mWidth - dWidth) / 2, mHeight - dHeight),  
  73.                 new PointF(random.nextInt(getWidth()), 0));// 随机          
  74.         va.addUpdateListener(new AnimatorUpdateListener() {  
  75.             @Override  
  76.             public void onAnimationUpdate(ValueAnimator animation) {  
  77.                 PointF pointF = (PointF) animation.getAnimatedValue();  
  78.                 iv.setX(pointF.x);  
  79.                 iv.setY(pointF.y);  
  80.                   
  81.                 // 这里偷个懒,顺便做一个alpha动画,这样alpha渐变也完成啦  
  82.                 iv.setAlpha(1 - animation.getAnimatedFraction());  
  83.             }  
  84.         });       
  85.       
  86.           
  87.           
  88.         ObjectAnimator alpha = ObjectAnimator.ofFloat(iv, ”alpha”0.2f,1.0f);        
  89.         ObjectAnimator scaleX = ObjectAnimator.ofFloat(iv, ”scaleX”0.2f,1.0f);  
  90.         ObjectAnimator scaleY = ObjectAnimator.ofFloat(iv, ”scaleY”0.2f,1.0f);  
  91.           
  92.         AnimatorSet set = new AnimatorSet();  
  93.         set.setDuration(3000);  
  94.         set.playTogether(alpha,scaleX,scaleY);  
  95.         set.play(va).after(alpha);  
  96.         set.setTarget(iv);  
  97.         set.addListener(new AnimatorListenerAdapter(){  
  98.             @Override  
  99.             public void onAnimationEnd(Animator animation) {  
  100.                 super.onAnimationEnd(animation);  
  101.                 System.out.println(”结束”);  
  102.                 removeView(iv);  
  103.             }  
  104.         });  
  105.         set.start();  
  106.     }  
  107.       
  108.     private PointF getPointF(){  
  109.           
  110.         PointF p = new PointF();  
  111.         p.x = random.nextInt(mWidth);  
  112.         p.y = 50;  
  113.         return p;  
  114.     }  
  115.       
  116.       
  117.   
  118. }  
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计算出的因子计算出当前时间的属性值,即在动画执行的不同的时间,返回不同的值,就在这利用贝塞尔曲线工式,获取一个随机移动的点的坐标做为返回值

  1. package com.example.test_periscopedemo;  
  2.   
  3. import android.animation.TypeEvaluator;  
  4. import android.graphics.PointF;  
  5.   
  6. //我们自定义一个BezierEvaluator 实现 TypeEvaluator  
  7. //由于我们view的移动需要控制x y 所以就传入PointF 作为参数,是不是感觉完全契合??  
  8. public class BezierEvaluator implements TypeEvaluator<PointF> {  
  9.   
  10.   
  11.   private PointF pointF1;//途径的两个点   
  12.   private PointF pointF2;  
  13.   public BezierEvaluator(PointF pointF1,PointF pointF2){  
  14.       this.pointF1 = pointF1;  
  15.       this.pointF2 = pointF2;  
  16.       System.out.println(”p1=”+pointF1);  
  17.       System.out.println(”p2=”+pointF2);  
  18.   }  
  19.   @Override  
  20.   public PointF evaluate(float time, PointF startValue,  
  21.                          PointF endValue) {  
  22.   
  23.       float timeLeft = 1.0f - time;  
  24.       PointF point = new PointF();//结果  
  25.   
  26.       PointF point0 = (PointF)startValue;//起点  
  27.   
  28.       PointF point3 = (PointF)endValue;//终点  
  29.       System.out.println(”time”+time);  
  30.       System.out.println(”p0=”+point0);  
  31.       System.out.println(”p3=”+point3);  
  32.       //代入公式   
  33.       point.x = timeLeft * timeLeft * timeLeft * (point0.x)  
  34.               + 3 * timeLeft * timeLeft * time * (pointF1.x)  
  35.               + 3 * timeLeft * time * time * (pointF2.x)  
  36.               + time * time * time * (point3.x);  
  37.   
  38.       point.y = timeLeft * timeLeft * timeLeft * (point0.y)  
  39.               + 3 * timeLeft * timeLeft * time * (pointF1.y)  
  40.               + 3 * timeLeft * time * time * (pointF2.y)  
  41.               + time * time * time * (point3.y);  
  42.       return point;  
  43.   }  
  44. }  
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;
  }
}


全部完成



  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值