Android样式的开发:Property Animation篇

简介: 笔记


前篇文章说过,Android框架还提供了两种动画体系,前一篇已经总结了视图动画(View Animation)的用法,本篇则接着总结另一种动画体系——属性动画(Property Animation)的用法。

视图动画只能作用于View,而且视图动画改变的只是View的绘制效果,View真正的属性并没有改变。比如,一个按钮做平移的动画,虽然按钮的确做了平移,但按钮可点击的区域并没随着平移而改变,还是在原来的位置。而属性动画则可以改变真正的属性,从而实现按钮平移时点击区域也跟着平移。通俗点说,属性动画其实就是在一定时间内,按照一定规律来改变对象的属性,从而使对象展现出动画效果。

属性动画是在android 3.0引入的动画体系,如果还想适配基本已经灭绝的2.x版本,只好绕道了。

属性动画和视图动画一样,可以通过xml文件定义,不同的是,视图动画的xml文件放于res/anim/目录下,而属性动画的xml文件则放于res/animator/目录下。一个是anim,一个是animator,别搞错了。同样的,在Java代码里引用属性动画的xml文件时,则用R.animator.filename,不同于视图动画,引用时为R.anim.filename

属性动画主要有三个元素:

相对应的有三个类:ValueAnimatorObjectAnimatorAnimatorSet

ValueAnimator是基本的动画类,处理值动画,通过监听某一值的变化,进行相应的操作。ObjectAnimatorValueAnimator的子类,处理对象动画。AnimatorSet则为动画集,可以组合另外两种动画或动画集。相应的三个标签元素的关系也一样。

样式开发主要还是用xml的形式,所以这里主要还是讲标签的用法。


animator


标签与对应的ValueAnimator类提供了属性动画的核心功能,包括计算动画值、动画时间细节、是否重复等。执行属性动画分两个步骤:

  1. 计算动画值
  2. 将动画值应用到对象和属性上

ValuAnimiator只完成第一步,即只计算值,要实现第二步则需要在值变化的监听器里自行更新对象属性。

通过标签可以很方便的对ValuAnimiator进行设置,可设置的属性如下:

  • android:duration 动画从开始到结束持续的时长,单位为毫秒
  • android:startOffset 设置动画执行之前的等待时长,单位为毫秒
  • android:repeatCount 设置动画重复执行的次数,默认为0,即不重复;可设为-1或infinite,表示无限重复
  • android:repeatMode 设置动画重复执行的模式,可设为以下两个值其中之一:
    restart 动画重复执行时从起点开始,默认为该值
    reverse 动画会反方向执行
  • android:valueFrom 动画开始的值,可以为int值、float值或color值
  • android:valueTo 动画结束的值,可以为int值、float值或color值
  • android:valueType 动画值类型,若为color值,则无需设置该属性
    intType 指定动画值,即以上两个value属性的值为整型
    floatType 指定动画值,即以上两个value属性的值为浮点型,默认值
  • android:interpolator 设置动画速率的变化,比如加速、减速、匀速等,需要指定Interpolator资源。具体用法在View Animation篇已经讲过,这里不再重复

接着,用一个实例讲解具体的用法吧。在这个例子里,将一个按钮的宽度进行缩放,从100%缩放到20%。

xml文件的代码如下:

<!-- res/animator/value_animator.xml -->
<?xml version="1.0" encoding="utf-8"?>
<animator xmlns:android="http://schemas.android.com/apk/res/android"
    android:duration="3000"
    android:valueFrom="100"
    android:valueTo="20"
    android:valueType="intType" />

可看到,值的变化从100到20,动画时长3000毫秒,以下则是目标按钮的xml代码:

<Button
    android:layout_width="match_parent"
    android:layout_height="wrap_content"
    android:background="@drawable/bg_btn_normal"
    android:onClick="onScaleWidth"
    android:text="点我"
    android:textColor="@android:color/white" />

按钮默认是填充屏幕宽度的,点击时的执行方法为onScaleWidth,以下则是onScaleWidth方法的代码:

public void onScaleWidth(final View view) {    
    // 获取屏幕宽度
    final int maxWidth = getWindowManager().getDefaultDisplay().getWidth();
    ValueAnimator valueAnimator = (ValueAnimator) AnimatorInflater.loadAnimator(this, R.animator.value_animator);
    valueAnimator.addUpdateListener(new ValueAnimator.AnimatorUpdateListener() {        
        @Override
        public void onAnimationUpdate(ValueAnimator animator) {            
            // 当前动画值,即为当前宽度比例值
            int currentValue = (Integer) animator.getAnimatedValue();            
            // 根据比例更改目标view的宽度
            view.getLayoutParams().width = maxWidth * currentValue / 100;
            view.requestLayout();
        }
    });
    valueAnimator.start();
}

View Animation篇中已经知道,视图动画是通过AnimationUtils类的loadAnimation()方法获取xml文件相对应的Animation类实例,而属性动画则是通过AnimatorInflater类的loadAnimation()方法获取相应的Animator类实例。

另外,ValueAnimator通过添加AnimatorUpdateListener监听器监听值的变化,从而再手动更新目标对象的属性。

最后,通过调用valueAnimator.start()方法启动动画。


objectAnimator


objectAnimator标签对应的类为ObjectAnimator,为ValueAnimator的子类。标签与标签不同的是,可以直接指定动画的目标对象的属性。标签可设置的属性除了和一样的那些,另外多了一个:

  • android:propertyName 目标对象的属性名,要求目标对象必须提供该属性的setter方法,如果动画的时候没有初始值,还需要提供getter方法

还是用实例说明具体用法,还是用上面的例子,将一个按钮的宽度进行缩放,从100%缩放到20%,但这次改用实现。

以下为xml文件的代码:

<!-- res/animator/object_animator.xml -->
<?xml version="1.0" encoding="utf-8"?>
<objectAnimator xmlns:android="http://schemas.android.com/apk/res/android"
    android:duration="3000"
    android:propertyName="width"
    android:valueFrom="100"
    android:valueTo="20"
    android:valueType="intType" />

的例子相比,就只是多了一个android:propertyName的属性,设置值为width。也就是说,动画改变的属性为width,值将从100逐渐减到20。另外,值是从setWidth()传递过去的,再从getWidth()获取。而且,这里设置的值代表的是比例值,因此,还需要进行计算转化为实际的宽度值。最后,对象实际的宽度值为view.getLayoutParams().width。因此,我将用一个包装类来包装原始的view对象,对其提供setWidth()和getWidth()方法,代码如下:

private static class ViewWrapper {
    private View target; //目标对象
    private int maxWidth; //最长宽度值
    public ViewWrapper(View target, int maxWidth) {
        this.target = target;        
        this.maxWidth = maxWidth;
    }    
    public int getWidth() {
        return target.getLayoutParams().width;
    } 
    public void setWidth(int widthValue) {
        //widthValue的值从100到20变化
        target.getLayoutParams().width = maxWidth * widthValue / 100;
        target.requestLayout();
    }
}

上面setWidth()的代码里,根据比例值转化为了实际的宽度值。最后,动画处理的代码如下:

public void onScaleWidth(View view) {    
    // 获取屏幕宽度
    int maxWidth = getWindowManager().getDefaultDisplay().getWidth();    
    // 将目标view进行包装
    ViewWrapper wrapper = new ViewWrapper(view, maxWidth);    
    // 将xml转化为ObjectAnimator对象
    ObjectAnimator objectAnimator = (ObjectAnimator) AnimatorInflater.loadAnimator(this, R.animator.object_animator);       // 设置动画的目标对象为包装后的view
    objectAnimator.setTarget(wrapper);    
    // 启动动画
    objectAnimator.start();
}

ObjectAnimator提供了属性的设置,但相应的需要有该属性的setter和getter方法。而ValueAnimator则只是定义了值的变化,并不指定目标属性,所以也不需要提供setter和getter方法,但只能在AnimatorUpdateListener监听器里手动更新属性。不过,也因为没有指定属性,所以其实更具灵活性了,你可以在监听器里根据值的变化做任何事情,比如更新多个属性,比如在缩放宽度的同时做垂直移动。

为了对View更方便的设置属性动画,Android系统也提供了View的一些属性和相应的setter和getter方法:

  • alpha:透明度,默认为1,表示不透明,0表示完全透明
  • pivotXpivotY:旋转的轴点和缩放的基准点,默认是View的中心点
  • scaleXscaleY:基于pivotX和pivotY的缩放,1表示无缩放,小于1表示收缩,大于1则放大
  • rotationrotationXrotationY:基于轴点(pivotX和pivotY)的旋转,rotation为平面的旋转,rotationX和rotationY为立体的旋转
  • translationXtranslationY:View的屏幕位置坐标变化量,以layout容器的左上角为坐标原点
  • xy:View在父容器内的最终位置,是左上角坐标和偏移量(translationX,translationY)的和


set


set标签对应于AnimatorSet类,可以将多个动画组合成一个动画集,如上面提到的在缩放宽度的同时做垂直移动,可以将一个缩放宽度的动画和一个垂直移动的动画组合在一起。

标签有一个属性可以设置动画的时序关系:

  • android:ordering 设置动画的时序关系,取值可为以下两个值之一:
    together 动画同时执行,默认值
    sequentially 动画按顺序执行

那如果想有些动画同时执行,有些按顺序执行,该怎么办呢?因为标签是可以嵌套其他标签的,也就是说可以将同时执行的组合在一个标签,再嵌在按顺序执行的标签内。

看实例代码吧,以下为xml文件:

<!-- res/animator/animator_set.xml -->
<?xml version="1.0" encoding="utf-8"?>
<set xmlns:android="http://schemas.android.com/apk/res/android"
    android:ordering="together">
    <objectAnimator
        android:duration="3000"
        android:propertyName="width"
        android:valueFrom="100"
        android:valueTo="20"
        android:valueType="intType" />
    <objectAnimator
        android:duration="3000"
        android:propertyName="marginTop"
        android:valueFrom="0"
        android:valueTo="100"
        android:valueType="intType" />
</set>

以上代码可实现两个同时执行的动画,一个将width从100缩放到20,一个将marginTop从0增加到100。多了一个marginTop属性,那么,在ViewWrapper添加setMarginTop()方法,添加后的ViewWrapper类代码如下:

private static class ViewWrapper {    
    private View target;    
    private int maxWidth; 
    public ViewWrapper(View target, int maxWidth) {        
        this.target = target;        
        this.maxWidth = maxWidth;
    } 
    public int getWidth() {        
        return target.getLayoutParams().width;
    }    
    public void setWidth(int widthValue) {
        target.getLayoutParams().width = maxWidth * widthValue / 100;
        target.requestLayout();
    } 
    public void setMarginTop(int margin) {
        LinearLayout.LayoutParams layoutParams = (LinearLayout.LayoutParams) target.getLayoutParams();
        layoutParams.setMargins(0, margin, 0, 0);
        target.setLayoutParams(layoutParams);
    }
}

最后,动画处理的代码:

public void onScaleWidth(View view) {    
    // 获取屏幕宽度
    int maxWidth = getWindowManager().getDefaultDisplay().getWidth();    
    // 将目标view进行包装
    ViewWrapper wrapper = new ViewWrapper(view, maxWidth);    
    // 将xml转化为ObjectAnimator对象
    AnimatorSet animatorSet = (AnimatorSet) AnimatorInflater.loadAnimator(this, R.animator.animator_set);    
    // 设置动画的目标对象为包装后的view
    animatorSet.setTarget(wrapper);    
    // 启动动画
    animatorSet.start();
}

这样就搞定了,实现了宽度缩放和垂直移动的效果。


写在最后


至此,视图动画和属性动画基本的用法都总结完了。示例代码可从github上查看

相关文章
|
14天前
|
搜索推荐 前端开发 API
探索安卓开发中的自定义视图:打造个性化用户界面
在安卓应用开发的广阔天地中,自定义视图是一块神奇的画布,让开发者能够突破标准控件的限制,绘制出独一无二的用户界面。本文将带你走进自定义视图的世界,从基础概念到实战技巧,逐步揭示如何在安卓平台上创建和运用自定义视图来提升用户体验。无论你是初学者还是有一定经验的开发者,这篇文章都将为你打开新的视野,让你的应用在众多同质化产品中脱颖而出。
40 19
|
14天前
|
JSON Java API
探索安卓开发:打造你的首个天气应用
在这篇技术指南中,我们将一起潜入安卓开发的海洋,学习如何从零开始构建一个简单的天气应用。通过这个实践项目,你将掌握安卓开发的核心概念、界面设计、网络编程以及数据解析等技能。无论你是初学者还是有一定基础的开发者,这篇文章都将为你提供一个清晰的路线图和实用的代码示例,帮助你在安卓开发的道路上迈出坚实的一步。让我们一起开始这段旅程,打造属于你自己的第一个安卓应用吧!
41 14
|
17天前
|
Java Linux 数据库
探索安卓开发:打造你的第一款应用
在数字时代的浪潮中,每个人都有机会成为创意的实现者。本文将带你走进安卓开发的奇妙世界,通过浅显易懂的语言和实际代码示例,引导你从零开始构建自己的第一款安卓应用。无论你是编程新手还是希望拓展技术的开发者,这篇文章都将为你打开一扇门,让你的创意和技术一起飞扬。
|
15天前
|
XML 存储 Java
探索安卓开发之旅:从新手到专家
在数字时代,掌握安卓应用开发技能是进入IT行业的关键。本文将引导读者从零基础开始,逐步深入安卓开发的世界,通过实际案例和代码示例,展示如何构建自己的第一个安卓应用。我们将探讨基本概念、开发工具设置、用户界面设计、数据处理以及发布应用的全过程。无论你是编程新手还是有一定基础的开发者,这篇文章都将为你提供宝贵的知识和技能,帮助你在安卓开发的道路上迈出坚实的步伐。
29 5
|
14天前
|
开发框架 Android开发 iOS开发
安卓与iOS开发中的跨平台策略:一次编码,多平台部署
在移动应用开发的广阔天地中,安卓和iOS两大阵营各占一方。随着技术的发展,跨平台开发框架应运而生,它们承诺着“一次编码,到处运行”的便捷。本文将深入探讨跨平台开发的现状、挑战以及未来趋势,同时通过代码示例揭示跨平台工具的实际运用。
|
15天前
|
XML 搜索推荐 前端开发
安卓开发中的自定义视图:打造个性化UI组件
在安卓应用开发中,自定义视图是一种强大的工具,它允许开发者创造独一无二的用户界面元素,从而提升应用的外观和用户体验。本文将通过一个简单的自定义视图示例,引导你了解如何在安卓项目中实现自定义组件,并探讨其背后的技术原理。我们将从基础的View类讲起,逐步深入到绘图、事件处理以及性能优化等方面。无论你是初学者还是有经验的开发者,这篇文章都将为你提供有价值的见解和技巧。
|
15天前
|
搜索推荐 前端开发 测试技术
打造个性化安卓应用:从设计到开发的全面指南
在这个数字时代,拥有一个定制的移动应用不仅是一种趋势,更是个人或企业品牌的重要延伸。本文将引导你通过一系列简单易懂的步骤,从构思你的应用理念开始,直至实现一个功能齐全的安卓应用。无论你是编程新手还是希望拓展技能的开发者,这篇文章都将为你提供必要的工具和知识,帮助你将创意转化为现实。
|
18天前
|
Java 调度 Android开发
安卓与iOS开发中的线程管理差异解析
在移动应用开发的广阔天地中,安卓和iOS两大平台各自拥有独特的魅力。如同东西方文化的差异,它们在处理多线程任务时也展现出不同的哲学。本文将带你穿梭于这两个平台之间,比较它们在线程管理上的核心理念、实现方式及性能考量,助你成为跨平台的编程高手。
|
15天前
|
Java Android开发 开发者
探索安卓开发:构建你的第一个“Hello World”应用
在安卓开发的浩瀚海洋中,每个新手都渴望扬帆起航。本文将作为你的指南针,引领你通过创建一个简单的“Hello World”应用,迈出安卓开发的第一步。我们将一起搭建开发环境、了解基本概念,并编写第一行代码。就像印度圣雄甘地所说:“你必须成为你希望在世界上看到的改变。”让我们一起开始这段旅程,成为我们想要见到的开发者吧!
24 0
|
18天前
|
存储 监控 Java
探索安卓开发:从基础到进阶的旅程
在这个数字时代,移动应用已成为我们日常生活的一部分。对于开发者来说,掌握安卓开发不仅是技能的提升,更是通往创新世界的钥匙。本文将带你了解安卓开发的核心概念,从搭建开发环境到实现复杂功能,逐步深入安卓开发的奥秘。无论你是初学者还是有经验的开发者,这篇文章都将为你提供新的见解和技巧,帮助你在安卓开发的道路上更进一步。
19 0