之前用过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());
}
}
}
主要的部分就是这两个,还有就是自己封装出来的一些东西,算是抛砖引玉吧。