Android中的属性动画

news2024/11/19 14:29:47

在属性动画出来之前,Android系统提供的动画只有帧动画和View动画。View动画大家可能知道,它提供了AlphaAnimation(透明度),RotateAnimation(负责旋转),TranslateAnimation(负责移动),ScaleAnimation(负责缩放)这4种动画方式。并且提供了AnimationSet动画集合来混合使用多种动画。

但是随着Android 3.0属性动画的推出,View风光不再,一个根本的原因在于View动画有个巨大的缺陷,不具备交互性。就是当某个View发生View动画之后,他的响应事件依然停留在动画运行前的地方。View动画是否就无用武之地了呢?当然不是,他执行效率高,使用方便,可以在一些不需要交互的地方使用。因此在3.0之后,谷歌推出了Animator。也就是属性动画。这个框架中,使用最多的就是AnimatorSet和ObjectAnimator配合。使用ObjectAnimator进行更细化的控制。控制一个对象和一个属性值;而使用多个ObjectAnimator组合到AnimatorSet行成一个动画组合。属性动画的本质是调用属性的get方法,set方法,来真实的控制一个View的属性值。因此,我们可以说属性动画非常强大,基本可以实现所有的动画效果。

1.ObjectAnimator

ObjectAnimator是属性动画最重要的一个类。创建一个ObjectAnimator只需要通过其静态工厂类直接返回一个ObjectAnimator对象。参数包括一个对象和对象的属性名称。注意,这个属性必须有get和set方法。其内部会通过java反射机制来调用set方法,修改对象的属性值。下面我们来看下平移动画是如何实现的。代码:

val ivMy = findViewById<ImageView>(R.id.iv_my)
        findViewById<Button>(R.id.btn_move).setOnClickListener {
            val objectAnimator = ObjectAnimator.ofFloat(ivMy,"translationX",200f)
            objectAnimator.duration = 300
            objectAnimator.start()
        }

没点击按钮移动之前:

当我点击之后:

 

 我们可以查看这个ofFloat方法的源码看下:

public static ObjectAnimator ofFloat(Object target, String propertyName, float... values) {
        ObjectAnimator anim = new ObjectAnimator(target, propertyName);
        anim.setFloatValues(values);
        return anim;
    }

大家从参数很容易看出,第一个参数是要操作的Object,第二个参数是要操作的属性,第三个参数是一个可变的float类型的数组,需要传进去该属性变化的取值过程。我们这里,只设置一个了200.

与View动画一样,也可以给属性动画设置显示时长,插值器等属性。常用的可以直接使用的属性动画的属性值:

1.translationX和translationY:用来沿着X轴或者Y轴进行平移。

2.rotation,rotationX,rotationY:用来围绕View的支点进行旋转。

3.PrivotX和PrivotY:View的支点位置,默认是View的中心点。

4.alpha:透明度,默认值是1(不透明),0代表完全透明

5.x和y:描述View对象在其容器中的最终位置。

前面说过,要操作的属性必须要有get和set方法,不然ObjectAnimator就无法生效。如果一个属性没有get和set方法,咱也可以通过自定义一个属性类或者包装类来间接的给这个属性增加get方法和set方法。来看下咋弄的:

新建一个类,把View传递进来,然后对这个View的属性进行更改,这种方式,在设计模式上叫做包装类:

package com.example.myapplication.views

import android.view.View

class MyView(view:View) {
    private val mTarget = view
    fun getWidth(){
        mTarget.layoutParams.width
    }
    fun setWidth(width:Int){
        mTarget.layoutParams.width = width
        mTarget.requestLayout()
    }

}

然后更改宽度:

findViewById<Button>(R.id.btn_move).setOnClickListener {
          val myView = MyView(ivMy)
            ObjectAnimator.ofInt(myView,"width",100).setDuration(300).start()
        }

当我点击之后,宽度变小了很多:

2.ValueAnimator

ValueAnimator本身不提供任何的动画效果。他更像是一个数值变化产生器,用来产生一定规律的数字,从而让调用者控制动画的实现过程。我们来看下:

findViewById<Button>(R.id.btn_move).setOnClickListener {
            val valueAnimator = ValueAnimator.ofFloat(0f,100f)
          
            valueAnimator.duration = 1000
            valueAnimator.start()
            valueAnimator.addUpdateListener {
                val floatValue = it.animatedValue as Float
                Log.d(TAG,"floatValue:$floatValue")
            }
        }

 这里产生了一系列的数字变化过程,看下log:

 但是这里也仅仅是产生了数字变化,还没有产生动画的变化。如果想要动画变化,可以在这里用这个数字进行设置属性:

valueAnimator.addUpdateListener {
                val floatValue = it.animatedValue as Float
                Log.d(TAG,"value:$floatValue")
                val layoutParams = ivMy.layoutParams
                layoutParams.width = floatValue.toInt()
                ivMy.layoutParams = layoutParams
            }

也就是说在addUpdateListener里监听数字的变化,然后应用到view身上。这里,view的宽度就会从小到大变化。

3.动画的监听

完成的动画过程,有start,repeat,end,cancel这4个过程,代码如下:

 val animator = ObjectAnimator.ofFloat(ivMy,"alpha",1.5f)
           animator.addListener(object :Animator.AnimatorListener{
               override fun onAnimationStart(animation: Animator?) {
               }

               override fun onAnimationEnd(animation: Animator?) {
               }

               override fun onAnimationCancel(animation: Animator?) {
               }

               override fun onAnimationRepeat(animation: Animator?) {
               }

           })

分别代表,开始,结束,取消,重复。

大部分时候我们只关心end,结束的时候。但是Lister必须得实现全部4个。所以呢,android也提供了AnimationListenerAdapter来让我们选择必要的事件进行监听:

 val animator = ObjectAnimator.ofFloat(ivMy,"alpha",1.5f)
            animator.addListener(object: AnimatorListenerAdapter() {
                override fun onAnimationEnd(animation: Animator?) {
                    super.onAnimationEnd(animation)
                }
            })

这里,我们就是只监听了end.

4.组合动画 AnimatrorSet

AnimatorSet提供了一个play方法,参数是一个Animator,返回一个AnimatorSet.Builder的实例,我们看下源码:

  public Builder play(Animator anim) {
        if (anim != null) {
            return new Builder(anim);
        }
        return null;
    }

接着我们看看他返回的这个内部类Builder:

public class Builder {

        /**
         * This tracks the current node being processed. It is supplied to the play() method
         * of AnimatorSet and passed into the constructor of Builder.
         */
        private Node mCurrentNode;

        /**
         * package-private constructor. Builders are only constructed by AnimatorSet, when the
         * play() method is called.
         *
         * @param anim The animation that is the dependency for the other animations passed into
         * the other methods of this Builder object.
         */
        Builder(Animator anim) {
            mDependencyDirty = true;
            mCurrentNode = getNodeForAnimation(anim);
        }

        /**
         * Sets up the given animation to play at the same time as the animation supplied in the
         * {@link AnimatorSet#play(Animator)} call that created this <code>Builder</code> object.
         *
         * @param anim The animation that will play when the animation supplied to the
         * {@link AnimatorSet#play(Animator)} method starts.
         */
        public Builder with(Animator anim) {
            Node node = getNodeForAnimation(anim);
            mCurrentNode.addSibling(node);
            return this;
        }

        /**
         * Sets up the given animation to play when the animation supplied in the
         * {@link AnimatorSet#play(Animator)} call that created this <code>Builder</code> object
         * ends.
         *
         * @param anim The animation that will play when the animation supplied to the
         * {@link AnimatorSet#play(Animator)} method ends.
         */
        public Builder before(Animator anim) {
            Node node = getNodeForAnimation(anim);
            mCurrentNode.addChild(node);
            return this;
        }

        /**
         * Sets up the given animation to play when the animation supplied in the
         * {@link AnimatorSet#play(Animator)} call that created this <code>Builder</code> object
         * to start when the animation supplied in this method call ends.
         *
         * @param anim The animation whose end will cause the animation supplied to the
         * {@link AnimatorSet#play(Animator)} method to play.
         */
        public Builder after(Animator anim) {
            Node node = getNodeForAnimation(anim);
            mCurrentNode.addParent(node);
            return this;
        }

        /**
         * Sets up the animation supplied in the
         * {@link AnimatorSet#play(Animator)} call that created this <code>Builder</code> object
         * to play when the given amount of time elapses.
         *
         * @param delay The number of milliseconds that should elapse before the
         * animation starts.
         */
        public Builder after(long delay) {
            // setup a ValueAnimator just to run the clock
            ValueAnimator anim = ValueAnimator.ofFloat(0f, 1f);
            anim.setDuration(delay);
            after(anim);
            return this;
        }

    }

可以看出,Builder类采用了建造者模式,每次调用方法时都返回Builder自身用于继续构建。AnimatorSet.Builder中包括以下4个方法:

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

接下来,我们使用代码来说明:

val animator1 = ObjectAnimator.ofFloat(ivMy,"translationX",0.0f,200.0f,0.0f)
            val animator2 = ObjectAnimator.ofFloat(ivMy,"scaleX",1.0f,2.0f)
            val animator3 = ObjectAnimator.ofFloat(ivMy,"rotationX",0.0f,90.0f,0.0f)
            val set = AnimatorSet()
            set.duration = 1000
            set.play(animator1).with(animator2).after(animator3)
            set.start()

这里,会先执行3,然后同时执行1和2.

看看效果:

 可以看到,先执行的翻转。

5.组合动画 PropertyValuesHolder

除了AnimatorSet类,还可以使用PropertyValuesHolder类来实现组合动画,不过这个组合动画没有AnimaotorSet类所实现的组合动画复杂。使用PropertyValuesHolder类只能做到多个动画一起执行。主要是使用ObjectAnimator.ofPropertyValuesHolder()方法,我们来看下代码:

 val valuesHolder1 = PropertyValuesHolder.ofFloat("scaleX",1.0f,1.5f)
            val valuesHolder2 = PropertyValuesHolder.ofFloat("rotationX",0.0f,90.0f,0.0f)
            val valuesHolder3 = PropertyValuesHolder.ofFloat("alpha",1.0f,0.3f,1.0f)
            val objectAnimator = ObjectAnimator.ofPropertyValuesHolder(ivMy,valuesHolder1,valuesHolder2,valuesHolder3)
            objectAnimator.setDuration(2000).start()

6.在XML中使用属性动画

和View动画一样,属性动画也可以直接写在XML里。在res文件中新建animator文件夹,在里面新建一个scale.xml。内容如下:

<?xml version="1.0" encoding="utf-8"?>
<objectAnimator xmlns:android="http://schemas.android.com/apk/res/android"
    android:duration="3000"
    android:propertyName="scaleX"
    android:valueFrom="1.0"
    android:valueTo="2.0"
    android:valueType="floatType"
    >

</objectAnimator>

在程序中使用:

val animator = AnimatorInflater.loadAnimator(this,R.animator.scale)
            animator.setTarget(ivMy)
            animator.start()

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.coloradmin.cn/o/124850.html

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈,一经查实,立即删除!

相关文章

2022年广西建筑安全员考试真题题库及答案

百分百题库提供建筑安全员考试试题、安全员证考试真题、安全员证考试题库等,提供在线做题刷题&#xff0c;在线模拟考试&#xff0c;助你考试轻松过关。 100.《中华人民共和国建筑法》规定,建设单位申请领取施工许可证,应当具备下列条件有() A.已经办理该建筑工程用地批准手续…

手绘图说电子元器件-晶体管

晶体二极管与单结晶体管 晶体二极管是电子电路中最重要的半导体器件,包括一般二极管和特殊二极管两大类。 晶体二极管 晶体二极管简称二极管,是一种常用的具有一个PN结的半导体器件。 晶体二极管的极性 晶体二极管两引脚有正、负极之分 晶体二极管的参数 晶体二极管的…

HOW POWERFUL ARE GRAPH NEURAL NETWORKS? 论文/GIN学习笔记

对GNN的评估 GNN 通用表达式 聚合&#xff1a; av(k)AGGREAGTE(k)({hu(k−1):u∈N(v)})a_v^{(k)}AGGREAGTE^{(k)}(\{ h_u^{(k-1)} : u \in \mathcal{N}(v) \}) av(k)​AGGREAGTE(k)({hu(k−1)​:u∈N(v)}) 更新&#xff1a; hv(k)COMBINE(k)(hv(k−1),av(k))h_v^{(k)} COMB…

【JavaSE成神之路】流程控制语句

哈喽&#xff0c;我是兔哥呀&#xff0c;今天就让我们继续这个JavaSE成神之路&#xff01; 这一节啊&#xff0c;咱们要学习的内容是流程控制语句。 先来看概念 Java的流程控制语句是指用来控制程序执行流程的语句&#xff0c;它们可以改变程序的执行顺序&#xff0c;使程序更…

javaee之SpringMVC1

三层架构与MVC设计模式介绍 一张图介绍 之前在写软件设计的三层架构的时候&#xff0c;有一张图直接拿过来 springMVC的一些简单介绍 入门案例 一、入门案例之需求分析 二、搭建开发环境 1.利用骨架创建一个maven项目 因为这个项目要部署到服务器&#xff0c;所以采用如下骨…

代码质量与安全 | 如何将清洁代码标准扩展到整个企业,促进业务上的成功?

清洁代码能够让软件开发工作变得更简单、更有趣。因为如果代码不够清洁&#xff0c;开发人员将花费很多时间在解决编码问题上&#xff0c;使他们无法将精力投入开发新代码、解决其他更有趣的问题上。 那么&#xff0c;该如何将清洁代码标准扩展到整个企业呢&#xff1f;阅读本…

操作系统:进程与线程之间的区别及联系

一、定义 1、进程&#xff1a;进程是一个具有独立功能的程序关于某个数据集合的以此运行活动。 是系统进行资源分配和调度的独立单位&#xff0c;也是基本的执行单元。是一个动态的概念&#xff0c;是一个活动的实体。它不只是程序的代码&#xff0c;还包括当前的活动。 进程…

学习UI设计都可以学习哪些软件?

学UI设计要学习哪些工具软件&#xff1f;今天来做个大盘点 ​Photoshop(简称PS) 不懂设计的人也听说过PS著名大名。确切地说&#xff0c;确切地说&#xff0c;PS它是一款强大的综合性软件&#xff0c;主要用于位图处理&#xff0c;可以做图片处理&#xff0c;也可以做UI设计、平…

【财务】FMS财务管理系统---日常数据核对与处理

开发FMS财务管理系统&#xff0c;每天都会与数据打交道&#xff0c;数据的核对与处理是日常工作的一部分&#xff0c;相信接触过财务系统的同学都深有感触&#xff0c;为了一个差异查来查去&#xff0c;最终发现是前端业务系统的问题&#xff0c;白忙一场&#xff0c;本篇就说一…

筑博设计:知识管理数字化提升企业核心竞争力

编者按&#xff1a;这是一个真正与业务相融合的知识管理系统&#xff0c;筑博设计陈主任的分享专业且精细&#xff0c;从容而豁达&#xff0c;不仅有行业的经验&#xff0c;还有做事的方法&#xff0c;更有做人的情怀&#xff0c;让我们看到了“知识就是力量&#xff0c;知识使…

【HTML+CSS+JavaScript】实现萤火虫闪烁效果

文章目录【HTMLCSSJavaScript】实现萤火虫闪烁效果一. 效果图二. HTML部分代码三. CSS部分代码四. JavaScript部分代码五. 外部js文件代码六. 完整的代码和图片获取【HTMLCSSJavaScript】实现萤火虫闪烁效果 本文主要讲解屏幕不断闪烁星光效果&#xff08;配合夜晚深林的背景图…

使用DataSecurity Plus监控Windows文件完整性

使用我们的文件完整性监控软件DataSecurity Plus&#xff0c;与精明的黑客&#xff0c;突发的恶意软件爆发以及挑战法规要求进行战斗。这种先进的工具还有助于生成可操作的报表&#xff0c;提供基于严重性的告警&#xff0c;简化合规性要求&#xff0c;发现妥协指标&#xff0c…

ip route常用语法

官方手册参考&#xff1a; ip-rule(8) - Linux manual page #interface ens11f0 #Bring up an interface ip link set dev ens11f0 up ip addr add 11.126.48.231/27 dev ens11f0#Use table 101 with metrics 101 for specific interface routing #Each interface should use i…

科技云报道:信创基础设施迎来“升级潮”,可持续性架构成关键技术

科技云报道原创。 如果说单一领域的技术突破是河流&#xff0c;信创毫无疑问是汪洋大海。 随着“数字中国”战略的明确&#xff0c;数字化转型进程加速&#xff0c;信创产业成为国家战略布局的重点领域之一&#xff0c;是缩短科技发展周期以及国内外科技差距的一剂良方。 2…

立根铸魂,崛起数智时代 | 麒麟信安携手openEuler 共推操作系统产业腾飞发展!

操作系统产业峰会2022于12月28日在线上举办。本次峰会由开放原子开源基金会、中国软件行业协会、CCF&#xff08;中国计算机学会&#xff09;开源专委会、绿色计算产业联盟、中关村科学城管委会共同主办&#xff0c;华为、麒麟信安等单位共同协办&#xff0c;以“立根铸魂 崛起…

数据结构基础:P1-基本概念----编程作业02:Maximum Subsequence Sum

本系列文章为浙江大学陈越、何钦铭数据结构学习笔记&#xff0c;前面文章链接如下&#xff1a; 数据结构基础&#xff1a;P1-基本概念 数据结构基础&#xff1a;P1-基本概念----编程作业01&#xff1a;最大子列和问题 文章目录一、题目描述二、代码实现一、题目描述 题目描述&…

【408篇】C语言笔记-第十九章(C语言语法进阶)

文章目录第一节&#xff1a;条件运算符与逗号运算符1. 条件运算符2. 逗号运算符第二节&#xff1a;自增自减运算符1. 自增自减运算符2. 自增自减运算符与取值运算符第三节&#xff1a;位运算符1. 位运算符2. 异或运算符实例解析第四节&#xff1a;switch和do while讲解1. switc…

使用Bigemap计算挖填土石方量

1、 Bigemap GIS Office 第一步&#xff1a; 用Bigemap GIS Office导出待计算区域的高程数据&#xff1a; 1.1 打开Bigemap GIS Office选择目标区域&#xff1a;如下图&#xff1a; 1.2选择的目标区域的高程数据&#xff08;NASA, TIF, 高精度高程数据&#xff09; 1…

【SSM框架】MyBatis核心配置文件详解

1.MyBatis核心配置文件之environments <!--environments&#xff1a;配置多个连接数据库的环境 属性&#xff1a;default&#xff1a;设置默认使用的环境的id --><environments default"development"><!--environment&#xff1a;配置某个具体的环境 …

C++ opencv形态学、轮廓查找、特征检测和图像分割

C opencv形态学、轮廓查找、特征检测和图像分割形态学基本处理方法二值化全局二值化局部二值化腐蚀和膨胀图像形态学运算开运算闭运算顶帽黑帽代码图像轮廓寻找轮廓绘画轮廓轮廓的面积和周长多边形逼近和凸包多边形逼近凸包外接矩形最小外接矩形最大外接矩形案例车辆检测&#…