仿小米MIUI卸载APP的动画(NineOld实现)

之前用过NineOld实现几个非常简单的动画后就放在那里了,这几天又重新捡起来自己简单封装了下去研究了下(我一直觉得动画才是Android交互的第一生产力)。demo的Git地址
https://github.com/qtstsq55/SimilarMiUiUnistallAppAnimation
说是仿MIUI的卸载动画,其实还差很远,本人反复研究了它的坐标轨迹,不过本人三角函数真心一般,只能画出个大概模子。(哪位大神知道它的轨迹函数可以告诉我啊!) 简单看下效果图吧
这里写图片描述这里写图片描述这里写图片描述这里写图片描述

这里只贴下主要的代码和大概NineOld的一些东西。

package com.example.xiaomiunistallappanimation;

import com.example.animation.AnimationEngine;
import com.example.animation.AnimationFactory;
import com.example.animation.AnimatorValue;
import com.example.animation.AnimatorValueImplements;
import com.nineoldandroids.animation.Animator;
import com.nineoldandroids.animation.Animator.AnimatorListener;
import com.nineoldandroids.animation.ValueAnimator;
import com.nineoldandroids.animation.ValueAnimator.AnimatorUpdateListener;
import com.example.xiaomiview.UnistallView;
import android.app.Activity;
import android.graphics.Bitmap;
import android.graphics.Color;
import android.os.Bundle;
import android.view.View;
import android.view.View.MeasureSpec;
import android.view.View.OnClickListener;
import android.view.animation.DecelerateInterpolator;
import android.view.animation.LinearInterpolator;
import android.widget.Button;
import android.widget.ImageView;
import android.widget.RelativeLayout;

public class MainActivity extends Activity {
    private ImageView im_unistall;//卸载的图片
    private Button btn_xiaomi;//卸载按钮
    private Button btn_reset;//重置按钮
    private UnistallView view;//绘制卸载碎片的View
    private int colors[]=new int[10];
    private boolean start_0=true;
    private boolean start_1=true;
    private boolean start_2=true;
    @Override
    protected void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        setContentView(R.layout.activity_main);
        initViews();
        initEvents();
        initColors();
    }

    private  Bitmap convertViewToBitmap(View view){
        view.measure(MeasureSpec.makeMeasureSpec(0, MeasureSpec.UNSPECIFIED), MeasureSpec.makeMeasureSpec(0, MeasureSpec.UNSPECIFIED));
        view.layout(0, 0, view.getMeasuredWidth(), view.getMeasuredHeight());
        view.buildDrawingCache();
        Bitmap bitmap = view.getDrawingCache();
        return bitmap;
    }
    //首先是从图片里提取主要的像素,我是从两条对角线中取值。
    private void initColors(){
        Bitmap bitmap=convertViewToBitmap(im_unistall);
        int width=im_unistall.getWidth();
        int height=im_unistall.getHeight();
        for(int i=0;i<colors.length/2;i++){
            if(i==colors.length/2-1){
                colors[i]=bitmap.getPixel((int)(width*i*0.25f-1), (int)(height*i*0.25f-1));
            }else{
                colors[i]=bitmap.getPixel((int)(width*i*0.25f), (int)(height*i*0.25f));
            }
        }
        for(int i=5;i<colors.length;i++){
            if(i==colors.length-1){
                colors[i]=bitmap.getPixel((int)(width*(i-colors.length/2)*0.25f-1), (int)(height-height*(i-colors.length/2)*0.25f));
            }else{
                colors[i]=bitmap.getPixel((int)(width*(i-colors.length/2)*0.25f), (int)(height-height*(i-colors.length/2)*0.25f)-1);
            }
        }

    }


    private void initViews(){
        im_unistall=(ImageView) findViewById(R.id.im_unistall);
        btn_xiaomi=(Button) findViewById(R.id.btn_xiaomi);
        btn_reset=(Button) findViewById(R.id.btn_reset);
        view=(UnistallView) findViewById(R.id.view_add);
    }

    private void initEvents(){
        btn_xiaomi.setOnClickListener(new OnClickListener() {
            @Override
            public void onClick(View arg0) {
               //这里用的是自己封装出来的接口,具体就是支持同时动画,也支持串行动画的一些东东
               //a1,a2是实现最初的抖动动画
               AnimatorValue a1=new AnimatorValueImplements(im_unistall, "TranslationX", 0f,6f,6f,-6f,-6f,0f);
               a1.getAnimator().setRepeatCount(2);
               AnimatorValue a2=new AnimatorValueImplements(im_unistall, "TranslationY", 0f,-6f,6f,6f,-6f,0f);
               a2.getAnimator().setRepeatCount(2);
               //a3,a4是实现接下来的缩小动画
               AnimatorValue a3=new AnimatorValueImplements(im_unistall, "ScaleX", 1f,0.6f,0.2f);
               AnimatorValue a4=new AnimatorValueImplements(im_unistall, "ScaleY", 1f,0.6f,0.2f);
               a3.before(a2);                        
               a3.getAnimator().addUpdateListener(new AnimatorUpdateListener() {
                @Override
                public void onAnimationUpdate(ValueAnimator arg0) {
                    if(start_0){
                        //开始的时候先加20个碎片
                        view.startAnimation(20,colors,im_unistall.getX()+ im_unistall.getWidth()/2,im_unistall.getY()+im_unistall.getHeight()/2);
                        start_0=false;
                    }else if((Float)(arg0.getAnimatedValue("ScaleX"))>0.3f&&(Float)(arg0.getAnimatedValue("ScaleX"))<0.4f){
                        if(start_1){
                           //缩小到30%再加30个碎片
                           view.addBall(30, im_unistall.getX()+im_unistall.getWidth()/2,im_unistall.getY()+im_unistall.getHeight()/2);
                           start_1=false; 
                        }
                    }else if((Float)(arg0.getAnimatedValue("ScaleX"))>0.2f&&(Float)(arg0.getAnimatedValue("ScaleX"))<0.3f){
                        if(start_2){
                            //缩小到20%再加40个碎片
                            view.addBall(40, im_unistall.getX()+im_unistall.getWidth()/2,im_unistall.getY()+im_unistall.getHeight()/2);
                            start_2=false; 
                       }
                    }
                }
            });
               //开始串行执行动画(a1,a2一起,a3,a4一起,a3在a2后面)
               AnimationEngine engine= AnimationFactory.getInstance().createEngine();
               engine.startTogetherByLink(1000,new AnimatorListener() {

                @Override
                public void onAnimationStart(Animator arg0) {

                }

                @Override
                public void onAnimationRepeat(Animator arg0) {

                }

                @Override
                public void onAnimationEnd(Animator arg0) {
                    im_unistall.setVisibility(View.INVISIBLE);
                }

                @Override
                public void onAnimationCancel(Animator arg0) {

                }
            },a1,a2,a3,a4);
            }
        });
        btn_reset.setOnClickListener(new OnClickListener() {
            @Override
            public void onClick(View arg0) {
                //重置的一些操作
                start_0=true;
                start_1=true;
                start_2=true;
                view.clearBalls();
                im_unistall.setVisibility(View.VISIBLE);
                AnimatorValue a1=new AnimatorValueImplements(im_unistall, "ScaleX", 0.2f,0.6f,1f);
                AnimatorValue a2=new AnimatorValueImplements(im_unistall, "ScaleY", 0.2f,0.6f,1f);
                AnimationFactory.getInstance().createEngine().startTogether(1000, null, a1,a2);
            }
        });
    }



}

这是绘制碎片的View代码

package com.example.xiaomiview;

import java.util.ArrayList;
import android.content.Context;
import android.graphics.Canvas;
import android.graphics.Paint;
import android.graphics.drawable.ShapeDrawable;
import android.graphics.drawable.shapes.OvalShape;
import android.util.AttributeSet;
import android.view.View;

import com.example.animation.AnimationFactory;
import com.example.animation.AnimatorValue;
import com.example.animation.AnimatorValueImplements;
import com.nineoldandroids.animation.TypeEvaluator;
import com.nineoldandroids.animation.ValueAnimator;

public class UnistallView  extends View implements ValueAnimator.AnimatorUpdateListener{

     private ArrayList<ShapeHolder> balls = new ArrayList<ShapeHolder>();
     private ArrayList<AnimatorValue> animatorValues = new ArrayList<AnimatorValue>();
     private AnimatorValue bounceAnim = null;
     private BallXYHolder ballHolder = null;
     private int colors[];

     public UnistallView(Context context) {
        super(context);
     }
    public UnistallView(Context context,AttributeSet set) {
        super(context,set);
     }

     @SuppressWarnings("unchecked")
    private void createAnimation() {
         for(int i=0,j=0;i<balls.size();i++,j++){
             float X=balls.get(i).getX();
             float Y= balls.get(i).getY();
             float deptX,deptY,deptAlpha;
             if(i%2==0){
                  deptX=-i*2; 
             }else{
                  deptX=i*2; 
             }
             int b=balls.size()/2;
             int a=j/2+5;
             deptY=Y/16;
             //碎片分10个动画步骤
             Object holder[]=new Object[10];
             deptAlpha=255/holder.length;
             //抛物线路径,大概是a*sin(ax),定义域0到5π/4和a*sin(bx),b递增,定义域0到π/2.
             if(i>balls.size()/2-1){
                 deptX=deptX/2;
                 for(int k=0;k<holder.length;k++){
                         holder[k]= new XYAHolder(X+deptX*k,(float) (Y-a*(Math.sin(b*(Math.PI/b/16*k)))*deptY),255-(k+1)*deptAlpha);
                 }
             }else{
                 for(int k=0;k<holder.length;k++){
                     if(k<holder.length-1){
                         holder[k]= new XYAHolder(X+deptX*k,(float) (Y-a*(Math.sin(a*(Math.PI/a/8*k)))*deptY),255-(k+1)*deptAlpha);
                     }else{
                         holder[k]= new XYAHolder(X+deptX*k,(float) (Y-a*(Math.sin(a*(Math.PI/a)+Math.PI/4))*deptY),255-(k+1)*deptAlpha);
                     }
                 }
             }
             ballHolder = new BallXYHolder(balls.get(i));
             bounceAnim = new AnimatorValueImplements(ballHolder, new XYEvaluator(),"XYA", holder);
             animatorValues.add(bounceAnim);
         }
     }

     public void startAnimation(int num,int colors[],float x,float y) {
         this.colors=colors;
         for(int i=0;i<num;i++){
            double r=Math.random()-0.5; 
            balls.add(createBall((float)(x+r*8), (float)(y+r*8),255f));
         }
         createAnimation();
//         for(int i=0;i<animatorValues.size();i++){
//             if(i>0&&i%4==0){
//               animatorValues.get(i).before(animatorValues.get(i-1));
//               animatorValues.get(i).getAnimator().addUpdateListener(this);
//             }
//         }
//         AnimatorValue[] values = new AnimatorValue[animatorValues.size()];
//         animatorValues.toArray(values);
         animatorValues.get(0).getAnimator().addUpdateListener(this);
         AnimationFactory.getInstance().createEngine().startTogether(5000, null,animatorValues);
     }
     //后续增加的碎片球,抛物线方程应该和初始的碎片不一致,但是这里就将就一下吧
     public void addBall(int num,float x,float y){
         ArrayList<AnimatorValue> animatorValues = new ArrayList<AnimatorValue>();
         for(int i=0,j=0;i<num;i++,j++){
             double r=Math.random()-0.5;
             ShapeHolder ball=createBall((float)(x+r*16), (float)(y+r*16),255f);
             balls.add(ball);
             float X=ball.getX();
             float Y= ball.getY();
             float deptX,deptY,deptAlpha;
             if(i%2==0){
                  deptX=-i*2; 
             }else{
                  deptX=i*2; 
             }
             int a=j/2+5;
             int b=balls.size()/2;
             deptY=Y/16;
             Object holder[]=new Object[10];
             deptAlpha=255/holder.length;
             if(i>balls.size()/2-1){
                 deptX=deptX/2;
                 for(int k=0;k<holder.length;k++){
                         holder[k]= new XYAHolder(X+deptX*k,(float) (Y-a*(Math.sin(b*(Math.PI/b/16*k)))*deptY),255-(k+1)*deptAlpha);
                 }
             }else{
                 for(int k=0;k<holder.length;k++){
                     if(k<holder.length-1){
                         holder[k]= new XYAHolder(X+deptX*k,(float) (Y-a*(Math.sin(a*(Math.PI/a/8*k)))*deptY),255-(k+1)*deptAlpha);
                     }else{
                         holder[k]= new XYAHolder(X+deptX*k,(float) (Y-a*(Math.sin(a*(Math.PI/a)+Math.PI/4))*deptY),255-(k+1)*deptAlpha);
                     }
                 }
             }
             ballHolder = new BallXYHolder(ball);
             bounceAnim = new AnimatorValueImplements(ballHolder, new XYEvaluator(),"XYA",holder);
             animatorValues.add(bounceAnim);
         }
//         for(int i=0;i<animatorValues.size();i++){
//             if(i>0&&i%4==0){
//               animatorValues.get(i).before(animatorValues.get(i-1));
//               animatorValues.get(i).getAnimator().addUpdateListener(this);
//             }
//         }
//         AnimatorValue[] values = new AnimatorValue[animatorValues.size()];
//         animatorValues.toArray(values);
         animatorValues.get(0).getAnimator().addUpdateListener(this);
         AnimationFactory.getInstance().createEngine().startTogether(5000, null,animatorValues);
     }



     private ShapeHolder createBall(float x, float y,float alpha) {
         OvalShape circle = new OvalShape();
         double random=Math.random()-0.5; 
         float radio=(float)(20+random*16);
         circle.resize(radio,radio);
         ShapeDrawable drawable = new ShapeDrawable(circle);
         ShapeHolder shapeHolder = new ShapeHolder(drawable);
         shapeHolder.setX(x - radio/2);
         shapeHolder.setY(y - radio/2);
         shapeHolder.setAlpha(alpha);
         Paint paint = drawable.getPaint(); 
         int  color_random=(int) (Math.random()*colors.length);
         paint.setColor(colors[color_random]);
         shapeHolder.setPaint(paint);
         return shapeHolder;
     }

     @Override
     protected void onDraw(Canvas canvas) {
         for (int i = 0; i < balls.size(); ++i) {
             ShapeHolder shapeHolder = balls.get(i);
             canvas.save();
             canvas.translate(shapeHolder.getX(), shapeHolder.getY());
             shapeHolder.getShape().draw(canvas);
             canvas.restore();
         }
     }

     public void onAnimationUpdate(ValueAnimator animation) {
         invalidate();
     }

     public void clearBalls(){
         balls.clear();
         animatorValues.clear();
     }

     //匀速插值,最后onDraw中绘制的位置和透明度就是从这里计算出来的
     public class XYEvaluator implements TypeEvaluator {
         public Object evaluate(float fraction, Object startValue, Object endValue) {
             XYAHolder startXYA = (XYAHolder) startValue;
             XYAHolder endXYA = (XYAHolder) endValue;
             return new XYAHolder(startXYA.getX() + fraction * (endXYA.getX() - startXYA.getX()),
                     startXYA.getY() + fraction * (endXYA.getY() - startXYA.getY()),  startXYA.getAlpha() + fraction * (endXYA.getAlpha() - startXYA.getAlpha()));
         }
     }

     public class XYAHolder {
         private float mX;
         private float mY;
         private float  mAlpha;
         public XYAHolder(float x, float y,float alpha) {
             mX = x;
             mY = y;
             mAlpha=alpha;
         }

         public float getX() {
             return mX;
         }

         public void setX(float x) {
             mX = x;
         }

         public float getY() {
             return mY;
         }

         public void setY(float y) {
             mY = y;
         }

         public float getAlpha() {
             return mAlpha;
         }

         public void setAlpha(int alpha) {
             mAlpha=alpha;
         }
     }

     public class BallXYHolder {

         private ShapeHolder mBall;

         public BallXYHolder(ShapeHolder ball) {
             mBall = ball;
         }

         public void setXYA(XYAHolder xyaHolder) {
             mBall.setX(xyaHolder.getX());
             mBall.setY(xyaHolder.getY());
             mBall.setAlpha(xyaHolder.getAlpha());
         }

         public XYAHolder getXYA() {
             return new XYAHolder(mBall.getX(), mBall.getY(),mBall.getAlpha());
         }

     }

}

主要的部分就是这两个,还有就是自己封装出来的一些东西,算是抛砖引玉吧。
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值