Android 中的动画可以分为以下几类:

  • 逐帧动画
  • 补间动画
  • 属性动画

一、逐帧动画

逐帧动画的原理就是让一系列的静态图片依次播放,利用人眼“视觉暂留”的原理,实现动画。

利用 xml 实现逐帧动画

逐帧动画通常是采用 XML 资源进行定义的,需要在 <animation-list .../> 标签下使用 <item .../> 子元素标签定义动画的全部帧,并指定各帧的持续时间。
定义逐帧动画的语法格式如下:

<animation-list xmlns:android="http://schemas.android.com/apk/res/android"android:oneshot="true|false"><item android:drawable="" android:duration=""/></animation-list>

其中android:oneshot控制该动画是否循环播放。如果为true,动画将不会循环播放, 否则该动画将会循环播放

步骤:

1、res/drawable 下新建 xml 文件,这里定义动画的每一帧,素材图片放到 drawable 下。

frame_animation.xml

<?xml version="1.0" encoding="utf-8"?>
<animation-list xmlns:android="http://schemas.android.com/apk/res/android"android:oneshot="true"><item android:drawable="@drawable/frame01" android:duration="100"/><item android:drawable="@drawable/frame02" android:duration="100"/><item android:drawable="@drawable/frame03" android:duration="100"/><item android:drawable="@drawable/frame04" android:duration="100"/><item android:drawable="@drawable/frame05" android:duration="100"/><item android:drawable="@drawable/frame06" android:duration="100"/><item android:drawable="@drawable/frame07" android:duration="100"/><item android:drawable="@drawable/frame08" android:duration="100"/><item android:drawable="@drawable/frame09" android:duration="100"/><item android:drawable="@drawable/frame10" android:duration="100"/><item android:drawable="@drawable/frame11" android:duration="100"/><item android:drawable="@drawable/frame12" android:duration="100"/><item android:drawable="@drawable/frame13" android:duration="100"/><item android:drawable="@drawable/frame14" android:duration="100"/><item android:drawable="@drawable/frame15" android:duration="100"/><item android:drawable="@drawable/frame16" android:duration="100"/><item android:drawable="@drawable/frame17" android:duration="100"/><item android:drawable="@drawable/frame18" android:duration="100"/></animation-list>

2、布局中将 AnimationDrawable 对象直接作为背景

<?xml version="1.0" encoding="utf-8"?>
<RelativeLayout xmlns:android="http://schemas.android.com/apk/res/android"android:orientation="vertical" android:layout_width="match_parent"android:layout_height="match_parent"><ImageViewandroid:layout_marginTop="50dp"android:layout_centerHorizontal="true"android:id="@+id/frame_image"android:layout_width="200dp"android:layout_height="200dp"android:background="@drawable/frame_animation"/><LinearLayoutandroid:layout_alignParentBottom="true"android:layout_width="match_parent"android:layout_height="wrap_content"android:orientation="horizontal"android:layout_margin="5dp"android:layout_marginBottom="20dp"><Buttonandroid:id="@+id/frame_start"android:layout_weight="1"android:layout_width="wrap_content"android:layout_height="wrap_content"android:onClick="start"android:text="start"/><Buttonandroid:id="@+id/frame_stop"android:layout_weight="1"android:layout_width="wrap_content"android:layout_height="wrap_content"android:onClick="stop"android:text="stop"/></LinearLayout>
</RelativeLayout>

3、Activity 中控制播放与停止

public class FrameAnimation extends AppCompatActivity {ImageView frame_image;AnimationDrawable animationDrawable;@Overrideprotected void onCreate(@Nullable Bundle savedInstanceState) {super.onCreate(savedInstanceState);setContentView(R.layout.activity_frame_animation);frame_image = findViewById(R.id.frame_image);// 获取 AnimationDrawable 对象animationDrawable = (AnimationDrawable) frame_image.getBackground();}public void start(View view){// 开始播放animationDrawable.start();}public void stop(View view){//停止播放animationDrawable.stop();}
}

利用 Java 代码实现逐帧动画

通过代码实现逐帧动画,就不用在布局中设置 backgroud。

1、首先要创建 AnimationDrawable 对象

    animationDrawable = new AnimationDrawable();for (int i = 1; i < 10; i ++ ){int id = getResources().getIdentifier("frame0" + i, "drawable", getPackageName());Drawable drawable = getResources().getDrawable(id);animationDrawable.addFrame(drawable, 100);}for (int i = 10; i < 19; i ++){int id = getResources().getIdentifier("frame" + i, "drawable", getPackageName());Drawable drawable = getResources().getDrawable(id);animationDrawable.addFrame(drawable, 100);}

2、控制播放和停止

 public void start(View view){// 开始播放// animationDrawable.start();animationDrawable.setOneShot(true);frame_image.setImageDrawable(animationDrawable);// 获取资源对象animationDrawable.stop();// 特别注意:在动画start()之前要先stop(),不然在第一次动画之后会停在最后一帧,这样动画就只会触发一次animationDrawable.start();// 启动动画}public void stop(View view){//停止播放// animationDrawable.stop();animationDrawable.setOneShot(true);frame_image.setImageDrawable(animationDrawable);animationDrawable.stop();}

完整代码:

public class FrameAnimation extends AppCompatActivity {ImageView frame_image;AnimationDrawable animationDrawable;@Overrideprotected void onCreate(@Nullable Bundle savedInstanceState) {super.onCreate(savedInstanceState);setContentView(R.layout.activity_frame_animation);frame_image = findViewById(R.id.frame_image);// 获取 AnimationDrawable 对象// animationDrawable = (AnimationDrawable) frame_image.getBackground();animationDrawable = new AnimationDrawable();for (int i = 1; i < 10; i ++ ){int id = getResources().getIdentifier("frame0" + i, "drawable", getPackageName());Drawable drawable = getResources().getDrawable(id);animationDrawable.addFrame(drawable, 100);}for (int i = 10; i < 19; i ++){int id = getResources().getIdentifier("frame" + i, "drawable", getPackageName());Drawable drawable = getResources().getDrawable(id);animationDrawable.addFrame(drawable, 100);}}public void start(View view){// 开始播放// animationDrawable.start();animationDrawable.setOneShot(true);frame_image.setImageDrawable(animationDrawable);// 获取资源对象animationDrawable.stop();// 特别注意:在动画start()之前要先stop(),不然在第一次动画之后会停在最后一帧,这样动画就只会触发一次animationDrawable.start();// 启动动画}public void stop(View view){//停止播放// animationDrawable.stop();animationDrawable.setOneShot(true);frame_image.setImageDrawable(animationDrawable);animationDrawable.stop();}
}

二、补间动画

补间动画就是指开发者指定动画的开始、动画的结束的"关键帧",而动画变化的"中间帧"由系统计算,并补齐。
补间动画有四种:

  • 淡入淡出: alpha
  • 位移:translate
  • 缩放:scale
  • 旋转: rotate

1、 XML 形式补间动画

补间动画一般也是通过 xml 来实现,对于 xml 形式补间动画的定义,也是需要在 res/anim/ 文件夹下定义动画资源,如:
alpha_anim.xml

<?xml version="1.0" encoding="utf-8"?>
<alpha xmlns:android="http://schemas.android.com/apk/res/android"android:duration="1000"android:fromAlpha="1.0"android:interpolator="@android:anim/accelerate_decelerate_interpolator"android:toAlpha="0.0" />

interpolator 代表插值器,主要作用是可以控制动画的变化速率,可以通过 @android:anim 来选择不同的插值器。

scale_anim.xml

<?xml version="1.0" encoding="utf-8"?>
<scale xmlns:android="http://schemas.android.com/apk/res/android"android:duration="1000"android:fromXScale="0.0"android:fromYScale="0.0"android:pivotX="50%"android:pivotY="50%"android:toXScale="1.0"android:toYScale="1.0"/>

translate_anim.xml

<?xml version="1.0" encoding="utf-8"?>
<rotate xmlns:android="http://schemas.android.com/apk/res/android"android:fromDegree="0"android:toDegree="1800"android:pivotX = "50%"android:pivotY="50%"android:duration = "3000"
/>

pivot 这个属性主要是在translate 和 scale 动画中,这两种动画都牵扯到view 的“物理位置“发生变化,所以需要一个参考点。而pivotX和pivotY就共同决定了这个点;它的值可以是float或者是百分比数值。
以 pivotX 为例,说明其取不同的值的含义:
10:距离动画所在view自身左边缘10像素
10% :距离动画所在view自身左边缘 的距离是整个view宽度的10%
10%p:距离动画所在view父控件左边缘的距离是整个view宽度的10%

rotate_anim.xml

<?xml version="1.0" encoding="utf-8"?><rotateandroid:fromDegrees="float"android:toDegrees="float"android:pivotX="50%"android:pivotY="50%" />

定义好了动画资源之后,就可以利用 AnimationUtils 工具类来加载指定动画资源,加载成功后返回一个 Animation,该对象可以控制图片或者视图播放动画。

示例:
1、定义动画资源:
res\anim\tween_anim.xml

<?xml version="1.0" encoding="utf-8"?>
<set xmlns:android="http://schemas.android.com/apk/res/android"android:interpolator="@android:anim/accelerate_decelerate_interpolator"><scaleandroid:duration="3000"android:fromXScale="0.0"android:fromYScale="0.0"android:pivotX="50%"android:pivotY="50%"android:toXScale="1.0"android:toYScale="1.0"/><alphaandroid:duration="3000"android:fromAlpha="1.0"android:toAlpha="0.5" /><rotateandroid:fromDegrees="0"android:toDegrees="720"android:pivotX = "50%"android:pivotY="50%"android:duration = "3000"/><translateandroid:fromXDelta="0"android:toXDelta="100"android:fromYDelta="0"android:toYDelta="100" />
</set>

2、Animation 控制图片播放动画

public class tweenAnimation extends AppCompatActivity {// tween_image;Button tween_start;@Overrideprotected void onCreate(@Nullable Bundle savedInstanceState) {super.onCreate(savedInstanceState);setContentView(R.layout.activity_tween_animation);final ImageView  tween_image = findViewById(R.id.tween_image);tween_start = findViewById(R.id.tween_start);// 加载动画资源final Animation anim = AnimationUtils.loadAnimation(this,R.anim.tween_anim);//设置动画结束后保留结束状态anim.setFillAfter(true);tween_start.setOnClickListener(new View.OnClickListener() {@Overridepublic void onClick(View view) {tween_image.startAnimation(anim);}});}
}

这几个动画可以组合在一起使用,同时完成缩放、透明的、旋转或者位移等的变化。

2、Java 代码实现补间动画

  • Translate
 Animation translateAnimation = new TranslateAnimation(0,500,0,500);// 创建平移动画的对象:平移动画对应的Animation子类为TranslateAnimation// 参数分别是:// 1. fromXDelta :视图在水平方向x 移动的起始值// 2. toXDelta :视图在水平方向x 移动的结束值// 3. fromYDelta :视图在竖直方向y 移动的起始值// 4. toYDelta:视图在竖直方向y 移动的结束值translateAnimation.setDuration(3000);// 播放动画直接 startAnimation(translateAnimation)//如:mButton.startAnimation(translateAnimation);
  • Scale:
        Animation scaleAnimation= new ScaleAnimation(0,2,0,2,Animation.RELATIVE_TO_SELF,0.5f,Animation.RELATIVE_TO_SELF,0.5f);// 1. fromX :动画在水平方向X的结束缩放倍数// 2. toX :动画在水平方向X的结束缩放倍数// 3. fromY :动画开始前在竖直方向Y的起始缩放倍数// 4. toY:动画在竖直方向Y的结束缩放倍数// 5. pivotXType:缩放轴点的x坐标的模式// 6. pivotXValue:缩放轴点x坐标的相对值// 7. pivotYType:缩放轴点的y坐标的模式// 8. pivotYValue:缩放轴点y坐标的相对值// pivotXType = Animation.ABSOLUTE:缩放轴点的x坐标 =  View左上角的原点 在x方向 加上 pivotXValue数值的点(y方向同理)// pivotXType = Animation.RELATIVE_TO_SELF:缩放轴点的x坐标 = View左上角的原点 在x方向 加上 自身宽度乘上pivotXValue数值的值(y方向同理)// pivotXType = Animation.RELATIVE_TO_PARENT:缩放轴点的x坐标 = View左上角的原点 在x方向 加上 父控件宽度乘上pivotXValue数值的值 (y方向同理)scaleAnimation.setDuration(3000);// 使用mButton.startAnimation(scaleAnimation);
  • Rotate:
   Animation rotateAnimation = new RotateAnimation(0,270,Animation.RELATIVE_TO_SELF,0.5f,Animation.RELATIVE_TO_SELF,0.5f);// 1. fromDegrees :动画开始时 视图的旋转角度(正数 = 顺时针,负数 = 逆时针)// 2. toDegrees :动画结束时 视图的旋转角度(正数 = 顺时针,负数 = 逆时针)// 3. pivotXType:旋转轴点的x坐标的模式// 4. pivotXValue:旋转轴点x坐标的相对值// 5. pivotYType:旋转轴点的y坐标的模式// 6. pivotYValue:旋转轴点y坐标的相对值// pivotXType = Animation.ABSOLUTE:旋转轴点的x坐标 =  View左上角的原点 在x方向 加上 pivotXValue数值的点(y方向同理)// pivotXType = Animation.RELATIVE_TO_SELF:旋转轴点的x坐标 = View左上角的原点 在x方向 加上 自身宽度乘上pivotXValue数值的值(y方向同理)// pivotXType = Animation.RELATIVE_TO_PARENT:旋转轴点的x坐标 = View左上角的原点 在x方向 加上 父控件宽度乘上pivotXValue数值的值 (y方向同理)rotateAnimation.setDuration(3000);mButton.startAnimation(rotateAnimation);

Alpha

 Animation alphaAnimation = new AlphaAnimation(1,0);// 1. fromAlpha:动画开始时视图的透明度(取值范围: -1 ~ 1)// 2. toAlpha:动画结束时视图的透明度(取值范围: -1 ~ 1)alphaAnimation.setDuration(3000);mButton.startAnimation(alphaAnimation);
  • 动画组合
        // 组合动画设置AnimationSet setAnimation = new AnimationSet(true);// 特别说明以下情况// 因为在下面的旋转动画设置了无限循环(RepeatCount = INFINITE)// 所以动画不会结束,而是无限循环// 所以组合动画的下面两行设置是无效的setAnimation.setRepeatMode(Animation.RESTART);setAnimation.setRepeatCount(1);// 设置了循环一次,但无效// 旋转动画Animation rotate = new RotateAnimation(0,360,Animation.RELATIVE_TO_SELF,0.5f,Animation.RELATIVE_TO_SELF,0.5f);rotate.setDuration(1000);rotate.setRepeatMode(Animation.RESTART);rotate.setRepeatCount(Animation.INFINITE);// 平移动画Animation translate = new TranslateAnimation(TranslateAnimation.RELATIVE_TO_PARENT,-0.5f,TranslateAnimation.RELATIVE_TO_PARENT,0.5f,TranslateAnimation.RELATIVE_TO_SELF,0,TranslateAnimation.RELATIVE_TO_SELF,0);translate.setDuration(10000);// 透明度动画Animation alpha = new AlphaAnimation(1,0);alpha.setDuration(3000);alpha.setStartOffset(7000);// 缩放动画Animation scale1 = new ScaleAnimation(1,0.5f,1,0.5f,Animation.RELATIVE_TO_SELF,0.5f,Animation.RELATIVE_TO_SELF,0.5f);scale1.setDuration(1000);scale1.setStartOffset(4000);// 将创建的子动画添加到组合动画里setAnimation.addAnimation(alpha);setAnimation.addAnimation(rotate);setAnimation.addAnimation(translate);setAnimation.addAnimation(scale1);// 使用mButton.startAnimation(setAnimation);

3、动画监听

为了实现一些需求,如动画结束后开始另一个动画或者页面跳转,这时候就需要监听动画。

 Animation.addListener(new AnimatorListener() {@Overridepublic void onAnimationStart(Animation animation) {//动画开始时执行}@Overridepublic void onAnimationRepeat(Animation animation) {//动画重复时执行}@Overridepublic void onAnimationCancel()(Animation animation) {//动画取消时执行}@Overridepublic void onAnimationEnd(Animation animation) {//动画结束时执行}});

4、自定义补间动画

Android 提供了 Animation 作为补间动画抽象基类,而且为该抽象基类提供了 AlphaAnimation、RotationAnimation、ScaleAnimation、TranslateAnimation 四个实现类,这四个实现类只是补间动画的基本形式:透明度、旋转、缩放、位移。但是要实现复杂的动画,就需要继承 Animation。继承 Animation 类关键是要重写一个方法:

applyTransformation(float interpolatedTime,Transformation t)
interploatedTime: 代表了动画的时间进行比。不管动画实际的持续时间如何,当动画播放时,该参数总是从 0 到 1。

Transformation t:该参数代表了补间动画在不同时刻对图形或组件的变形程度。

在实现自定义动画的关键就是重写 applyTransformation 方法时 根据 interpolatedTime 时间来动态地计算动画对图片或视图的变形程度。

Transformation 代表了对图片或者视图的变形,该对象封装了一个 Matrix 对象,对它所包装了 Matrix 进行位移、倾斜、旋转等变换时,Transformation 将会控制对应的图片或视图进行相应的变换。

为了控制图片或者 View 进行三维空间的变换,还需要借助于 Android 提供的一个 Camera,这个 Camera 并非代表手机摄像头,而是空间变换工具。作用类似于 Matrix,其常用方法如下:
getMatrix(Matrix matrix):将 Camera 所做的变换应用到指定的 matrix 上。
rotateX(float deg):将组件沿 X 轴旋转。
rotateY(float deg):将组件沿 Y 轴旋转。
rotateZ(float deg):将组件沿 Z 轴旋转。
translate(float x,float y,float z):目标组件在三维空间里变换。
applyToCanvas(Canvas canvas):把 Camera 所做的变换应用到 Canvas 上。

下面示例利用 Camera 自定义三维空间动画。
自定义 Animation

public class CustomAnimation extends Animation {private float centerX;private float centerY;// 定义动画的持续事件private int duration;private Camera camera = new Camera();public CustomAnimation(float x,float y,int duration){this.centerX = x;this.centerY = y;this.duration = duration;}@Overridepublic void initialize(int width, int height, int parentWidth, int parentHeight) {super.initialize(width, height, parentWidth, parentHeight);//设置动画的持续时间setDuration(duration);//设置动画结束后保留效果setFillAfter(true);setInterpolator(new LinearInterpolator());}@Overrideprotected void applyTransformation(float interpolatedTime, Transformation t) {//super.applyTransformation(interpolatedTime, t);camera.save();// 根据 interpolatedTime 时间来控制X,Y,Z 上偏移camera.translate(100.0f - 100.f * interpolatedTime,150.0f * interpolatedTime - 150,80.0f - 80.0f * interpolatedTime);// 根据 interploatedTime 设置在 X 轴 和 Y 轴旋转camera.rotateX(360 * interpolatedTime);camera.rotateY(360 * interpolatedTime);// 获取 Transformation 参数的 Matrix 对象Matrix matrix = t.getMatrix();camera.getMatrix(matrix);matrix.preTranslate(-centerX,-centerY);matrix.postTranslate(centerX,centerY);camera.restore();}
}

上面自定义了动画,主要是设置了旋转。
使用:

  linearLayout.startAnimation(new CustomAnimation(metrics.xdpi/2,metrics.ydpi/2,3500));

三、属性动画

属性动画可以看作是增强版的补间动画,与补间动画的不同之处体现在:

  • 补间动画只能定义两个关键帧在透明、旋转、位移和倾斜这四个属性的变换,但是属性动画可以定义任何属性的变化。
  • 补间动画只能对 UI 组件执行动画,但属性动画可以对任何对象执行动画。

与补间动画类似的是,属性动画也需要定义几个方面的属性:

  • 动画持续时间。默认为 300ms,可以通过 android:duration 属性指定。
  • 动画插值方式。通过 android:interploator 指定。
  • 动画重复次数。通过 android:repeatCount 指定。
  • 重复行为。通过 android:repeatMode 指定。
  • 动画集。在属性资源文件中通过 <set .../> 来组合。
  • 帧刷新率。指定多长时间播放一帧。默认为 10 ms。

属性动画 API

  • Animator: 提供创建属性动画的基类,基本不会直接使用这个类。
  • ValueAnimator:属性动画用到的主要的时间引擎,负责计算各个帧的属性值。
  • ObjectAnimator: ValueAnimator 的子类,对指定对象的属性执行动画。
  • AnimatorSet:Animator 的子类,用于组合多个 Animator。

除了这些 API,属性动画还提供了一个 Evaluator ,用来控制属性动画如何计算属性值。

  • IntEvaluator:计算 int 类型属性值的计算器。
  • FloatEvaluator: 用于计算 float 类型属性值的计算器。
  • ArgbEvaluator: 用于计算十六进制形式表示的颜色值的计算器。
  • TypeEvaluator: 可以自定义计算器。

使用 ValueAnimator 创建动画的步骤:

  • 调用 ValueAnimator 的 ofInt()、ofFloat() 或者 ofObject() 静态方法创建 ValueAnimator 实例。
  • 调用 ValueAnimator 的 setXxx() 等方法设置持续时间,插值方式、重复次数等。
  • 调用 ValueAnimator 的 start() 方法启动动画。
  • 为 ValueAnimator 注册 AnimatorUpdateListener 监听器,在该监听器中可以监听 ValueAnimator 计算出来的值改变,并将这些值应用到指定对象上。

属性动画的一般使用:

定义属性动画和补间动画等类似,有两种方式:

  • 使用 ValueAnimator 或者 ObjectAnimator 的静态工厂方法创建动画。
  • 使用资源文件来定义动画。

属性动画的使用:

  • 创建 ValueAnimator 或 ObjectAnimator 对象 —— 即可以从 XML 资源文件加载该动画也可以直接调用 ValueAnimator 或者 ObjectAnimator 的静态工厂方法创建动画。
  • 根据需要为 Animator 对象设置属性。
  • 如果需要监听 Animator 的动画开始事件,动画结束事件、动画重复事件、动画值改变事件,并根据事件提供响应处理代码,需要为Animator 对象设置监听器。
  • 如果有多个动画需要同时播放,需要使用 AnimatorSet 组合这些动画。
  • 调用 Animator 对象的 start 启动动画。

属性动画示例:
1、布局文件

<?xml version="1.0" encoding="utf-8"?>
<RelativeLayout xmlns:android="http://schemas.android.com/apk/res/android"android:orientation="vertical"android:layout_width="match_parent"android:layout_height="match_parent"><ImageViewandroid:layout_width="50dp"android:layout_height="50dp"android:id="@+id/imageView_b"android:src="@drawable/img"android:layout_centerVertical="true"android:layout_centerHorizontal="true" /><ImageViewandroid:layout_width="50dp"android:layout_height="50dp"android:id="@+id/imageView_c"android:src="@drawable/img"android:layout_centerVertical="true"android:layout_centerHorizontal="true" /><ImageViewandroid:layout_width="50dp"android:layout_height="50dp"android:id="@+id/imageView_d"android:src="@drawable/img"android:layout_centerVertical="true"android:layout_centerHorizontal="true" /><ImageViewandroid:layout_width="50dp"android:layout_height="50dp"android:id="@+id/imageView_e"android:src="@drawable/img"android:layout_centerVertical="true"android:layout_centerHorizontal="true" /><ImageViewandroid:layout_width="50dp"android:layout_height="50dp"android:id="@+id/imageView_a"android:src="@drawable/img"android:layout_centerVertical="true"android:layout_centerHorizontal="true" />
</RelativeLayout>

2、使用

public class PropertyAnimator  extends Activity implements View.OnClickListener {private int[] mRes = {R.id.imageView_a, R.id.imageView_b, R.id.imageView_c,R.id.imageView_d, R.id.imageView_e};private List<ImageView> mImageViews = new ArrayList<>();private boolean mFlag = true;@Overrideprotected void onCreate(Bundle savedInstanceState) {super.onCreate(savedInstanceState);setContentView(R.layout.property_animator);int sum = mRes.length;for (int i = 0; i < sum; i++) {ImageView imageView = (ImageView) findViewById(mRes[i]);imageView.setOnClickListener(this);mImageViews.add(imageView);}}@Overridepublic void onClick(View v) {switch (v.getId()) {case R.id.imageView_a:if (mFlag) {startAnim();} else {closeAnim();}break;case R.id.imageView_b:Toast.makeText(PropertyAnimator.this, "b", Toast.LENGTH_SHORT).show();break;case R.id.imageView_c:Toast.makeText(PropertyAnimator.this, "c", Toast.LENGTH_SHORT).show();break;case R.id.imageView_d:Toast.makeText(PropertyAnimator.this, "d", Toast.LENGTH_SHORT).show();break;case R.id.imageView_e:Toast.makeText(PropertyAnimator.this, "e", Toast.LENGTH_SHORT).show();break;}}private void closeAnim() {ObjectAnimator animator0 = ObjectAnimator.ofFloat(mImageViews.get(0),"alpha", 0.5F, 1F);ObjectAnimator animator1 = ObjectAnimator.ofFloat(mImageViews.get(1),"translationY", 200F, 0);ObjectAnimator animator2 = ObjectAnimator.ofFloat(mImageViews.get(2),"translationX", 200F, 0);ObjectAnimator animator3 = ObjectAnimator.ofFloat(mImageViews.get(3),"translationY", -200F, 0);ObjectAnimator animator4 = ObjectAnimator.ofFloat(mImageViews.get(4),"translationX", -200F, 0);AnimatorSet set = new AnimatorSet();set.setDuration(500);set.setInterpolator(new BounceInterpolator());set.playTogether(animator0, animator1, animator2, animator3, animator4);set.start();mFlag = true;}private void startAnim() {ObjectAnimator animator0 = ObjectAnimator.ofFloat(mImageViews.get(0),"alpha",1F,0.5F);ObjectAnimator animator1 = ObjectAnimator.ofFloat(mImageViews.get(1),"translationY",200F);ObjectAnimator animator2 = ObjectAnimator.ofFloat(mImageViews.get(2),"translationX",200F);ObjectAnimator animator3 = ObjectAnimator.ofFloat(mImageViews.get(3),"translationY",-200F);ObjectAnimator animator4 = ObjectAnimator.ofFloat(mImageViews.get(4),"translationX",-200F);AnimatorSet set = new AnimatorSet();set.setDuration(500);set.setInterpolator(new BounceInterpolator());set.playTogether(animator0,animator1,animator2,animator3,animator4);set.start();mFlag = false;}
}

四、使用 SurfaceView 实现动画

实现动画还可以通过自定义 View 的方式,但是自定义 View 有如下缺陷:

  • View 缺乏双缓冲机制。
  • 当程序需要更新 View 上的图像时,程序必须重绘 View 上显示的整张图片。
  • 新线程无法直接更新 View 组件。

因此,自定义 View 实现绘图不是很好的选择,尤其是游戏绘图时,性能不是很好。因此,Android 提供了一个 SurfaceView 来代替 View。

SurfaceView 一般使用:

首先继承SurfaceView,并实现SurfaceHolder.Callback接口,实现它的三个方法:

  • surfaceCreated(SurfaceHolder holder):surface创建的时候调用,一般在该方法中启动绘图的线程。
  • surfaceChanged(SurfaceHolder holder, int format, int width,int height):surface尺寸发生改变的时候调用,如横竖屏切换。
  • surfaceDestroyed(SurfaceHolder holder) :surface被销毁的时候调用,如退出游戏画面,一般在该方法中停止绘图线程。

SurfaceView 一般与 SurfaceHolder 结合使用,SurfaceHolder 用于向与之关联的 SurfaceView 上绘图,调用 SurfaceView 的 getHolder() 方法就可以获取 SurfaceView 关联的 SurfaceHolder。

SurfaceHolder 提供了如下方法获取 Canvas 对象。

  • Canvas lockCanvas(): 锁定整个 SurfaceView 对象,获取该 Surface 上的 Canvas。
  • Canvas locakCanvas(Rect dirty): 锁定 SurfaceView 上的 Rect 划分的区域,获取该 Surface 上的 Canvas,这样可以提高画面的跟新速度。

释放 Canvas 并提交绘制:

  • unlockCanvasAndPost(canvas)

SurfaceView 与普通 View 还有一个重要区别: View 的绘图必须在 UI 线程中进行,而 SurfaceView 由 SurfaceHolder 来完成,SurfaceHodler 会开启新的线程去绘制,不会阻塞 UI 线程。

SurfaceView 实现动画示例:

  • 定义一个动画 Bean 类:
public class SurfaceBean {public Point point;/*** 移动动画*/private ValueAnimator moveAnim;/*** 放大动画*/private ValueAnimator zoomAnim;/*** 透明度*/public int alpha = 255;//private Bitmap bitmap;/*** 绘制bitmap的矩阵  用来做缩放和移动的*/private Matrix matrix = new Matrix();/*** 缩放系数*/private float sf = 0;/*** 产生随机数*/private Random random;public boolean isEnd = false;//是否结束public SurfaceBean(Context context, int resId, SurfaceAnimView surfaceAnimView) {random = new Random();bitmap = BitmapFactory.decodeResource(context.getResources(), resId);init(new Point(surfaceAnimView.getWidth() / 2, surfaceAnimView.getHeight()- bitmap.getHeight() / 2), new Point((random.nextInt(surfaceAnimView.getWidth())), 0));}public SurfaceBean(Bitmap bitmap, SurfaceAnimView surfaceAnimView) {random = new Random();this.bitmap = bitmap;//为了让在起始坐标点时显示完整 需要减去bitmap.getHeight()/2init(new Point(surfaceAnimView.getWidth() / 2, surfaceAnimView.getHeight() - bitmap.getHeight() / 2), new Point((random.nextInt(surfaceAnimView.getWidth())), 0));}@TargetApi(Build.VERSION_CODES.HONEYCOMB)private void init(final Point startPoint, Point endPoint) {moveAnim = ValueAnimator.ofObject(new BezierEvaluator(new Point(random.nextInt(startPoint.x * 2), Math.abs(endPoint.y - startPoint.y) / 2)), startPoint, endPoint);moveAnim.setDuration(1500);moveAnim.addUpdateListener(new ValueAnimator.AnimatorUpdateListener() {@Overridepublic void onAnimationUpdate(ValueAnimator animation) {point = (Point) animation.getAnimatedValue();alpha = (int) ((float) point.y / (float) startPoint.y * 255);}});moveAnim.start();zoomAnim = ValueAnimator.ofFloat(0, 1f).setDuration(700);zoomAnim.addUpdateListener(new ValueAnimator.AnimatorUpdateListener() {@Overridepublic void onAnimationUpdate(ValueAnimator animation) {Float f = (Float) animation.getAnimatedValue();sf = f.floatValue();}});zoomAnim.start();}public void stop() {if (moveAnim != null) {moveAnim.cancel();moveAnim = null;}if (zoomAnim != null) {zoomAnim.cancel();zoomAnim = null;}}/*** 主要绘制函数*/public void draw(Canvas canvas, Paint p) {if (bitmap != null && alpha > 0) {p.setAlpha(alpha);matrix.setScale(sf, sf, bitmap.getWidth() / 2, bitmap.getHeight() / 2);matrix.postTranslate(point.x - bitmap.getWidth() / 2, point.y - bitmap.getHeight() / 2);canvas.drawBitmap(bitmap, matrix, p);} else {isEnd = true;}}/*** 二次贝塞尔曲线*/@TargetApi(Build.VERSION_CODES.HONEYCOMB)private class BezierEvaluator implements TypeEvaluator<Point> {private Point centerPoint;public BezierEvaluator(Point centerPoint) {this.centerPoint = centerPoint;}@Overridepublic Point evaluate(float t, Point startValue, Point endValue) {int x = (int) ((1 - t) * (1 - t) * startValue.x + 2 * t * (1 - t) * centerPoint.x + t * t * endValue.x);int y = (int) ((1 - t) * (1 - t) * startValue.y + 2 * t * (1 - t) * centerPoint.y + t * t * endValue.y);return new Point(x, y);}}
}
  • 定义绘图类
public class SurfaceAnimView extends SurfaceView implements SurfaceHolder.Callback {private SurfaceHolder surfaceHolder;private ArrayList<SurfaceBean> surfaceBeans = new ArrayList<>();private Paint p;/*** 负责绘制的工作线程*/private DrawThread drawThread;public SurfaceAnimView(Context context) {this(context, null);}public SurfaceAnimView(Context context, AttributeSet attrs) {this(context, attrs, 0);}public SurfaceAnimView(Context context, AttributeSet attrs, int defStyleAttr) {super(context, attrs, defStyleAttr);this.setZOrderOnTop(true);/**设置画布  背景透明*/this.getHolder().setFormat(PixelFormat.TRANSLUCENT);surfaceHolder = getHolder();surfaceHolder.addCallback(this);p = new Paint();p.setAntiAlias(true);drawThread = new DrawThread();}public void addBean(SurfaceBean surfaceBean){surfaceBeans.add(surfaceBean);if (surfaceBeans.size() > 40) {surfaceBeans.remove(0);}start();}@Overridepublic void surfaceCreated(SurfaceHolder holder) {if (drawThread == null) {drawThread = new DrawThread();}drawThread.start();}@Overridepublic void surfaceChanged(SurfaceHolder holder, int format, int width, int height) {}@Overridepublic void surfaceDestroyed(SurfaceHolder holder) {if (drawThread != null) {drawThread.isRun = false;drawThread = null;}}class DrawThread extends Thread {boolean isRun = true;@Overridepublic void run() {super.run();/**绘制的线程 死循环 不断的跑动*/while (isRun) {Canvas canvas = null;try {synchronized (surfaceHolder) {canvas = surfaceHolder.lockCanvas();/**清除画面*/canvas.drawColor(Color.TRANSPARENT, PorterDuff.Mode.CLEAR);boolean isEnd = true;for (int i = 0; i < surfaceBeans.size(); i++) {isEnd = surfaceBeans.get(i).isEnd;surfaceBeans.get(i).draw(canvas, p);}/**这里做一个性能优化的动作,由于线程是死循环的 在没有心需要的绘制的时候会结束线程*/if (isEnd) {isRun = false;drawThread = null;}}} catch (Exception e) {e.printStackTrace();} finally {if (canvas != null) {surfaceHolder.unlockCanvasAndPost(canvas);}}try {/**用于控制绘制帧率*/Thread.sleep(10);} catch (InterruptedException e) {e.printStackTrace();}}}}public void stop() {if (drawThread != null) {for (int i = 0; i < surfaceBeans.size(); i++) {surfaceBeans.get(i).stop();}drawThread.isRun = false;drawThread = null;}}public void start() {if (drawThread == null) {drawThread = new DrawThread();drawThread.start();}}
}
  • 使用
public class SurfaceViewTest extends AppCompatActivity {SurfaceAnimView surface_anim_view;Button button;@Overrideprotected void onCreate(@Nullable Bundle savedInstanceState) {super.onCreate(savedInstanceState);setContentView(R.layout.surfaceview_anim);surface_anim_view = findViewById(R.id.surface_anim_view);button = findViewById(R.id.add);surface_anim_view.start();}public void add(View view) {SurfaceBean surfaceBean = new SurfaceBean(BitmapFactory.decodeResource(getResources(), R.drawable.img_t), surface_anim_view);surface_anim_view.addBean(surfaceBean);}
}

作者:hcwang17
链接:https://www.jianshu.com/p/609b6d88798d
来源:简书
简书著作权归作者所有,任何形式的转载都请联系作者获得授权并注明出处。

Android 动画总结相关推荐

  1. Android动画效果translate、scale、alpha、rotate详解

    动画类型 Android的animation由四种类型组成 XML中 alpha 渐变透明度动画效果 scale 渐变尺寸伸缩动画效果 translate 画面转换位置移动动画效果 rotate 画面 ...

  2. android jason动画,Android 动画之Lottie动画使用

    Android 动画之Lottie动画使用 一:简介 Lottie是Airbnb开源的一套跨平台的完整解决方案,设计师只需要使用After Effects(简称AE)设计动画之后,使用Lottic提供 ...

  3. android动画的实现原理,Android动画的实现原理 .

    1.动画运行模式 独行模式 中断模式 2.Animation类 每个动画都重载了父类的applyTransformation方法这个方法的主要作用是把一些属性组装成一个Transformation类, ...

  4. Android动画曲线库AndroidEasingFunctions

    Android动画曲线库AndroidEasingFunctions AndroidEasingFunction是基于Easing Function(缓动函数)的Android动画曲线库.它提供了九大 ...

  5. android 动画引擎,一个使用openGL渲染的炫丽Android动画库

    这是一个 android 动画特效库 可以实现各种炫酷动画. github地址: ht t ps:// gith  u b.co m/g pl ib s/an dro id- ma gic-s ur ...

  6. Android 动画分析学习笔记

    一:分类: Android动画分三种:view动画(对场景中的对象不断做图像变换<平移,缩放,旋转,透明度>).帧动画(顺序播放一系列图像产生动画效果).属性动画(动态改变对象属性). 二 ...

  7. Android动画之Tween动画实战

    Android动画分为Tween动画和Frame动画,上一节通过一个实例介绍了Frame动画,本节将介绍Tween动画.Tween可以把对象进行缩小.放大.旋转和渐变等操作. Tween动画有四个主要 ...

  8. Android动画 详解(一 补间动画)

    2019独角兽企业重金招聘Python工程师标准>>> 打算整理下 android动画方面的知识,嗯  开始 一.android补间动画 分为四大类 alpha(透明度渐变).sca ...

  9. Android 动画之ScaleAnimation应用详解

    本节讲解ScaleAnimation 动画在应用中的实现,有需要的朋友可以参考下 android中提供了4中动画: AlphaAnimation 透明度动画效果 ScaleAnimation 缩放动画 ...

  10. Android动画开发——Animation动画效果

    动画类型 Android的animation由四种类型组成 XML中 alpha 渐变透明度动画效果 scale 渐变尺寸伸缩动画效果 translate 画面转换位置移动动画效果 rotate 画面 ...

最新文章

  1. 05-dispatch_semphore
  2. 1.11 Java数组填充(fill())
  3. LiveVideoStackCon 2019北京你来吗?
  4. 微信 小程序 python 渲染_微信小程序渲染html内容
  5. Matrix Subtraction(小米icpc邀请赛第一场)
  6. Spring-boot IDEA使用注解@ConfigurationProperties时报错解决
  7. [Leetcode][第1002题][JAVA][查找常用字符][计数][HashMap]
  8. Java工作笔记-判断文件是否被正在被写入
  9. 使用SecureCRT时屏幕僵死的处理方法——Linux终端设置技巧
  10. Java JUC学习 - ConcurrentLinkedDeque 详解
  11. linux下的dns服务器
  12. 番茄瑜伽13招-学好可以疏经活血祛风止痛
  13. [翻译] LASIImageView - 显示进度指示并异步下载图片
  14. vscode android调试,使用VsCode开发调试React Native笔记
  15. 9-21 调试javaweb 数据库连接感想
  16. Redis(RedisTemplate)使用hash哈希
  17. idea vscode快捷键
  18. 最好用的伪原创工具手机app
  19. win10系统CAJViewer 绿色提示缺少由于找不到 MSVCR71.dll
  20. 打造敏捷的自组织团队

热门文章

  1. 集美大学计算机学院和信息工程学院,集美大学有哪些院系和专业-什么专业比较好...
  2. pymol配体平移与旋转
  3. 如何提高本地文件上传至百度云的速度_【软件推荐】满速下载软件,说出来你可能不信最高速度达到150m/s...
  4. win7系统 无法访问 Windows installer服务。Windows Installer 没有正确安装时
  5. Flutter中解决AndroidX包与Support包冲突问题
  6. 秋冬饮品研发没思路?带你看新品5大趋势!
  7. 清醒认识数据第一步,把关数据质量
  8. python修改电脑桌面壁纸_python实现桌面壁纸切换功能
  9. 填补空缺——压缩感知
  10. 同相和反相比例运算放大电路