Android动画基础详析 | 概述、逐帧动画、视图动画(附诸多实际运行效果动图)

简介: Android动画基础详析 | 概述、逐帧动画、视图动画(附诸多实际运行效果动图)
为了描述方便,下文中我们把执行动画的组件暂时称为“目标组件”;

1.1 概述

  • 动画的意义:

视觉效果(良好观感)、
引导用户(理解我们的应用功能);

  • 下文将笔记:

逐帧动画、
视图动画、
属性动画

  • 逐帧动画:逐帧动画的基础是帧,也即图片,图片一般由美工制作;

     没有原图就无法制作逐帧动画,则应用范围比较小;

  • 视图动画:应用广泛;

     操作的是视图对象,可以令视图对象产生透明度渐变、位移、旋转等效果
     但是也有它的局限性(局限于视图)

  • 属性动画:操作的对象不再局限于视图,可以真实地改变对象的属性

2 逐帧动画

  • 概述:

逐帧动画也称图片动画,
通过在一个固定区域,
逐张地呈现一系列事先加载好的图片而产生动画效果;

  • 定义逐帧动画的方法:

使用AnimationDrawable对象定义逐帧动画;
它是一个Drawable容器(DrawableContainer)
可以理解为,它事先加载好了一系列的图片
普通的Drawable一样,它可以被设为视图对象的背景

- **```最简单的定义逐帧动画的方法,```**
    - 在drawable文件夹下新建一个xml文件;
    - 在xml文件中,通过<animation-list>来定义AnimationDrawable对象;

一个对应一个AnimationDrawable对象;

    - <animation-list>的下层包含若干个<item>,

即一个帧动画对应的一套的若干个帧图;

    - 每一个item设置好每一个帧的图片以及对应的延时;
  如此便定义好了AnimationDrawable对象;
- **```接着使用AnimationDrawable,```**
    - 在某个Activity或者fragment的布局文件中,开启一个<View>控件,

作为帧动画的展示区域;
可以自由设置这个区域的大小;

    - 通过View控件的属性android:background将定义好的AnimationDrawable对象(drawable/xml's name——<animation-list>)设置进来作为View控件的背景;
    - 在布局文件对应的Java代码处,实例化这个View控件;
    - 通过(AnimationDrawable)view.getBackground();获得view的背景Drawable对象并向下转型为AnimationDrawable对象,

然后赋给一个AnimationDrawable的实例变量引用;

    - 使用AnimationDrawable的实例调用start()、stop(),

即可分别做帧动画的开启和关闭

    - 可以在Activity.java中,

通过animationDrawable.setOneShot(true);
将动画设置为只播放这套帧图一次;
或者给添加android:oneshot="true"属性,也可实现;

  • 小结:

逐帧动画的基础是帧,也即图片,图片一般由美工制作;
没有原图就无法制作逐帧动画,则应用范围比较小;
将一套帧图设置在(AnimationDrawable)容器中,
并且这个(AnimationDrawable)需要作为一个View的背景(android:background),
依赖View来实现;

  • 下面做一个demo:

    • 首先新建一个项目,准备好三张图片:

frame_1.png
frame_2.png
frame_3.png

- 在drawable文件夹下新建一个xml文件,

文件目录:

并编写如下代码:
代码中通过来定义AnimationDrawable对象,
一个animation-list便签对应(映射为)一个AnimationDrawable对象;
每个item为一帧,
android:drawable 属性设置帧图;
android:duration 设置延时,单位为ms;
<?xml version="1.0" encoding="utf-8"?>
<animation-list xmlns:android="http://schemas.android.com/apk/res/android">
    <item android:drawable="@drawable/frame_1"
        android:duration="100"/>

    <item android:drawable="@drawable/frame_2"
        android:duration="100"/>

    <item android:drawable="@drawable/frame_3"
        android:duration="100"/>
</animation-list>
如此便定义好了AnimationDrawable对象;
  • 现在编写xml布局(activity_frame_animation):
<?xml version="1.0" encoding="utf-8"?>
<RelativeLayout xmlns:android="http://schemas.android.com/apk/res/android"
    xmlns:app="http://schemas.android.com/apk/res-auto"
    xmlns:tools="http://schemas.android.com/tools"
    android:layout_width="match_parent"
    android:layout_height="match_parent"
    tools:context=".FrameAnimationActivity">

    <View
        android:id="@+id/view"
        android:background="@drawable/loading"
        android:layout_centerInParent="true"
        android:layout_width="300dp"
        android:layout_height="300dp"/>

    <LinearLayout
        android:orientation="horizontal"
        android:layout_width="match_parent"
        android:layout_height="wrap_content"
        android:layout_alignParentBottom="true">
        <Button
            android:id="@+id/btnStart"
            android:text="Start"
            android:onClick="onClick"
            android:layout_width="0dp"
            android:layout_weight="1"
            android:layout_height="wrap_content" />
        <Button
            android:id="@+id/btnStop"
            android:text="Stop"
            android:onClick="onClick"
            android:layout_width="0dp"
            android:layout_weight="1"
            android:layout_height="wrap_content" />
    </LinearLayout>

</RelativeLayout>
View组件即帧动画的展示区域;
android:background 属性,把方才定义好的AnimationDrawable对象(loading.xml)设置进来;
两个button用来调试动画;
  • 接着是FrameAnimationActivity:
public class FrameAnimationActivity extends AppCompatActivity {

    private AnimationDrawable animationDrawable;

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

        View view = findViewById(R.id.view);
        //view.getBackground()返回的是一个Drawable对象,在这里已经明确背景是AnimationDrawable,
        // 所以可以做一个强制转换(窄化、向下转型)
        animationDrawable = (AnimationDrawable)view.getBackground();
    }

    public void onClick(View view){
        switch (view.getId()){
            case R.id.btnStart:
                animationDrawable.start();
                break;
            case R.id.btnStop:
                animationDrawable.stop();
                break;
        }
    }
}
  • 实例化方才设置了AnimationDrawable为背景的View对象;
  • 使用view实例getBackground()获得背景;
  • view.getBackground()返回的是一个Drawable对象,

在这里已经明确背景是AnimationDrawable,
所以可以做一个强制转换(窄化、向下转型);

  • 后面是简单的点击事件:

使用AnimationDrawable的实例调用start()、stop(),
分别做帧动画的开启和关闭;

Tip1 查看继承关系

光标移动到AnimationDrawable上,
ctrl + h 可以查看继承关系,
我们可以看到AnimationDrawable是Drawable的子类:

查看类的结构
  • Windows:ctrl + F12
  • Mac:cmd + F12
查看继承关系
  • Windows:ctrl + h
  • Mac:control + h
  • 运行效果如下:
  • 最后,

可以看到帧动画默认是对设置好的一系列帧图做循环往复的播放的,
可以在Activity.java中,通过animationDrawable.setOneShot(true);
将动画设置为只播放这套帧图一次:
或者给添加android:oneshot="true"属性,也可实现:

3.1 视图动画. 原理

上面说过,
逐帧动画的基础是帧,也即图片,图片一般由美工制作;
没有原图就无法制作逐帧动画,则应用范围比较小;
将一套帧图设置在(AnimationDrawable)容器中,
并且这个(AnimationDrawable)需要作为一个View的背景(android:background),
依赖View来实现;

视图动画则直接操作视图对象(View、TextView、Button、ImageView),
是之产生对应的动画;
可以令视图对象产生透明度渐变、位移、旋转等效果;

  • 对应的类:

    • android.view.animation
    • Animation

      • AlphaAnimation
      • ScaleAnimation
      • TranslateAnimation
      • RotateAnimation
      • AnimationSet
  • 实现的机制——补间动画:

对于动画,
给定一个视图对象的一套起点参数、一套终点参数和一个过程时长即可,
补间动画根据提供的参数自动地进行一个过程的变换;

  • 实现的底层原理:

每个视图对象都有一个变换矩阵
用于把视图映射到手机屏幕上,
对这个变换矩阵在单位时间内做对应的数据变更,
即可以使视图产生各种运动效果;

3.2 视图动画. 透明度动画(AlphaAnimation)

  • 建立:可以在xml资源文件中建立,也可以在java文件中建立;
  • 每一个标签对应一个AlphaAnimation对象;
  • 控制视图实现从一个透明度到另一个透明度的变换;
  • 下面做一个demo:

    • 首先,整理刚才的项目,

帧动画的跟视图动画的我们分包处理,
然后新建一个ViewAnimationActivity空Activity:

  • res文件夹下新建anim文件夹,对anim文件夹新建一个资源文件,名为alpha:

编写alpha.xml:

<?xml version="1.0" encoding="utf-8"?>
<set xmlns:android="http://schemas.android.com/apk/res/android">

    <alpha 
        android:duration = "1000"
        android:fromAlpha="1.0"
        android:toAlpha="0.1"/>
</set>
  • 属性说明:

android:duration 补间时长
android:fromAlpha 起始透明度
android:toAlpha 终止时透明度
以上,一个简单的透明度动画即准备完毕;

编写布局activity_view_animation.xml:

<?xml version="1.0" encoding="utf-8"?>
<ScrollView xmlns:android="http://schemas.android.com/apk/res/android"
    xmlns:app="http://schemas.android.com/apk/res-auto"
    xmlns:tools="http://schemas.android.com/tools"
    android:layout_width="match_parent"
    android:layout_height="match_parent"
    tools:context=".view.ViewAnimationActivity">

    <LinearLayout
        android:orientation="vertical"
        android:layout_width="match_parent"
        android:layout_height="wrap_content">

        <TextView
            android:id="@+id/viewAlphaAnimation"
            android:layout_gravity="center_horizontal"
            android:textSize="50dp"
            android:text="Alpha"
            android:onClick="onClick"
            android:padding="16dp"
            android:layout_width="wrap_content"
            android:layout_height="wrap_content"
            android:textAppearance="@style/Base.TextAppearance.AppCompat.Large"
            android:background="@color/colorPrimary"
            android:textColor="@android:color/white"/>
    </LinearLayout>
</ScrollView>
这里最外层是一个滚动布局,
然后嵌入线性布局,再嵌入TV,TV用来做AlphaAnimation;

编写ViewAnimationActivity:

onClick(View view)//view这里指的是点击了的id为viewAlphaAnimation的TV
public class ViewAnimationActivity extends AppCompatActivity {

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

    public void onClick(View view){
        switch (view.getId()){
            case R.id.viewAlphaAnimation:
                Animation alphaAnimation = AnimationUtils.loadAnimation(this, R.anim.alpha);
                view.startAnimation(alphaAnimation);
                break;
        }
    }
}

核心代码就两句:

Animation alphaAnimation = AnimationUtils.loadAnimation(this, R.anim.alpha);
view.startAnimation(alphaAnimation);
  • 第一句,

调用AnimationUtils中的loadAnimation(),
把刚才定义好的alpha.xml动画资源文件加载进来,
一参为context,二参为资源文件id;

  • 第二句,

要实现动画的视图实例(这里是刚刚编写的TV)调用startAnimation(),
把第一句中加载转换得到的Animation对象设置进来;

至此,便实现AlphaAnimation;



  • 以上,是通过xml的方法定义的动画,事实上我们还有一种简洁的定义动画的方法:

    • 如下,我们直接在目标视图对应的java文件中,

写下这三行代码,
也就是调用AlphaAnimation构造方法,直接实现动画的创建;
setDuration()用来设置动画延时:

运行效果一样:



下面是一个基于源码,关于applyTransformation()方法的知识普及:
Animation类继承结构:我们进入Animation类的源码,可以看到这么一个方法——applyTransformation(),其实这个方法就跟我们方才说的 “每个视图对象都通过一个转换矩阵映射到手机屏幕上” 中的 转换矩阵 有关:
它的不同子类对这个方法进行了不同的实现,
比如我们看一下AlphaAnimation类的源码:可以看到AlphaAnimation类中applyTransformation()的实现就是调节透明度;

  • 另外,我们可以看到,用于开启动画的方法startAnimation()是定义在View类中的:
  • 这其实说明每一个View类对象及其子类对象都可以做视图动画;
  • 小结:

    • 定义以及使用透明度动画(xml法):
    • 建立文件夹res/anim;
    • 在其下新建一个xml;
    • xml中编写标签,指定duration、fromAlpha、toAlpha等属性;
    • 在目标视图的java文件处,

调用AnimationUtils.loadAnimation()把上述定义了标签的xml加载进来,
转换成Animation对象;

  • 目标视图实例以上述转换得到的Animation对象为参数调用startAnimation(),

完成动画开启;

java代码法:

AlphaAnimation aa = new AlphaAnimation(1,0);
               aa.setDuration(1000);
               view.startAnimation(aa);

3.3 视图动画. 缩放动画(ScaleAnimation)

  • 建立:可以在xml资源文件中建立,也可以在java文件中建立;
  • 每一个标签对应一个ScaleAnimation对象;
  • 控制视图实现在X轴、Y轴上从一个缩放程度到另一个缩放程度的变换;
  • 下面做一个demo:

同理上方,这里简述了;

  • res文件夹下新建一个资源文件,名为scale;对其进行编写:
<?xml version="1.0" encoding="utf-8"?>
<set xmlns:android="http://schemas.android.com/apk/res/android">

    <scale
        android:duration="1000"
        android:fromXScale="1.0"
        android:toXScale="2.0"
        android:fromYScale="1.0"
        android:toYScale="1.0"/>
</set>
  • 属性说明:

android:duration 补间时长
android:fromXScale/android:toXScale 起始X轴缩放度比例,
                 1表示原图比例,即不缩放;
android:fromYScale/android:toYScale 起始Y轴缩放度比例
以上,一个简单的缩放动画即准备完毕;

  • 值=0.0 :表示收缩到没有
  • 值<1.0 :表示收缩
  • 值=1.0 :表示无伸缩
  • 值>1.0 :表示放大

编写布局activity_view_animation.xml:

Tip2 抽取控件属性为Style文件

  • 接着会弹出下面这个窗口,打钩表示要抽取的属性,Style name表示待会儿自动创建的style标签名:
  • 点击ok之后便抽取完毕:
  • 如下,抽象出来的样式会放在values文件夹下的style.xml文件中,用一个style便签存储起来:

下面是activity_view_animation.xml:

<?xml version="1.0" encoding="utf-8"?>
<ScrollView xmlns:android="http://schemas.android.com/apk/res/android"
   ...
    tools:context=".view.ViewAnimationActivity">

    <LinearLayout
        android:orientation="vertical"
        android:layout_width="match_parent"
        android:layout_height="wrap_content">

        <TextView
            android:id="@+id/viewAlphaAnimation"
            ...
            style="@style/AnimationTextView" />

        <TextView
            android:id="@+id/viewScaleAnimation"
            android:layout_gravity="center_horizontal"
            android:textSize="50dp"
            android:text="Scale"
            android:onClick="onClick"
            android:layout_width="wrap_content"
            android:layout_height="wrap_content"
            style="@style/AnimationTextView" />

    </LinearLayout>
</ScrollView>

到java文件:

public class ViewAnimationActivity extends AppCompatActivity {

    @Override
    protected void onCreate(Bundle savedInstanceState) {
       ...
    }

    public void onClick(View view){
        switch (view.getId()){
            case R.id.viewAlphaAnimation:
//               ...
//                view.startAnimation(alphaAnimation);//view这里指的是点击了的id为viewAlphaAnimation的TV
                ...
                break;
            case R.id.viewScaleAnimation:
                Animation scaleAnimation = AnimationUtils.loadAnimation(this, R.anim.scale);
                view.startAnimation(scaleAnimation);
                break;
        }
    }
}
代码同理透明度动画,至此完成设置;

运行:

  • 添加一个属性 android:fillAfter="true",使动画结束后View组件保持结束时的状态:
<?xml version="1.0" encoding="utf-8"?>
<set xmlns:android="http://schemas.android.com/apk/res/android"
    android:duration="1000"
    android:fillAfter="true">

    <scale
        android:fromXScale="1.0"
        android:toXScale="2.0"
        android:fromYScale="1.0"
        android:toYScale="1.0"/>
</set>

效果:
java代码实现:

case R.id.viewScaleAnimation:
//                Animation scaleAnimation = AnimationUtils.loadAnimation(this, R.anim.scale);
//                view.startAnimation(scaleAnimation);
                ScaleAnimation sa = new ScaleAnimation(1,2,1,1);
                sa.setDuration(1000);
                view.startAnimation(sa);
                Toast.makeText(this,"我是java定义的缩放动画",Toast.LENGTH_SHORT).show();
                break;

运行效果同上;



- 动画基准点概念

动画的运作都是有一个基准点的,
而默认的基准点是在目标组件的左上角,
用xml描述即:把代码改成上图所示如此,运行效果也会跟刚才的一样;

  • 当然了,事情没那么简单,基准点的这个参数设置是有讲究的,

默认是设置为0,其实它有三种描述样式,如下:

  • 50:以View左上角为原点沿坐标轴正方向偏移50px;
  • 50%:以View左上角为原点沿坐标轴正方向偏移View宽/高度的50%;
  • 50%p:以View左上角为原点沿坐标轴正方向偏移父(parent)控件宽/高度的50%。区别如图:图片来源:https://www.jianshu.com/p/10dc575896d3

demo(修改scale.xml):

<?xml version="1.0" encoding="utf-8"?>
<set xmlns:android="http://schemas.android.com/apk/res/android"
    android:duration="1000"
    android:fillAfter="true">

    <scale
        android:fromXScale="1.0"
        android:toXScale="2.0"
        android:fromYScale="1.0"
        android:toYScale="2.0"
        android:pivotX="50%"
        android:pivotY="50%"/>
</set>

java:

ase R.id.viewScaleAnimation:
                Animation scaleAnimation = AnimationUtils.loadAnimation(this, R.anim.scale);
                view.startAnimation(scaleAnimation);

运行:
java实现:

case R.id.viewScaleAnimation:
//                Animation scaleAnimation = AnimationUtils.loadAnimation(this, R.anim.scale);
//                view.startAnimation(scaleAnimation);
                ScaleAnimation sa = new ScaleAnimation(1,2,1,2,
                        Animation.RELATIVE_TO_SELF,0.5F,
                        Animation.RELATIVE_TO_SELF,0.5F);
                sa.setDuration(1000);
                sa.setFillAfter(true);
                view.startAnimation(sa);
                Toast.makeText(this,"我是java定义的缩放动画",Toast.LENGTH_SHORT).show();
                break;

运行效果同上;

使用. 小结:
似同透明度动画,
xml法则定义动画资源xml文件,
在java处调用loadAnimation()把xml加载进来,
视图实例调用startAnimation()开启动画即可;
java定义法,则:

ScaleAnimation sa = new ScaleAnimation(1,2,1,1);
               sa.setDuration(1000);
               view.startAnimation(sa);

3.4 视图动画. 位移动画(TranslateAnimation)

建立:可以在xml资源文件中建立,也可以在java文件中建立;
每一个标签对应一个TranslateAnimation对象;
控制视图实现在X轴、Y轴上从一个坐标到另一个坐标的移动变换;
下面做一个demo:
同理上方,这里简述了;
res文件夹下新建一个资源文件,名为translate;对其进行编写:

<?xml version="1.0" encoding="utf-8"?>
<set xmlns:android="http://schemas.android.com/apk/res/android"
    android:duration="1000"
    android:fillAfter="true">
    <translate
        android:fromXDelta="0"
        android:fromYDelta="0"
        android:toXDelta="100%"
        android:toYDelta="100%" />
</set>
  • 属性说明:

fromXDelta/fromYDelta 从X轴/Y轴起始点
toXDelta/toYDelta 到X轴/Y轴终点
注意这里描述起始点与终点的方式似同于上方描述动画基准点的方式;

Tip3.0 格式化代码功能

这里调整了一下style:

   <style name="AnimationTextView">
       <item name="android:textSize">25dp</item>
       <item name="android:padding">20dp</item>
       <item name="android:layout_margin">16dp</item>
       <item >name="android:textAppearance">@style/Base.TextAppearance.AppCompat.Large</item>
       <item name="android:background">@color/colorPrimary</item>
       <item name="android:textColor">@android:color/white</item>
   </style>

下面是activity_view_animation.xml:

<?xml version="1.0" encoding="utf-8"?>
<ScrollView xmlns:android="http://schemas.android.com/apk/res/android"
    xmlns:app="http://schemas.android.com/apk/res-auto"
    xmlns:tools="http://schemas.android.com/tools"
    android:layout_width="match_parent"
    android:layout_height="match_parent"
    tools:context=".view.ViewAnimationActivity">

    <LinearLayout
        android:orientation="vertical"
        android:layout_width="match_parent"
        android:layout_height="wrap_content">

        <TextView
            android:id="@+id/viewAlphaAnimation"
            android:layout_gravity="center_horizontal"
            android:text="Alpha"
            android:onClick="onClick"
            android:layout_width="wrap_content"
            android:layout_height="wrap_content"
            style="@style/AnimationTextView" />

        <TextView
            android:id="@+id/viewTranslateAnimation"
            android:layout_gravity="center_horizontal"
            android:text="translate"
            android:onClick="onClick"
            android:layout_width="wrap_content"
            android:layout_height="wrap_content"
            style="@style/AnimationTextView" />

        <TextView
            android:id="@+id/viewScaleAnimation"
            android:layout_gravity="center_horizontal"
            android:text="Scale"
            android:onClick="onClick"
            android:layout_width="wrap_content"
            android:layout_height="wrap_content"
            style="@style/AnimationTextView" />


    </LinearLayout>
</ScrollView>

java:

public class ViewAnimationActivity extends AppCompatActivity {

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

    public void onClick(View view){
        switch (view.getId()){
            case R.id.viewAlphaAnimation:
...
                break;
            case R.id.viewScaleAnimation:
...
            case R.id.viewTranslateAnimation:
                Animation translateAnimation = AnimationUtils.loadAnimation(this, R.anim.translate);
                view.startAnimation(translateAnimation);
                break;
        }
    }
}

运行效果:

Tip 3.1自定义背景样式描边父布局

  • Drawable文件夹下面新建一个文件,edge.xml
<?xml version="1.0" encoding="utf-8"?>
<shape  xmlns:android="http://schemas.android.com/apk/res/android">
   <solid android:color="@android:color/transparent"/>
   <stroke android:color="@android:color/black"
       android:width="1px"/>
</shape>

中心透明,边界线1px宽度黑线

将edge.xml设置为LinearLayout的背景:

   <LinearLayout
       android:background="@drawable/edge"
       android:orientation="vertical"
       android:layout_width="match_parent"
       android:layout_height="wrap_content">

修改translate.xml:

<?xml version="1.0" encoding="utf-8"?>
<set xmlns:android="http://schemas.android.com/apk/res/android"
    android:duration="1000"
    android:fillAfter="true">
    <translate
        android:fromXDelta="0"
        android:fromYDelta="0"
        android:toXDelta="50%"
        android:toYDelta="100%" />
</set>

activity_view_animation.xml:(视图放在最后一个)

<?xml version="1.0" encoding="utf-8"?>
<ScrollView...
    tools:context=".view.ViewAnimationActivity">

    <LinearLayout
        android:background="@drawable/edge"
        android:orientation="vertical"
        android:layout_width="match_parent"
        android:layout_height="wrap_content"
        android:padding="16dp">

        ...

        <TextView
            android:id="@+id/viewTranslateAnimation"
            android:layout_gravity="center_horizontal"
            android:text="translate"
            android:onClick="onClick"
            android:layout_width="wrap_content"
            android:layout_height="wrap_content"
            style="@style/AnimationTextView" />
    </LinearLayout>
</ScrollView>

style:

<style name="AnimationTextView">
        <item name="android:textSize">25dp</item>
        <item name="android:padding">20dp</item>
        <item name="android:layout_margin">5dp</item>
        <item name="android:textAppearance">@style/Base.TextAppearance.AppCompat.Large</item>
        <item name="android:background">@color/colorPrimary</item>
        <item name="android:textColor">@android:color/white</item>
    </style>

运行结果:
我们可以看到动画视图发生了偏移,
但是超出父控件的部分是不能被绘制出来的;(有上面的定制,描边区域内即父控件)

可以知道上文的 缩放动画 其实有一部分是超出了父布局,从而没有显示出来
  • 要权宜解决这个问题也很简单,

我们可以在第三个视图下添加一个空白View,
便可以扩大父布局区域,
从而可以看到完整的偏移动画了:

<?xml version="1.0" encoding="utf-8"?>
<ScrollView xmlns:android="http://schemas.android.com/apk/res/android"
    ...
    tools:context=".view.ViewAnimationActivity">

    <LinearLayout
       ...
        android:padding="16dp">

        <TextView
            ...
            style="@style/AnimationTextView" />

        <TextView
           ...
            style="@style/AnimationTextView" />

        <TextView
            android:id="@+id/viewTranslateAnimation"
            android:layout_gravity="center_horizontal"
            android:text="translate"
            android:onClick="onClick"
            android:layout_width="wrap_content"
            android:layout_height="wrap_content"
            style="@style/AnimationTextView" />
        
        <View
            android:layout_width="match_parent"
            android:layout_height="200dp"/>
    </LinearLayout>
</ScrollView>

运行效果:

使用. 小结:
似同透明度动画,

  • xml法

1.定义动画资源xml文件,
2.在java处调用loadAnimation()把xml加载进来,
3.视图实例调用startAnimation()开启动画即可;

  • java定义法,则:
TranslateAnimation ta = new TranslateAnimation(
                       Animation.RELATIVE_TO_SELF,0,
                       Animation.RELATIVE_TO_SELF,0.5F,
                       Animation.RELATIVE_TO_SELF,0,
                       Animation.RELATIVE_TO_SELF,1);
               ta.setFillAfter(true);
               ta.setDuration(1000);
               view.startAnimation(ta);



- 其实关于TranslateAnimation 等子类的定义也是大同小异,
大概知道使用方法之后,
可以通过AS查看各个类的源码,
知晓其更多的构造方法,
根据需要使用不同的构造方法定义动画;

3.5 视图动画. 旋转动画(RotateAnimation)

建立:可以在xml资源文件中建立,也可以在java文件中建立;
每一个标签对应一个RotateAnimation对象;
控制视图实现在X轴、Y轴上从一个坐标到另一个坐标的移动变换;
下面做一个demo:
同理上方,这里简述了;

res文件夹下新建一个资源文件,名为rotate;对其进行编写:

<?xml version="1.0" encoding="utf-8"?>
<set xmlns:android="http://schemas.android.com/apk/res/android"
    android:fillAfter="true"
    android:duration="1000">

    <rotate
        android:fromDegrees="0"
        android:toDegrees="270"/>
</set>

属性说明:
fromDegrees、android:toDegrees :起始、终止角度

  • from=负数->to=正数:表示顺时针旋转
  • from=负数->to=负数:表示逆时针旋转
  • from=正数->to=正数:表示顺时针旋转
  • from=正数->to=负数:表示逆时针旋转

activity_view_animation.xml:

<?xml version="1.0" encoding="utf-8"?>
<ScrollView xmlns:android="http://schemas.android.com/apk/res/android"
    ...
    tools:context=".view.ViewAnimationActivity">

    <LinearLayout
        android:background="@drawable/edge"
       ...>

        <TextView
            android:id="@+id/viewAlphaAnimation"
            .../>

        <TextView
            android:id="@+id/viewScaleAnimation"
            .../>

        <TextView
            ... />

        <TextView
            android:id="@+id/viewRotateAnimation"
            android:layout_gravity="center_horizontal"
            android:text="rotate"
            android:onClick="onClick"
            android:layout_width="wrap_content"
            android:layout_height="wrap_content"
            style="@style/AnimationTextView" />

        <View.../>
    </LinearLayout>
</ScrollView>

ViewAnimationActivity.java:

            case R.id.viewRotateAnimation:
                Animation rotateAnimation = AnimationUtils.loadAnimation(this, R.anim.rotate);
                view.startAnimation(rotateAnimation);

运行效果:

Tip4 创建一个简单的菜单刷新按钮

Tip 首先添加一个icon

然后点击ok:然后点击next,然后finsh,这样之后会在Drawable目录下生成一段vector的xml:
将之设计成白色:

<vector xmlns:android="http://schemas.android.com/apk/res/android"
       android:width="24dp"
       android:height="24dp"
       android:viewportWidth="24.0"
       android:viewportHeight="24.0">
   <path
       android:fillColor="#FFF"
       android:pathData="M12,6v3l4,-4 -4,-4v3c-4.42,0 -8,3.58 -8,8 0,1.57 0.46,3.03 1.24,4.26L6.7,14.8c-0.45,-0.83 -0.7,-1.79 -0.7,-2.8 0,-3.31 2.69,-6 6,-6zM18.76,7.74L17.3,9.2c0.44,0.84 0.7,1.79 0.7,2.8 0,3.31 -2.69,6 -6,6v-3l-4,4 4,4v-3c4.42,0 8,-3.58 8,-8 0,-1.57 -0.46,-3.03 -1.24,-4.26z"/>
</vector>

Tip 建立菜单文件夹 & 文件


<?xml version="1.0" encoding="utf-8"?>
<menu xmlns:android="http://schemas.android.com/apk/res/android"
   xmlns:app="http://schemas.android.com/apk/res-auto">
   <item
       android:id="@+id/renew"
       android:title="Renew"
       app:showAsAction = "always"
       android:icon="@drawable/ic_autorenew_black_24dp"/>
</menu>

接着回java文件编写加载布局以及点击事件:

   @Override
  public boolean onCreateOptionsMenu(Menu menu) {
       getMenuInflater().inflate(R.menu.renew, menu);
       return super.onCreateOptionsMenu(menu);
   }

   @Override
   public boolean onOptionsItemSelected(MenuItem item) {
       switch (item.getItemId()){
           case R.id.renew:
               recreate();
               break;
       }
       return super.onOptionsItemSelected(item);
   }

运行,结果如下(点击刷新按钮,Activity重建“刷新”):

  • 旋转动画同样可以设置基准点
  • 下面的代码表示让视图以本身中点为旋转中心旋转无限次,

每次的时长为300ms:
android:repeatCount属性可以指定重复次数;
android:repeatCount=3便是重复3次;

<?xml version="1.0" encoding="utf-8"?>
<set xmlns:android="http://schemas.android.com/apk/res/android"
    android:fillAfter="true">

    <rotate
        android:duration="1000"
        android:pivotX="50%"
        android:pivotY="50%"
        android:fromDegrees="0"
        android:toDegrees="360"
        android:repeatCount="infinite"/>
</set>
  • 另外还有一个属性android:repeatMode

即决定,
下次开始的时候,以什么样的方式开始——重新开始还是原路返回:
这个属性默认值是restart,即android:repeatMode="restart",即重新开始;
我们用为位移动画来诠释:
更改translate.xml:

<?xml version="1.0" encoding="utf-8"?>
<set xmlns:android="http://schemas.android.com/apk/res/android"
android:fillAfter="true">
    <translate
        android:duration="2000"
        android:repeatCount="1"
        android:fromXDelta="0"
        android:fromYDelta="0"
        android:toXDelta="80%p"
        android:toYDelta="0" />
</set>
  • 运行,效果如下:

其中位移动画android:repeatCount="1",则一轮动画之后又重复了一次;
下次动画开始的时候,是重新开始的方式开始,
也即方才说的 默认android:repeatMode="restart";

  • 以及,其中旋转动画是方才android:repeatCount="infinite"部分的代码的效果;
  • 现在更改translate.xml:
<?xml version="1.0" encoding="utf-8"?>
<set xmlns:android="http://schemas.android.com/apk/res/android"
android:fillAfter="true">
    <translate
        android:duration="2000"
        android:repeatCount="1"
        android:repeatMode="reverse"
        android:fromXDelta="0"
        android:fromYDelta="0"
        android:toXDelta="80%p"
        android:toYDelta="0" />
</set>

运行,效果如下:

  • 即下次动画开始的时候,是原路返回的方式开始;

使用. 小结:
似同透明度动画,

  • xml法 则

1.定义动画资源xml文件,
2.在java处调用loadAnimation()把xml加载进来,
3.视图实例调用startAnimation()开启动画即可;

  • java定义法,则:
           case R.id.viewTranslateAnimation:
               TranslateAnimation ta = new TranslateAnimation(
                       Animation.RELATIVE_TO_PARENT,0,
                       Animation.RELATIVE_TO_PARENT,(0.8F),
                       Animation.RELATIVE_TO_SELF,0,
                       Animation.RELATIVE_TO_SELF,0);
               ta.setRepeatCount(1);
               ta.setRepeatMode(REVERSE);
               ta.setFillAfter(true);
              ta.setDuration(2000);
              view.startAnimation(ta);
               break;
           case R.id.viewRotateAnimation:
               RotateAnimation ra = new RotateAnimation(0,360,
                       Animation.RELATIVE_TO_SELF,0.5F,
                       Animation.RELATIVE_TO_SELF,0.5F);
               ra.setDuration(1000);
               ra.setFillAfter(true);
               ra.setRepeatCount(INFINITE);
               view.startAnimation(ra);
               break;

效果跟方才的动画效果是一样的;

3.6 集合动画

建立:可以在xml资源文件中建立,也可以在java文件中建立;
每一个标签对应一个AnimationSet对象;
控制视图实现复合动画;
下面做一个demo:
同理上方,这里简述了;

res文件夹下新建一个资源文件,名为set.xml ;对其进行编写:

<?xml version="1.0" encoding="utf-8"?>
<set xmlns:android="http://schemas.android.com/apk/res/android">
    <rotate
        android:duration = "1000"
        android:pivotX="50%"
        android:pivotY="50%"
        android:fromDegrees="0"
        android:toDegrees="720"/>
    <translate
        android:duration="1000"
        android:fromXDelta="0"
        android:toXDelta="500"
        android:fromYDelta="0"
        android:toYDelta="0"/>
</set>

activity_view_animation.xml:

<?xml version="1.0" encoding="utf-8"?>
<ScrollView ...>

    <LinearLayout...>

        <TextView
            ...

        <TextView
            android:id="@+id/viewSetAnimation"
            android:text="Set"
            android:onClick="onClick"
            android:layout_width="wrap_content"
            android:layout_height="wrap_content"
            style="@style/AnimationTextView" />

        <View
            android:layout_width="match_parent"
            android:layout_height="200dp"/>
    </LinearLayout>
</ScrollView>

ViewAnimationActivity.java:

            case R.id.viewSetAnimation:
                Animation setAnimation = AnimationUtils.loadAnimation(this, R.anim.set);
                view.startAnimation(setAnimation);

运行效果:
修改set.xml,给增加属性android:startOffset="1000"
(属性说明:即整个复合动画开始1000ms后,再运行中的内容):

<?xml version="1.0" encoding="utf-8"?>
<set xmlns:android="http://schemas.android.com/apk/res/android">
    <rotate
        .../>
    <translate
        ...
        android:startOffset="1000"
        .../>
</set>

运行效果:

  • java方法(能实现,但是有误差,不推荐):
case R.id.viewSetAnimation:
                AnimationSet as = new AnimationSet(true);
                as.setDuration(2000);

                RotateAnimation ra1 = new RotateAnimation(0,720,
                        Animation.RELATIVE_TO_SELF,0.5F,
                        Animation.RELATIVE_TO_SELF,0.5F);
                ra1.setDuration(1000);
                as.addAnimation(ra1);

                TranslateAnimation ta1 = new TranslateAnimation(0,500,0,0);
                ta1.setDuration(1000);
                ta1.setStartOffset(1000);
                as.addAnimation(ta1);

                view.startAnimation(as);

3.7 插值器

  • 在定义动画的时候可以通过设置插值器,

来设置动画在不同时间点的不同变化率;
(变化率即动画完成进度同动画完成所需时间的微分)

0.定义控件:

        <View
            android:id="@+id/viewAccelerate"
            android:onClick="onClick"
            style="@style/InterpolatorView" />

        <View
            android:id="@+id/viewLinear"
            android:alpha="0.5"
            android:onClick="onClick"
            style="@style/InterpolatorView" />

(这里其实跟前面的动画定义都差不多,只是多了一步设置插值器)

  1. 加载动画xml;(AnimationUtils.loadAnimation())

2.设置插值器;(setInterpolator())
3.开启动画;(startAnimation())

case R.id.viewAccelerate:
                View viewAccelerate = findViewById(R.id.viewAccelerate);
                View viewLinear = findViewById(R.id.viewLinear);

                Animation animationLinear = AnimationUtils.loadAnimation(this,R.anim.translate);
                Animation animationAccelerate = AnimationUtils.loadAnimation(this,R.anim.translate);

                animationLinear.setInterpolator(new LinearInterpolator());
                animationAccelerate.setInterpolator(new AccelerateInterpolator());

                viewLinear.startAnimation(animationLinear);
                viewAccelerate.startAnimation(animationAccelerate);

                break;

  • 或者这样定义:
  1. 创建动画实例;

2.设置插值器;(setInterpolator())
3.开启动画;(startAnimation())

        Animation alphaAnimation = new AlphaAnimation(1,0);
        // 步骤1:创建透明度动画的对象 & 设置动画效果

        alphaAnimation.setDuration(3000);
        alphaAnimation.setInterpolator(new OvershootInterpolator());
        // 步骤2:给动画设置插值器

        view.startAnimation(alphaAnimation);
        // 步骤3:开启动画

运行效果:

Tip5 duration的默认值

Android给duration封装了几个默认值,能够设置更形象的动画时长;


  • 本文内容参考自 慕课网. 就业班
相关文章
|
1月前
|
算法 数据处理 Android开发
掌握安卓性能优化的秘诀:电池寿命与运行效率的提升
【10月更文挑战第6天】 本文深入探讨了安卓应用开发中的性能优化技巧,重点分析了影响电池寿命和运行效率的关键因素,并提供了针对性的优化策略。通过代码优化、资源管理、后台任务处理等方法,开发者可以显著提升应用的续航能力和流畅度。同时,结合具体案例,展示了如何在实际开发中应用这些技巧,确保应用在各种场景下都能保持高效运行。本文旨在为安卓开发者提供实用的性能优化指导,助力其打造更优质的应用体验。
46 2
|
1月前
|
Android开发 开发者
安卓应用开发中的自定义视图
【9月更文挑战第37天】在安卓开发的海洋中,自定义视图犹如一座座小岛,等待着勇敢的探索者去发现其独特之处。本文将带领你踏上这段旅程,从浅滩走向深海,逐步揭开自定义视图的神秘面纱。
38 3
|
3月前
|
存储 Shell Android开发
基于Android P,自定义Android开机动画的方法
本文详细介绍了基于Android P系统自定义开机动画的步骤,包括动画文件结构、脚本编写、ZIP打包方法以及如何将自定义动画集成到AOSP源码中。
76 2
基于Android P,自定义Android开机动画的方法
|
3月前
|
供应链 物联网 区块链
未来触手可及:探索新兴技术的趋势与应用安卓开发中的自定义视图:从基础到进阶
【8月更文挑战第30天】随着科技的飞速发展,新兴技术如区块链、物联网和虚拟现实正在重塑我们的世界。本文将深入探讨这些技术的发展趋势和应用场景,带你领略未来的可能性。
|
3月前
|
测试技术 Android开发 Python
探索软件测试的艺术:从基础到高级安卓应用开发中的自定义视图
【8月更文挑战第29天】在软件开发的世界中,测试是不可或缺的一环。它如同艺术一般,需要精细的技巧和深厚的知识。本文旨在通过浅显易懂的语言,引领读者从软件测试的基础出发,逐步深入到更复杂的测试策略和工具的使用,最终达到能够独立进行高效测试的水平。我们将一起探索如何通过不同的测试方法来确保软件的质量和性能,就像艺术家通过不同的色彩和笔触来完成一幅画作一样。
|
13天前
|
搜索推荐 前端开发 Android开发
安卓应用开发中的自定义视图实现
【10月更文挑战第30天】在安卓开发的海洋中,自定义视图是那抹不可或缺的亮色,它为应用界面的个性化和交互体验的提升提供了无限可能。本文将深入探讨如何在安卓平台创建自定义视图,并展示如何通过代码实现这一过程。我们将从基础出发,逐步引导你理解自定义视图的核心概念,然后通过一个实际的代码示例,详细讲解如何将理论应用于实践,最终实现一个美观且具有良好用户体验的自定义控件。无论你是想提高自己的开发技能,还是仅仅出于对安卓开发的兴趣,这篇文章都将为你提供价值。
|
22天前
|
Android开发 UED
Android 中加载 Gif 动画
【10月更文挑战第20天】加载 Gif 动画是 Android 开发中的一项重要技能。通过使用第三方库或自定义实现,可以方便地在应用中展示生动的 Gif 动画。在实际应用中,需要根据具体情况进行合理选择和优化,以确保用户体验和性能的平衡。可以通过不断的实践和探索,进一步掌握在 Android 中加载 Gif 动画的技巧和方法,为开发高质量的 Android 应用提供支持。
|
1月前
|
Java Unix Linux
Android Studio中Terminal运行./gradlew clean build提示错误信息
遇到 `./gradlew clean build`命令执行出错时,首先应检查错误信息的具体内容,这通常会指向问题的根源。从权限、环境配置、依赖下载、版本兼容性到项目配置本身,逐一排查并应用相应的解决措施。记住,保持耐心,逐步解决问题,往往复杂问题都是由简单原因引起的。
228 2
|
2月前
|
Android开发 开发者
安卓开发中的自定义视图:从入门到精通
【9月更文挑战第19天】在安卓开发的广阔天地中,自定义视图是一块充满魔力的土地。它不仅仅是代码的堆砌,更是艺术与科技的完美结合。通过掌握自定义视图,开发者能够打破常规,创造出独一无二的用户界面。本文将带你走进自定义视图的世界,从基础概念到实战应用,一步步展示如何用代码绘出心中的蓝图。无论你是初学者还是有经验的开发者,这篇文章都将为你打开一扇通往创意和效率的大门。让我们一起探索自定义视图的秘密,将你的应用打造成一件艺术品吧!
61 10