Android动画之视图动画

前言

       动画在Android中是一个相当于重要的知识点,使用场景也很多,炫酷的界面效果少不了动画来提升,这里我们就先来说说Android中的动画,在说Android的动画之前,我们先来了解一下动画这个效果的通用含义。

什么是动画?

       我们在平常生活中,经常会遇到动画这个词,大家也不要想歪了,可不是大家理解的岛国xx片哦!,那动画究竟是什么呐?在不同的场景中,这个词的名词解释都不一样,那我们这里也来讨论一下动画是什么?

1,动画就是动起来的画
2,动画最基本的一点就是能够随一定的时间间隔而变化

       这个定义也是我总结的,感觉还挺契合这个概念,如果大家有异议,请告诉我,反正我也不会改的。

动画属性

       动画的概念已经有了,那通用的动画有哪些属性呐,或者说具备哪些属性,比如我有如下一个动画:

       从这个动画我们能够得出哪些属性?

描述结论
作用的对象traget
持续时间duration
从哪儿来from
到哪儿去to
开始时间beginTime
重复次数repeatCount
怎么去timeLine

       上面我大致总结了一下公共的属性,这些属性针对任何动画都适用,可能还总结的不够全,这里只是起到一个抛砖引玉的作用。

动画组合

       定义有了,属性有了,还有啥公共是属性?那就是动画的执行效果了,一般有单个动画,就是一次只执行一个动画,还有就是组合动画了,多个动画可以组合成,串行,并行,串并行动画。

       对公共的动画我们大致就先说这么多,现在开始转移到我们的主题内容,Android动画。接下的我们就来说说在Android中的动画的一些概念和怎么使用!

Android中的动画分类
  • 视图动画
    • Tween动画
      • 平移动画
      • 旋转动画
      • 缩放动画
      • 渐变动画
    • Frame动画
      • AnimationDrawable
  • 属性动画

视图动画

       到这里,我们终于今天本章的主题了。这里主要来首先来说说视图动画,在下一章中我们会说说属性动画。视图动画与属性动画都同属于Android,但是实现方式是完全不同的。

注意:视图动画只能作用于View,能够实现的效果也仅限于上述的几种和他们动画的组合。

Android中的属性对应关系

       前面我们总结了动画的通用属性,那我们这里就来看看Android中这些属性的对应关系:

描述结论Android属性
作用的对象tragetView
持续时间durationduration
从哪儿来fromfromXXX
到哪儿去totoXXX
开始时间beginTimestartOffset
重复次数repeatCountrepeactCount
怎么去timeLineinterpolator

       我们可以看到在Android中,属性对应的名称大致跟通用的总结的一致,至少看到名字就能理解意思。因此我们后面在实现动画的时候就按照这些属性来配置就ok了。

Tween动画的结构
  • Animation
    • AlphaAnimation
    • TranslateAnimation
    • RotateAnimation
    • ScaleAnimation
    • AnimationSet
             这里前四个是单个动画,后面一个是动画集,他可以组合前面的动画。

Android动画实现

       视图动画主要有两种方式来实现:

1,xml实现,首先在res/anim目录下定义动画的xml文件,之后带代码中用AnimationUtils.loadAnimation()加载
2, 代码实现,直接new对应的动画实例来实现动画。

       我们来看几个动画效果,之后实例进行操作一遍:
这里写图片描述

xml实现

       在res/anim下定义动画文件,我们分别定义渐变,平移,旋转,缩放四个动画文件:

       渐变动画,名称为alpha.xml

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

       fromAlpha表示动画开始的透明度,toAlpha表示动画结束的透明度,1.0表示完全不透明,0.0便是完全透明,两个都是浮点数,duration便是动画的持续时间,单位为毫秒,2000毫秒表示2秒。startOffset表示动画开始的延迟时间,点击开始1秒后开始执行,单位也是毫秒。

       平移动画,名称为translate.xml

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

</translate>

       fromXDelta,fromYDelta表示动画开始时的x,y坐标,toXDelta,toYDelta动画结束的x,y坐标,这里需要注意的是他可以设置三种类型的值,比如我们设置50:

50: 表示50个像素,是绝对值
50%: 表示自身大小一半,记住这里是可以设置%号的,他等于0.5,如果设置0.5表示绝对值0.5
50%p :表示父元素大小的一半,p表示相对于父元素

       缩放动画,名称为scale.xml

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

</scale>

       fromXScale,fromXScale动画开始时x,y的缩放大小,toXScale,toXScale:动画结束的x,y缩放大小,1表示本身大小,数值越大,缩放比例越大,pivotX,piovtY:缩放的中心坐标位置,可以设置值与平移动画可以设置的类似:

50: 表示50个像素,是绝对值
50%: 表示自身中心位置
50%p :表示父元素的中心位置,p表示相对于父元素

       旋转动画,名称为rotate.xml

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

</rotate>

       fromDegrees动画开始的角度,toDegress动画结束的角度,正数为顺时针,负数为逆时针,pivotX,piovtY:旋转的中心坐标位置,设置的方式与缩放相似。

       xml文件定义好后,我们就需要在代码中进行使用,可以先定义一个view,之后在界面中find出来,之后对view设置动画:

private void rotate() {
    Animation rotate = AnimationUtils.loadAnimation(this, R.anim.rotate);
    imageView.startAnimation(rotate);
}

private void scale() {
    Animation scale = AnimationUtils.loadAnimation(this, R.anim.scale);
    imageView.startAnimation(scale);
}

private void translate() {
    Animation translate = AnimationUtils.loadAnimation(this, R.anim.translate);
    imageView.startAnimation(translate);
}

private void alpha() {
    Animation alpha = AnimationUtils.loadAnimation(this, R.anim.alpha);
    imageView.startAnimation(alpha);
}

       在代码中,都需要先load动画,采用AnimationUtils来进行load,之后将load出来的动画设置到view上就可以了。

代码实现

       上面我们采用xml实现四种动画,接下来我们用代码来实现这个四种动画:

private void rotate() {
    RotateAnimation rotate = new RotateAnimation(0, 360, Animation.RELATIVE_TO_SELF, 0.5f,Animation.RELATIVE_TO_SELF, 0.5f);
    rotate.setDuration(3000);
    imageView.startAnimation(rotate);
}

private void scale() {
    ScaleAnimation scale = new ScaleAnimation(1,2, 1, 2, Animation.RELATIVE_TO_SELF,0.5f,Animation.RELATIVE_TO_SELF, 0.5f);
    scale.setDuration(3000);
    imageView.startAnimation(scale);
}

private void translate() {
    TranslateAnimation translate = new TranslateAnimation(0, 200, 0, 0);
    translate.setDuration(3000);
    imageView.startAnimation(translate);
}

private void alpha() {
    AlphaAnimation alpha = new AlphaAnimation(1.0f, 0.0f);
    alpha.setDuration(3000);
    imageView.startAnimation(alpha);
}

       从代码可以看出,四种动画都分别对应了不同的类,已经有四种现场的动画类可以使用。每次使用只需要new对应的实例,设置不同的参数,参数设置也很简单。这里就不在详细说了。

动画的开始于结束

       动画我们已经实现了,但是如果你需要在动画的执行过程中,做某些操作怎么办呐?比如在动画开始的时候提示一下动画的开始,在动画的结束时,我们提示动画的结束,这我们又能怎么做呐?

       系统也完全考虑到这个问题,我们可以对动画设置监听,可以对动画设置AnimationListener,如下:

rotate.setAnimationListener(new Animation.AnimationListener() {
            @Override
            public void onAnimationStart(Animation animation) {
                //todo
            }

            @Override
            public void onAnimationEnd(Animation animation) {
                // todo  next anim
            }

            @Override
            public void onAnimationRepeat(Animation animation) {

            }
        });

       设置动画监听,在动画开始结束等时机,系统都会回调对应的函数,比如onAnimationStart就是告诉动画开始执行了。这样我们就可以根据不同的回调做想要做的事情。

组合动画

       我们在抽象动画的公共属性说过,动画有单个动画,还有组合动画,上面我们所实现的都是单个动画,那怎么实现组合动画,组合动画又分为串行动画,并行动画,或者既有串行又有并行。这些又怎么实现:

  1. 多个单个动画来实现
    • 串行动画,可以定义多个动画,分别对每一个动画设置监听,在上一个动画结束时,开始下一个动画,也可以设置动画的startOffset,下一个动画的startOffset为上一个动画的startOffset与duration之和
    • 并行动画,定义多个动画,设置同样的startOffset与duration
      1. 采用AnimationSet来实现,下面我们主要才实现一下该方式

       我们需要实现如下效果的动画,由于将MP4转换成gif,效果变得很差,将就着看吧:
这里写图片描述
       这里也可以用两种方式实现,xml实现与代码实现,这里万变不离其宗,只不过是将上面的单个动画安装需要的效果组合一下就ok,下面我们用xml来实现一下该动画:

       定义一个love_flay.xml

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

    <translate
        android:duration="3000"
        android:fromXDelta="0"
        android:fromYDelta="0"
        android:toXDelta="0"
        android:toYDelta="-300"></translate>

    <rotate
        android:duration="3000"
        android:fromDegrees="0"
        android:pivotX="50%"
        android:pivotY="50%"
        android:startOffset="3000"
        android:toDegrees="-45"></rotate>

    <scale
        android:duration="3000"
        android:fromXScale="1"
        android:fromYScale="1"
        android:pivotX="50%"
        android:pivotY="50%"
        android:startOffset="3000"
        android:toXScale="2"
        android:toYScale="2"></scale>
    <alpha
        android:duration="3000"
        android:fromAlpha="1.0"
        android:startOffset="3000"
        android:toAlpha="0.0"></alpha>


</set>

       代码加载上面的定义的xml。

private void loveAnim() {
    Animation animation  = AnimationUtils.loadAnimation(this, R.anim.love_fly);
    love.startAnimation(animation);
}

       这样我们就实现了上面要求的效果,那代码方式又怎么实现? 首先定义多个单个动画,之后采用AnimationSet来实现,将定义的动画都add到set中,比如我们需要实现一个在平移的过程中放大并且逐渐消失的动画:

private void alpha() {
    AlphaAnimation alpha = new AlphaAnimation(1.0f, 0.0f);
    alpha.setDuration(3000);

    TranslateAnimation translate = new TranslateAnimation(0, 200, 0, 0);
    translate.setDuration(3000);

    ScaleAnimation scale = new ScaleAnimation(1,2, 1, 2, Animation.RELATIVE_TO_SELF,0.5f,Animation.RELATIVE_TO_SELF, 0.5f);
    scale.setDuration(3000);

    AnimationSet set = new AnimationSet(true);
    set.addAnimation(alpha);
    set.addAnimation(translate);
    set.addAnimation(alpha);
    imageView.startAnimation(set);
}

       new AnimationSet(true),true表示是否公用插值器,后面我们会详细说说插值器。

       这里我们就已经说完了怎么实现组合动画,之后我们来讲讲插值器是个啥!

插值器

       说道插值器,我们首先要知道插值器是干什么用的?

插值器控制动画在时间轴上的变换效果
       我们先来几个效果:
这里写图片描述

       我们分别就这几个效果来做一下说明系统到底是怎么来控制效果的,首先我们来看看AccelerateDecelerateInterpolator,该插值器是先加速后减速。

       插值器中主要是根据getInterpolation来获取某一个时间点的值,AccelerateDecelerateInterpolator的调用函数为(float)(Math.cos((input + 1) * Math.PI) / 2.0f) + 0.5f; 输入范围为0到1,根据这个函数我们可以生产如下的图片:

这里写图片描述

       从函数图可以看出,当输入在0到1中时,函数先加速后减速,系统就是靠这个来控制动画的执行效果的。

       我们在来看CycleInterpolator,该插值器有一个震荡的效果,我们来看看他的调用函数:(float)(Math.sin(2 * mCycles * Math.PI * input)); mCycles = 2, input( 0, 1),mCycles为2表示震荡两次,我们来看看函数图:
这里写图片描述

       可以看到在0到1中循环了两次。

       从上面的可以看到如果你想实现特定的效果,只需要实现特定的插值器就好了。

动画的最终位置

       上面我们执行了多个动画,都发现每次执行完成后都回到了初始位置,那我们怎么才能将动画停止在最终位置?其实很容易实现该效果,只需要设置动画的setFillAfter为true就可以了。

动画的响应

       我们对动画设置点击事件,代码如下:

public class LocationActivity extends AppCompatActivity {

    private ImageView love;

    public static void start(Context context) {
        Intent intent = new Intent();
        intent.setClass(context, LocationActivity.class);
        context.startActivity(intent);
    }

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

    private void findViews() {
        love = (ImageView) findViewById(R.id.love);
        love.setOnClickListener(new View.OnClickListener() {
            @Override
            public void onClick(View v) {
                Toast.makeText(LocationActivity.this, "clicked", Toast.LENGTH_LONG).show();
            }
        });
    }

    private void setViewListener() {
        findViewById(R.id.set).setOnClickListener(new View.OnClickListener() {
            @Override
            public void onClick(View v) {
                loveAnim();
            }
        });
    }

    private void loveAnim() {
        TranslateAnimation translate = new TranslateAnimation(0,0,0,-200);
        translate.setDuration(3000);
        translate.setFillAfter(true);
        love.startAnimation(translate);
    }
}

       执行后,发现动画在最终位置不能点击,而点击事件还是在初始位置。

视图动画是不可交互动画,点击事件还是在初始位置

帧动画

       我们来看看如下效果:
这里写图片描述
       首先我们需要定义一个动画文件,但是该动画文件不是放在res/anim下,而是放置drawable下面。
       定义一个play_list.xml:

<?xml version="1.0" encoding="utf-8"?>
<animation-list xmlns:android="http://schemas.android.com/apk/res/android">
    <item android:drawable="@drawable/play_1" android:duration="50" />
    <item android:drawable="@drawable/play_2" android:duration="50" />
    <item android:drawable="@drawable/play_3" android:duration="50" />
    <item android:drawable="@drawable/play_4" android:duration="50" />
    <item android:drawable="@drawable/play_5" android:duration="50" />
    <item android:drawable="@drawable/play_6" android:duration="50" />
    <item android:drawable="@drawable/play_7" android:duration="50" />
    <item android:drawable="@drawable/play_8" android:duration="50" />
    <item android:drawable="@drawable/play_9" android:duration="50" />
    <item android:drawable="@drawable/play_10" android:duration="50" />
    <item android:drawable="@drawable/play_11" android:duration="50" />
    <item android:drawable="@drawable/play_12" android:duration="50" />
    <item android:drawable="@drawable/play_13" android:duration="50" />
    <item android:drawable="@drawable/play_14" android:duration="50" />
    <item android:drawable="@drawable/play_15" android:duration="50" />
    <item android:drawable="@drawable/play_16" android:duration="50" />
    <item android:drawable="@drawable/play_17" android:duration="50" />
    <item android:drawable="@drawable/play_18" android:duration="50" />

</animation-list>

       之后在代码中进行加载:

public class FrameActivity extends AppCompatActivity implements View.OnClickListener {

    private ImageView frame;
    private AnimationDrawable drawable;

    public static void start(Context context) {
        Intent intent = new Intent();
        intent.setClass(context, FrameActivity.class);
        context.startActivity(intent);
    }

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

    private void findViews() {
        frame = (ImageView) findViewById(R.id.frame);
    }

    private void setViewsListener() {
        findViewById(R.id.start).setOnClickListener(this);
        findViewById(R.id.end).setOnClickListener(this);
    }

    @Override
    public void onClick(View v) {
        switch (v.getId()) {
            case R.id.start:
                startAnim();
                break;
            case R.id.end:
                stopAnim();
                break;
        }
    }

    private void stopAnim() {
        if(drawable!=null){
            if(drawable.isRunning()){
                drawable.stop();
            }
        }
    }

    private void startAnim() {
        if(drawable==null){
            frame.setBackgroundResource(R.drawable.play_list);
            drawable = (AnimationDrawable) frame.getBackground();
            drawable.setOneShot(true);
        }
        if(!drawable.isRunning()){
            drawable.start();
        }
    }
}

       将动画文件设置为view的背景,之后在获取出来为AnimationDrawable,setOneShot如果为true,表示只执行一次,start开始执行动画,isRunning表示动画知否在执行,stop停止动画。其他属性就不一一解释了。

总结

       到此视图动画就讲述完了,下一章来说说属性动画。

  • 3
    点赞
  • 3
    收藏
    觉得还不错? 一键收藏
  • 5
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论 5
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值