Android APP完整基础教程(13)应用资源-动画

Android APP完整基础教程(13)应用资源-动画,第1张

Android APP完整基础教程(13)应用资源-动画

动画模式在android系统中被分为三类,分别为:

    tween(view) animation:补间动画frame(drawable) animation:逐帧动画property animation:属性动画

本章节分别对齐进行解读。

1 Tween Animation 1.1 Tween Animation基础

Animation是以XML格式定义的,XML文件存放在路径res/anim下。这里按照XML文档的结构{父节点|子节点|属性}来介绍Tween Animation。先介绍Tween Animation共同的节点属性,如下所示:

 Tween Animation由4种基本动画组成:Alpha(渐变透明度)、Scale(缩放)、Translate(位置移动)、Rotate(旋转),同时Animation类还有AlphaAnimation、ScaleAnimation、 TranslateAnimation、RotateAnimation 4个子类与之分别对应,每个子类都在父类的基础上增加了各自独有的属性。分别解读如下:

@1 Alpha属性说明:

@2 Scale属性说明:

@3 Translate属性说明:

@4 Rotate属性说明:

1.2 Tween Animation的用法

Tween Animation的用法有2种:从XML文件中读取 和 代码中设置/读取。

@1 代码中直接从XML资源中读取Animation并使用

用XML定义的动画放在/res/anim/文件夹内,XML文件的根元素可以为,,,, interpolator元素 或(表示以上几个动画的集合,set可以嵌套)。默认情况所有动画是同时进行的,可以通过startOffset属性设置各个动画的开始偏移(开始时间)来达到动画顺序播放的效果。定义好动画XML文件后通过代码对指定的View应用该动画。如下所示:

ImageView XXXImage = (ImageView)findViewById(R.id.spaceshipImage);
Animation yyyAnimation=AnimationUtils.loadAnimation(this,R.anim.yyy);
XXXImage.startAnimation(yyyAnimation);

@2 通过代码设置Tween Animation属性

代码中使用Animation子类来初始化Animation对象。Animation基类包含大量的set/getXXX()函数来设置/读取Animation的属性。相关方法可参照文档:Android Animation XML属性。

Tween Animation关于动画相关的方法可参照文档:Android Animation method解读。

1.3 动画的运行控制与模式

@1 Interpolator

动画的进度使用 Interpolator来控制,interpolator定义一个动画的变化率,用于在运行时控制动画。这使得基本的动画效果(alpha, scale, translate, rotate)得以加速,减速,重复等。Interpolator 是基类,封装了所有 Interpolator 的共同方法,它只有一个方法,即 getInterpolation (float input),该方法提供了几个Interpolator 子类,实现了不同的速度曲线,如下: 

说明:对于 LinearInterpolator ,变化率是个常数,即f(x) = x。Interpolator其他的几个子类,也都是按照特定的算法,实现了对变化率。还可以定义自己的Interpolator子类,实现抛物线、自由落体等物理效果。

@2 动画运行模式

独占模式:程序主线程进入一个循环,根据动画指令不断刷新屏幕,直到动画结束。中断模式:单独线程对时间计数,隔一段时间给主线程发通知,主线程接到通知后更新屏幕。

@3 动画实现原理

Transformation记录了仿射矩阵Matrix,动画每触发一次,会对原来的矩阵做一次运算, View的Bitmap与这个矩阵相乘就可实现相应的 *** 作(旋转、平移、缩放等)。图形变换通过仿射矩阵实现。图形变换是图形学中的基本知识,简单来讲,每种变换都是一次矩阵运算。在 Android中,Canvas 类中包含当前矩阵,当调用Canvas.drawBitmap(bmp, x, y, Paint)绘制时,Android会先把bmp做一次矩阵运算,然后将运算的结果显示在Canvas上。之后只需不断修改Canvas的矩阵并刷新屏幕,View里的对象就会不停的做图形变换,因此就形成了动画。 2 frame Animation

Android用AnimationDrawable类来定义和使用frame Animation。它可以在XML Resource定义(还是存放到res/anim文件夹下),也可以使用AnimationDrawable中的API定义。与Tween Animation不同,frame Animation是顺序播放事先做好的图像,通过一系列Drawable依次显示来模拟动画的效果。在XML中的定义方式如下:

    
    ...

说明:必须以为根元素,以表示要轮换显示的图片,duration属性表示各项显示的时间。XML文件要放在/res/drawable/

@2 Drawable Animation使用示例:

定义一帧一帧的动画item,配置如下所示:

   
   
   

其包含3帧动画,3帧动画中分别应用了drawable中的3张图片:XXX1、XXX2、XXX3,每帧动画持续200毫秒。然后我们将以上XML保存在res/anim/文件夹下,命名为XXX.xml。

编写代码,显示动画的代码如下:

protected void onCreate(Bundle savedInstanceState) {
    super.onCreate(savedInstanceState);
    setContentView(R.layout.main);
    imageView = (ImageView) findViewById(R.id.imageView);
    imageView.setBackgroundResource(R.drawable.drawable_anim);
    anim = (AnimationDrawable) imageView.getBackground();
}

public boolean onTouchEvent(MotionEvent event) {
    if (event.getAction() == MotionEvent.ACTION_DOWN) {
        anim.stop();
        anim.start();
        return true;
    }
    return super.onTouchEvent(event);
}

注意:

调用Imageview的setBackgroundResource方法,如果直接在XML布局文件中设置其src属性,当触发动画时会ForceClose问题。在start()前要先stop(),不然第一次动画运行后会停在最后一帧,这样动画就只会触发一次。不要在onCreate中调用start,因为AnimationDrawable还没有完全跟Window相关联,如果想要界面显示时就开始动画的话,可以在onWindowFoucsChanged()中调用start()。

@3 阅读Android 文档中对AnimationDrawable的介绍,关键内容整理如下:

更多关于AnimationDrawable的介绍,查看文档:Android AnimationDrawable详细解读

注意:frame Animation 的XML 文件中不定义 interpolator 属性,因为定义它没有任何意义。

3 Animator

Animator代表一个属性动画,但是它只是一个抽象类。我们通常会使用它的子类:AnimatiorSet、ValueAnimator、ObjectAnimator、TimeAnimator。XML文件应放在res/animator/中。

3.1 属性动画的工作方式

@1 属性动画,它更改对象的实际属性。

在Tween Animation中,其改变的是View的绘制效果,View的属性保持不变,比如无论你在对话中如何缩放Button的大小,Button有效点击区域还是没有应用动画时的区域,其位置与大小都不变。在属性动画中,改变的是对象的实际属性,如Button的缩放,Button的位置与大小属性值都改变了。且属性动画不止可以应用于View,还可以应用于任何对象。属性动画只是表示一个值在一段时间内的改变,当值改变时要做什么事情由我们来决定。

@2 在Property Animation中,可以对动画应用以下属性:

3.2 常见属性动画解读&代码使用实例 3.2.1 ValueAnimator动画(代码实现机制)

整个属性动画机制当中最核心的一个类,属性动画的运行机制是通过不断地对值进行 *** 作来实现的,而初始值和结束值之间的动画过渡就是由ValueAnimator这个类来负责计算的。它的内部使用一种时间循环的机制来计算值与值之间的动画过渡,我们只需要将初始值和结束值提供给ValueAnimator,并且告诉它动画所需运行的时长,那么ValueAnimator就会自动帮我们完成从初始值平滑地过渡到结束值这样的效果。除此之外,ValueAnimator还负责管理动画的播放次数、播放模式、以及对动画设置监听器等,是一个非常重要的类。但是ValueAnimator的用法却一点都不复杂,实例(将一个值从0平滑过渡到1,时长300毫秒,就可以这样写)如下:

ValueAnimator anim = ValueAnimator.ofFloat(0f, 1f);  
anim.setDuration(300);  
anim.start();  
3.2.2 ObjectAnimator动画(代码实现机制)

相比于ValueAnimator,ObjectAnimator更常用,ValueAnimator不过是对值进行了平滑的动画过渡。而ObjectAnimator(继承自ValueAnimator)可以直接对任意对象的任意属性进行动画 *** 作。

@1 alpha案例

一个TextView在5秒中内从常规变换成全透明,再从全透明变换成常规,代码如下:

ObjectAnimator animator = ObjectAnimator.ofFloat(textview, "alpha", 1f, 0f, 1f);  
animator.setDuration(5000);  
animator.start();  

@2 rotation案例

将TextView进行一次360度的旋转,将@1中第二个参数改成"rotation",然后将动画的初始值和结束值分别设置成0和360,代码如下:

ObjectAnimator animator = ObjectAnimator.ofFloat(textview, "rotation", 0f, 360f);  
animator.setDuration(5000);  
animator.start();  

@3 translation案例

将TextView先向左移出屏幕,然后再移动回来,代码如下:

float curTranslationX = textview.getTranslationX();  
ObjectAnimator animator = ObjectAnimator.ofFloat(textview, "translationX",curTranslationX, -500f, curTranslationX);  
animator.setDuration(5000);  
animator.start(); 

先是调用了TextView的getTranslationX()方法来获取到当前TextView的translationX的位置,然后ofFloat()方法的第二个参数传入"translationX",后面三个参数用于告诉TextView如何移动。

@4 Scale实例

将TextView在垂直方向上放大3倍再还原,将ofFloat()方法的第二个参数改成了"scaleY",表示在垂直方向上进行缩放,代码如下:

ObjectAnimator animator = ObjectAnimator.ofFloat(textview, "scaleY", 1f, 3f, 1f);  
animator.setDuration(5000);  
animator.start();  

@5  特殊说明

关于 ofFloat()方法的第二个参数传递参数:不局限于alpha、rotation、translationX和scaleY这些值,我们可以传入任意值到ofFloat()方法的第2个参数。因为ObjectAnimator在设计的时候就没有针对于View来进行设计,而是针对于任意对象的,它所负责的工作就是不断地向某个对象中的某个属性进行赋值,然后对象根据属性值的改变再来决定如何展现出来。

3.2.3 组合动画

@1 基本概念

独立动画实现的视觉效果有限,因此将多个动画组合到一起播放就显得尤为重要。实现组合动画主要借助AnimatorSet这个类,这个类提供了一个play()方法,传入Animator对象(ValueAnimator或ObjectAnimator)将会返回一个AnimatorSet.Builder实例,AnimatorSet.Builder中包括以下四个方法:

after(Animator anim)    //将现有动画插入到传入的动画之后执行。
after(long delay)       //将现有动画延迟指定毫秒后执行。
before(Animator anim)   //将现有动画插入到传入的动画之前执行。
with(Animator anim)     //将现有动画和传入的动画同时执行。

有这四个方法,我们就可以完成组合动画的逻辑。

@2 使用实例

这里让TextView先从屏幕外移动进屏幕,然后开始旋转360度,旋转的同时进行淡入淡出 *** 作,关键代码如下所示:

ObjectAnimator moveIn = ObjectAnimator.ofFloat(textview, "translationX", -500f, 0f); 
ObjectAnimator rotate = ObjectAnimator.ofFloat(textview, "rotation", 0f, 360f);  
ObjectAnimator fadeInOut = ObjectAnimator.ofFloat(textview, "alpha", 1f, 0f, 1f);  
AnimatorSet animSet = new AnimatorSet();  
animSet.play(rotate).with(fadeInOut).after(moveIn);  
animSet.setDuration(5000);  
animSet.start();     

先创建三个动画对象,然后new出一个AnimatorSet对象后将这三个动画对象进行播放排序,让旋转和淡入淡出动画同时进行,并把它们插入到了平移动画的后面,最后设置动画时长及启动动画。

3.3 属性动画XML使用实例 3.3.1 ValueAnimator动画使用实例

ValueAnimator代码和xml设置中,因为不是 *** 作对象,所以没有setPropertyName,只是根据value进行某种动作需要加监听器,监听值的变化做相应的处理。xm配置如下:

  
  
  

加载XML动画,关键代码实现如下:

ValueAnimator valueAnimator = (ValueAnimator) AnimatorInflater.loadAnimator(this, R.animator.animator);  
valueAnimator.setTarget(tv_num);  
valueAnimator.setevaluator(new Typeevaluator() {  
    @Override   
    public Integer evaluate(float fraction, Integer startValue, Integer endValue) {  
        System.out.println("时间比率,fraction:" + fraction);  
        System.out.println("结果值:" + (int)((startValue + fraction * (endValue - startValue)) / 10 * 10));  
        return (int)((startValue + fraction * (endValue - startValue)) / 10 * 10);  
    }  
});  
valueAnimator.addUpdateListener(new AnimatorUpdateListener() {  
    @Override  
    public void onAnimationUpdate(ValueAnimator animation) {  
        //在onAnimationUpdate中 该值返回第一个动画的 当前帧的evaluate 值  
        System.out.println("animation.getAnimatedValue()==" + animation.getAnimatedValue());  
        tv_num.setText(animation.getAnimatedValue() + "");  
    }  
});  
valueAnimator.start();  
3.3.2 ObjectAnimator动画XML的使用实例

动画XML定义如下:

  
  
 

加载XML动画,关键代码如下所示:

imageview_scale.setBackground(getResources().getDrawable(R.drawable.a11));  
ObjectAnimator scaleAnimator = (ObjectAnimator) AnimatorInflater.loadAnimator(this, R.animator.scale_object_animator);  
scaleAnimator.setTarget(imageview_scale);//设置动画作用的目标对象  
scaleAnimator.setDuration(1000);  
scaleAnimator.setRepeatCount(50);  
scaleAnimator.start();  
3.3.3 AnimatorSet 动画集

由ObjectAnimator 和 ValueAnimator 组成,对应的xml中的写法类似为

  ... ... 

xml定义动画集一般在文件夹res/animator下,这里是res/animator/set_rotate_scale.xml

@1 XML配置文件定义如下:

  

      
      
          
          
      
      
          
          
          
              
    

 加载XML动画集,代码实现如下:

imageview_rotate.setBackground(getResources().getDrawable(R.drawable.a11));  
AnimatorSet animatorSet = (AnimatorSet) AnimatorInflater.loadAnimator(this, R.animator.set_rotate_scale);  
animatorSet.setTarget(imageview_rotate);  
animatorSet.setDuration(1000);  
animatorSet.setInterpolator(new BounceInterpolator());//设置end时的d跳插入器  
animatorSet.start();

更多关于动画的内容可查看文档:Android 动画解读

4 Animator监听器

若我们希望监听到动画事件,动画何时开始,何时结束,然后在开始或者结束的时候去执行一些逻辑处理。这个功能是完全可以实现的,Animator类当中提供了一个addListener()方法,这个方法接收一个AnimatorListener,我们只需要去实现这个AnimatorListener就可以监听动画的各种事件了。

只要是继承自Animator的,addListener()这个方法算是个通用的方法,添加一个监听器的代码如下所示:

anim.addListener(new AnimatorListener() {  
    @Override  
    public void onAnimationStart(Animator animation) {}  
  
    @Override  
    public void onAnimationRepeat(Animator animation) {}  
  
    @Override  
    public void onAnimationEnd(Animator animation) {}  
  
    @Override  
    public void onAnimationCancel(Animator animation) {}  
});  

但是也许很多时候我只想要监听动画响应的单一事件,那么可以使用AnimatorListenerAdapter,使用这个类就可以解决掉实现接口繁琐的问题了,如下所示:

anim.addListener(new AnimatorListenerAdapter() {});  

这里我们向addListener()方法中传入这个适配器对象,由于AnimatorListenerAdapter中已经将每个接口都实现好了,所以这里不用实现任何一个方法也不会报错。那么如果想监听动画结束这个事件,就只需要单独重写这一个方法就可以了,如下所示:
 

anim.addListener(new AnimatorListenerAdapter() {  
    @Override  
    public void onAnimationEnd(Animator animation) {  
    }  
});  

欢迎分享,转载请注明来源:内存溢出

原文地址: https://outofmemory.cn/zaji/5718365.html

(0)
打赏 微信扫一扫 微信扫一扫 支付宝扫一扫 支付宝扫一扫
上一篇 2022-12-17
下一篇 2022-12-18

发表评论

登录后才能评论

评论列表(0条)

保存