Android动画效果


1 章节目录

1.1 目标

  • 帧动画

  • 补间动画

1.2 重点

  • 帧动画

  • 补间动画

1.3 难点

  • 帧动画

  • 补间动画

2 动画

在APP中添加上一些动画,会让我们的应用变得 很炫,比如最简单的关开Activity,当然自定义控件动画肯定必不可少啦~而Android中的动画 分为三大类,逐帧动画(Frame)以及补间动画(Tween),还有Android 3.0以后引入的属性动画(Property)

3 帧动画

3.1 帧动画简介

帧动画非常容易理解,其实就是简单的由N张静态图片收集起来,然后我们通过控制依次显示 这些图片,因为人眼"视觉残留"的原因,会让我们造成动画的"错觉",跟放电影的原理一样!

而Android中实现帧动画,一般我们会用到前面讲解到的一个Drawable:AnimationDrawable先编写好Drawable,然后代码中调用start()以及stop()开始或停止播放动画

当然我们也可以在Java代码中创建逐帧动画,创建AnimationDrawable对象,然后调用 addFrame(Drawable frame,int duration)向动画中添加帧,接着调用start()和stop()而已

3.2 帧动画的使用效果(一)

 

代码实现

首先编写activity_main.xml

<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
   android:layout_width="match_parent"
   android:layout_height="match_parent"
   android:orientation="vertical">

   <Button
       android:id="@+id/btn_start"
       android:layout_width="wrap_content"
       android:layout_height="wrap_content"
       android:text="开始" />

   <Button
       android:id="@+id/btn_stop"
       android:layout_width="wrap_content"
       android:layout_height="wrap_content"
       android:text="停止" />

   <ImageView
       android:id="@+id/img_show"
       android:layout_width="120dp"
       android:layout_height="120dp"
       android:layout_gravity="center"
       android:background="@anim/miao_gif" />

</LinearLayout>

接下来编写我们的动画文件,非常简单,先在drawable目录下,接着开始撸我们的 动画文件:miao_gif.xml: 这里的android:oneshot是设置动画是否只是播放一次,true只播放一次,false循环播放!

<?xml version="1.0" encoding="utf-8"?>
<animation-list xmlns:android="http://schemas.android.com/apk/res/android"
   android:oneshot="false">
   <item
       android:drawable="@mipmap/img_miao1"
       android:duration="80" />
   <item
       android:drawable="@mipmap/img_miao2"
       android:duration="80" />
   <item
       android:drawable="@mipmap/img_miao3"
       android:duration="80" />
   <!--限于篇幅,省略其他item,自己补上-->
  ...
</animation-list>

动画文件有了,接着到我们的布局文件:activity_main.xml

<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
   android:layout_width="match_parent"
   android:layout_height="match_parent"
   android:orientation="vertical">

   <Button
       android:id="@+id/btn_start"
       android:layout_width="wrap_content"
       android:layout_height="wrap_content"
       android:text="开始" />

   <Button
       android:id="@+id/btn_stop"
       android:layout_width="wrap_content"
       android:layout_height="wrap_content"
       android:text="停止" />

   <ImageView
       android:id="@+id/img_show"
       android:layout_width="120dp"
       android:layout_height="120dp"
       android:layout_gravity="center"
       android:background="@anim/miao_gif" />
   
</LinearLayout>

最后是我们的MainActivity.java,这里在这里控制动画的开始以及暂停:

public class MainActivity extends AppCompatActivity implements View.OnClickListener {

   private Button btn_start;
   private Button btn_stop;
   private ImageView img_show;
   private AnimationDrawable anim;

   @Override
   protected void onCreate(Bundle savedInstanceState) {
       super.onCreate(savedInstanceState);
       setContentView(R.layout.activity_main);
       bindViews();
       anim = (AnimationDrawable) img_show.getBackground();
  }

   private void bindViews() {
       btn_start = (Button) findViewById(R.id.btn_start);
       btn_stop = (Button) findViewById(R.id.btn_stop);
       img_show = (ImageView) findViewById(R.id.img_show);
       btn_start.setOnClickListener(this);
       btn_stop.setOnClickListener(this);
  }
   
   @Override
   public void onClick(View v) {
       switch (v.getId()) {
           case R.id.btn_start:
               anim.start();
               break;
           case R.id.btn_stop:
               anim.stop();
               break;
      }
  }
}

3.3 帧动画的使用效果(二)

在指定地方播放帧动画

 

代码实现

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

   <TextView android:text="@string/hello_world"
  android:layout_width="wrap_content"
       android:layout_height="wrap_content" />

</RelativeLayout>

依旧是先上我们的动画文件:anim_zhuan.xml

<animation-list xmlns:android="http://schemas.android.com/apk/res/android"
   android:oneshot="true">
   <item
       android:drawable="@mipmap/img_zhuan1"
       android:duration="80" />
   <item
       android:drawable="@mipmap/img_zhuan2"
       android:duration="80" />
   <item
       android:drawable="@mipmap/img_zhuan3"
       android:duration="80" />
    <!--限于篇幅,省略其他item,自己补上-->
  ...
</animation-list>

接着我们来写一个自定义的ImageView:FrameView.java,这里通过反射获得当前播放的帧, 然后是否为最后一帧,是的话隐藏控件!

public class FrameView extends ImageView {

   private AnimationDrawable anim;

   public FrameView(Context context) {
       super(context);
  }

   public FrameView(Context context, AttributeSet attrs) {
       super(context, attrs);
  }

   public FrameView(Context context, AttributeSet attrs, int defStyleAttr) {
       super(context, attrs, defStyleAttr);
  }

   public void setAnim(AnimationDrawable anim){
       this.anim = anim;
  }

   public void setLocation(int top,int left){
       this.setFrame(left,top,left + 200,top + 200);
  }

   @Override
   protected void onDraw(Canvas canvas) {
       try{
           //反射调用AnimationDrawable里的mCurFrame值
           Field field = AnimationDrawable.class
                  .getDeclaredField("mCurFrame");
           field.setAccessible(true);
           int curFrame = field.getInt(anim);// 获取anim动画的当前帧
           if (curFrame == anim.getNumberOfFrames() - 1)// 如果已经到了最后一帧
          {
               //让该View隐藏
               setVisibility(View.INVISIBLE);
          }
      }catch (Exception e){e.printStackTrace();}
       super.onDraw(canvas);
  }
}

最后是我们的MainActivity.java,创建一个FrameLayout,添加View,对触摸事件中按下的 事件做处理,显示控件以及开启动画~

public class MainActivity extends AppCompatActivity {

   private FrameView fView;
   private AnimationDrawable anim = null;

   @Override
   protected void onCreate(Bundle savedInstanceState) {
       super.onCreate(savedInstanceState);
       FrameLayout fly = new FrameLayout(this);
       setContentView(fly);
       fView = new FrameView(this);
       fView.setBackgroundResource(R.anim.anim_zhuan);
       fView.setVisibility(View.INVISIBLE);
       anim = (AnimationDrawable) fView.getBackground();
       fView.setAnim(anim);
       fly.addView(fView);
       fly.setOnTouchListener(new View.OnTouchListener() {
           @Override
           public boolean onTouch(View v, MotionEvent event) {
               //设置按下时才产生动画效果
               if(event.getAction() == MotionEvent.ACTION_DOWN){
                   anim.stop();
                   float x = event.getX();
                   float y = event.getY();
                   fView.setLocation((int) y - 40,(int)x-20);  //View显示的位置
                   fView.setVisibility(View.VISIBLE);
                   anim.start();    //开启动画
              }
               return false;
          }
      });
  }
}

4 补间动画

与前面学的帧动画不同,帧动画是通过连续播放图片来模拟动画效果,而补间动画开发者只需指定动画开始,以及动画结束"关键帧", 而动画变化的"中间帧"则由系统计算并补齐!

4.1 补间动画的分类和Interpolator

4.1.1 Andoird所支持的补间动画效果有如下这五种,或者说四种吧,第五种是前面几种的组合而已

  • AlphaAnimation:透明度渐变效果,创建时许指定开始以及结束透明度,还有动画的持续 时间,透明度的变化范围(0,1),0是完全透明,1是完全不透明;对应<alpha/>标签!

  • ScaleAnimation:缩放渐变效果,创建时需指定开始以及结束的缩放比,以及缩放参考点, 还有动画的持续时间;对应<scale/>标签!

  • TranslateAnimation:位移渐变效果,创建时指定起始以及结束位置,并指定动画的持续 时间即可;对应<translate/>标签!

  • RotateAnimation:旋转渐变效果,创建时指定动画起始以及结束的旋转角度,以及动画 持续时间和旋转的轴心;对应<rotate/>标签

  • AnimationSet:组合渐变,就是前面多种渐变的组合,对应<set/>标签

4.2.2 Interpolator

用来控制动画的变化速度,可以理解成动画渲染器,当然我们也可以自己实现Interpolator 接口,自行来控制动画的变化速度,而Android中已经为我们提供了可供选择的实现类:

渲染器说明
LinearInterpolator 动画以均匀的速度改变
AccelerateInterpolator 在动画开始的地方改变速度较慢,然后开始加速
AccelerateDecelerateInterpolator 在动画开始、结束的地方改变速度较慢,中间时加速
CycleInterpolator 动画循环播放特定次数,变化速度按正弦曲线改变: Math.sin(2 * mCycles * Math.PI * input)
DecelerateInterpolator 在动画开始的地方改变速度较快,然后开始减速
AnticipateInterpolator 反向,先向相反方向改变一段再加速播放
AnticipateOvershootInterpolator 开始的时候向后然后向前甩一定值后返回最后的值
BounceInterpolator 跳跃,快到目的值时值会跳跃,如目的值100,后面的值可能依次为85,77,70,80,90,100
OvershottInterpolator 回弹,最后超出目的值然后缓慢改变到目的值

而这个Interpolator 我们一般是在写动画xml文件时会用到,属性是:android:interpolator, 而上面对应的值是:@android:anim/linear_interpolator,其实就是驼峰命名法变下划线而已 AccelerateDecelerateInterpolator对应:@android:anim/accelerate_decelerate_interpolator!

4.2 各种补间动画的详细讲解

这里的android:duration都是动画的持续时间,单位是毫秒

4.2.1 AlphaAnimation(透明度渐变)

anim_alpha.xml

<alpha xmlns:android="http://schemas.android.com/apk/res/android"  
   android:interpolator="@android:anim/accelerate_decelerate_interpolator"  
   android:fromAlpha="1.0"  
   android:toAlpha="0.1"  
   android:duration="2000"/>

属性解释:

  • fromAlpha :起始透明度

  • toAlpha:结束透明度

  • 透明度的范围为:0-1,完全透明-完全不透明


4.2.2 ScaleAnimation(缩放渐变)

anim_scale.xml

<scale xmlns:android="http://schemas.android.com/apk/res/android"  
   android:interpolator="@android:anim/accelerate_interpolator"  
   android:fromXScale="0.2"  
   android:toXScale="0.2"  
   android:fromYScale="1.5"  
   android:toYScale="1.5"  
   android:pivotX="50%"  
   android:pivotY="50%"  
   android:duration="2000"/>

属性解释:

  • fromXScale/fromYScale:沿着X轴/Y轴缩放的起始比例

  • toXScale/toYScale:沿着X轴/Y轴缩放的结束比例

  • pivotX/pivotY:缩放的中轴点X/Y坐标,即距离自身左边缘的位置,比如50%就是以图像的 中心为中轴点


4.2.3 TranslateAnimation(位移渐变)

anim_translate.xml

<translate xmlns:android="http://schemas.android.com/apk/res/android"  
   android:interpolator="@android:anim/accelerate_decelerate_interpolator"  
   android:fromXDelta="0"  
   android:toXDelta="320"  
   android:fromYDelta="0"  
   android:toYDelta="0"  
   android:duration="2000"/>

属性解释:

  • fromXDelta/fromYDelta:动画起始位置的X/Y坐标

  • toXDelta/toYDelta:动画结束位置的X/Y坐标


4.2.4 RotateAnimation(旋转渐变)

anim_rotate.xml

<rotate xmlns:android="http://schemas.android.com/apk/res/android"  
   android:interpolator="@android:anim/accelerate_decelerate_interpolator"  
   android:fromDegrees="0"  
   android:toDegrees="360"  
   android:duration="1000"  
   android:repeatCount="1"  
   android:repeatMode="reverse"/>

属性解释:

  • fromDegrees/toDegrees:旋转的起始/结束角度

  • repeatCount:旋转的次数,默认值为0,代表一次,假如是其他值,比如3,则旋转4次 另外,值为-1或者infinite时,表示动画永不停止

  • repeatMode:设置重复模式,默认restart,但只有当repeatCount大于0或者infinite或-1时 才有效。还可以设置成reverse,表示偶数次显示动画时会做方向相反的运动!


4.2.5 AnimationSet(组合渐变)

非常简单,就是前面几个动画组合到一起而已~

anim_set.xml

<set xmlns:android="http://schemas.android.com/apk/res/android"  
   android:interpolator="@android:anim/decelerate_interpolator"  
   android:shareInterpolator="true" >  
 
   <scale  
       android:duration="2000"  
       android:fromXScale="0.2"  
       android:fromYScale="0.2"  
       android:pivotX="50%"  
       android:pivotY="50%"  
       android:toXScale="1.5"  
       android:toYScale="1.5" />  
 
   <rotate  
       android:duration="1000"  
       android:fromDegrees="0"  
       android:repeatCount="1"  
       android:repeatMode="reverse"  
       android:toDegrees="360" />  
 
   <translate  
       android:duration="2000"  
       android:fromXDelta="0"  
       android:fromYDelta="0"  
       android:toXDelta="320"  
       android:toYDelta="0" />  
 
   <alpha  
       android:duration="2000"  
       android:fromAlpha="1.0"  
       android:toAlpha="0.1" />  

</set>  

4.3 实例

首先来个简单的布局:activity_main.xml

<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
   android:layout_width="match_parent"
   android:layout_height="match_parent"
   android:orientation="vertical">

   <Button
       android:id="@+id/btn_alpha"
       android:layout_width="wrap_content"
       android:layout_height="wrap_content"
       android:text="透明度渐变" />

   <Button
       android:id="@+id/btn_scale"
       android:layout_width="wrap_content"
       android:layout_height="wrap_content"
       android:text="缩放渐变" />

   <Button
       android:id="@+id/btn_tran"
       android:layout_width="wrap_content"
       android:layout_height="wrap_content"
       android:text="位移渐变" />

   <Button
       android:id="@+id/btn_rotate"
       android:layout_width="wrap_content"
       android:layout_height="wrap_content"
       android:text="旋转渐变" />

   <Button
       android:id="@+id/btn_set"
       android:layout_width="wrap_content"
       android:layout_height="wrap_content"
       android:text="组合渐变" />

   <ImageView
       android:id="@+id/img_show"
       android:layout_width="wrap_content"
       android:layout_height="wrap_content"
       android:layout_gravity="center"
       android:layout_marginTop="48dp"
       android:src="@mipmap/img_face" />
   
</LinearLayout>

接着到我们的MainActivity.java,同样非常简单,只需调用AnimationUtils.loadAnimation() 加载动画,然后我们的View控件调用startAnimation开启动画即可

public class MainActivity extends AppCompatActivity implements View.OnClickListener{

   private Button btn_alpha;
   private Button btn_scale;
   private Button btn_tran;
   private Button btn_rotate;
   private Button btn_set;
   private ImageView img_show;
   private Animation animation = null;

   @Override
   protected void onCreate(Bundle savedInstanceState) {
       super.onCreate(savedInstanceState);
       setContentView(R.layout.activity_main);
       bindViews();
  }

   private void bindViews() {
       btn_alpha = (Button) findViewById(R.id.btn_alpha);
       btn_scale = (Button) findViewById(R.id.btn_scale);
       btn_tran = (Button) findViewById(R.id.btn_tran);
       btn_rotate = (Button) findViewById(R.id.btn_rotate);
       btn_set = (Button) findViewById(R.id.btn_set);
       img_show = (ImageView) findViewById(R.id.img_show);

       btn_alpha.setOnClickListener(this);
       btn_scale.setOnClickListener(this);
       btn_tran.setOnClickListener(this);
       btn_rotate.setOnClickListener(this);
       btn_set.setOnClickListener(this);

  }

   @Override
   public void onClick(View v) {
       switch (v.getId()){
           case R.id.btn_alpha:
               animation = AnimationUtils.loadAnimation(this,
                       R.anim.anim_alpha);
               img_show.startAnimation(animation);
               break;
           case R.id.btn_scale:
               animation = AnimationUtils.loadAnimation(this,
                       R.anim.anim_scale);
               img_show.startAnimation(animation);
               break;
           case R.id.btn_tran:
               animation = AnimationUtils.loadAnimation(this,
                       R.anim.anim_translate);
               img_show.startAnimation(animation);
               break;
           case R.id.btn_rotate:
               animation = AnimationUtils.loadAnimation(this,
                       R.anim.anim_rotate);
               img_show.startAnimation(animation);
               break;
           case R.id.btn_set:
               animation = AnimationUtils.loadAnimation(this,
                       R.anim.anim_set);
               img_show.startAnimation(animation);
               break;
      }
  }
}

 

4.4 为Activity设置过场动画

Activty设置过场动画非常简单,调用的方法是:overridePendingTransition(int enterAnim, int exitAnim)

用法很简单:在startActivity(intent)或者finish()后添加

参数依次是:新Activity进场时的动画,以及旧Activity退场时的动画

Intent intent = new Intent(MainActivity3.this, MainActivity4.class);
startActivity(intent);
overridePendingTransition(R.anim.anim_alpha, R.anim.anim_scale);
 
posted @ 2022-07-26 18:44  张帅贝  阅读(268)  评论(0编辑  收藏  举报