Android 之 动画合集之属性动画 -- 初见

news2024/11/19 0:43:15

本节引言:

本节给带来的是Android动画中的第三种动画——属性动画(Property Animation), 记得在上一节Android 之 动画合集之补间动画为Fragment 设置过渡动画的时候,说过,App包和V4包下的Fragment调用setCustomAnimations()对应的 动画类型是不一样的,v4包下的是Animation,而app包下的是Animator

Animation一般动画就是我们前面学的帧动画和补间动画Animator则是本节要讲的属性动画


1.属性动画概念叨叨逼

直接上图


2.ValueAnimator简单使用

使用流程

  • 1.调用ValueAnimator的ofInt(),ofFloat()或ofObject()静态方法创建ValueAnimator实例
  • 2.调用实例的setXxx方法设置动画持续时间,插值方式,重复次数等
  • 3.调用实例的addUpdateListener添加AnimatorUpdateListener监听器,在该监听器中 可以获得ValueAnimator计算出来的值,你可以值应用到指定对象上~
  • 4.调用实例的start()方法开启动画! 另外我们可以看到ofInt和ofFloat都有个这样的参数:float/int... values代表可以多个值!

使用示例

代码实现

布局文件:activity_main.xml,非常简单,四个按钮,一个ImageView

<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
    android:id="@+id/ly_root"
    android:layout_width="match_parent"
    android:layout_height="match_parent"
    android:orientation="vertical">

    <Button
        android:id="@+id/btn_one"
        android:layout_width="wrap_content"
        android:layout_height="wrap_content"
        android:text="动画1" />

    <Button
        android:id="@+id/btn_two"
        android:layout_width="wrap_content"
        android:layout_height="wrap_content"
        android:text="动画2" />

    <Button
        android:id="@+id/btn_three"
        android:layout_width="wrap_content"
        android:layout_height="wrap_content"
        android:text="动画3" />

    <Button
        android:id="@+id/btn_four"
        android:layout_width="wrap_content"
        android:layout_height="wrap_content"
        android:text="动画4" />

    <ImageView
        android:id="@+id/img_babi"
        android:layout_width="wrap_content"
        android:layout_height="wrap_content"
        android:layout_gravity="center"
        android:background="@mipmap/img_babi" />

</LinearLayout>

接着到MainActivity.java, 首先需要一个修改View位置的方法,这里调用moveView()设置左边和上边的起始坐标以及宽高!

接着定义了四个动画,分别是:直线移动,缩放,旋转加透明,以及圆形旋转!

然后通过按钮触发对应的动画~

public class MainActivity extends AppCompatActivity implements View.OnClickListener {

    private Button btn_one;
    private Button btn_two;
    private Button btn_three;
    private Button btn_four;
    private LinearLayout ly_root;
    private ImageView img_babi;
    private int width;
    private int height;

    @Override
    public void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        setContentView(R.layout.activity_main);
        bindViews();
    }

    private void bindViews() {
        ly_root = (LinearLayout) findViewById(R.id.ly_root);
        btn_one = (Button) findViewById(R.id.btn_one);
        btn_two = (Button) findViewById(R.id.btn_two);
        btn_three = (Button) findViewById(R.id.btn_three);
        btn_four = (Button) findViewById(R.id.btn_four);
        img_babi = (ImageView) findViewById(R.id.img_babi);

        btn_one.setOnClickListener(this);
        btn_two.setOnClickListener(this);
        btn_three.setOnClickListener(this);
        btn_four.setOnClickListener(this);
        img_babi.setOnClickListener(this);
    }

    @Override
    public void onClick(View v) {
        switch (v.getId()) {
            case R.id.btn_one:
                lineAnimator();
                break;
            case R.id.btn_two:
                scaleAnimator();
                break;
            case R.id.btn_three:
                raAnimator();
                break;
            case R.id.btn_four:
                circleAnimator();
                break;
            case R.id.img_babi:
                Toast.makeText(MainActivity.this, "不愧是coder-pig~", Toast.LENGTH_SHORT).show();
                break;
        }
    }


    //定义一个修改ImageView位置的方法
    private void moveView(View view, int rawX, int rawY) {
        int left = rawX - img_babi.getWidth() / 2;
        int top = rawY - img_babi.getHeight();
        int width = left + view.getWidth();
        int height = top + view.getHeight();
        view.layout(left, top, width, height);
    }


    //定义属性动画的方法:

    //按轨迹方程来运动
    private void lineAnimator() {
        width = ly_root.getWidth();
        height = ly_root.getHeight();
        ValueAnimator xValue = ValueAnimator.ofInt(height,0,height / 4,height / 2,height / 4 * 3 ,height);
        xValue.setDuration(3000L);
        xValue.addUpdateListener(new ValueAnimator.AnimatorUpdateListener() {
            @Override
            public void onAnimationUpdate(ValueAnimator animation) {
                // 轨迹方程 x = width / 2
                int y = (Integer) animation.getAnimatedValue();
                int x = width / 2;
                moveView(img_babi, x, y);
            }
        });
        xValue.setInterpolator(new LinearInterpolator());
        xValue.start();
    }

    //缩放效果
    private void scaleAnimator(){
    
        //这里故意用两个是想让大家体会下组合动画怎么用而已~
        final float scale = 0.5f;
        AnimatorSet scaleSet = new AnimatorSet();
        ValueAnimator valueAnimatorSmall = ValueAnimator.ofFloat(1.0f, scale);
        valueAnimatorSmall.setDuration(500);

        ValueAnimator valueAnimatorLarge = ValueAnimator.ofFloat(scale, 1.0f);
        valueAnimatorLarge.setDuration(500);

        valueAnimatorSmall.addUpdateListener(new ValueAnimator.AnimatorUpdateListener() {
            @Override
            public void onAnimationUpdate(ValueAnimator animation) {
                float scale = (Float) animation.getAnimatedValue();
                img_babi.setScaleX(scale);
                img_babi.setScaleY(scale);
            }
        });
        valueAnimatorLarge.addUpdateListener(new ValueAnimator.AnimatorUpdateListener() {
            @Override
            public void onAnimationUpdate(ValueAnimator animation) {
                float scale = (Float) animation.getAnimatedValue();
                img_babi.setScaleX(scale);
                img_babi.setScaleY(scale);
            }
        });

        scaleSet.play(valueAnimatorLarge).after(valueAnimatorSmall);
        scaleSet.start();

        //其实可以一个就搞定的
//        ValueAnimator vValue = ValueAnimator.ofFloat(1.0f, 0.6f, 1.2f, 1.0f, 0.6f, 1.2f, 1.0f);
//        vValue.setDuration(1000L);
//        vValue.addUpdateListener(new ValueAnimator.AnimatorUpdateListener() {
//            @Override
//            public void onAnimationUpdate(ValueAnimator animation) {
//                float scale = (Float) animation.getAnimatedValue();
//                img_babi.setScaleX(scale);
//                img_babi.setScaleY(scale);
//            }
//        });
//        vValue.setInterpolator(new LinearInterpolator());
//        vValue.start();
    }


    //旋转的同时透明度变化
    private void raAnimator(){
        ValueAnimator rValue = ValueAnimator.ofInt(0, 360);
        rValue.setDuration(1000L);
        rValue.addUpdateListener(new ValueAnimator.AnimatorUpdateListener() {
            @Override
            public void onAnimationUpdate(ValueAnimator animation) {
                int rotateValue = (Integer) animation.getAnimatedValue();
                img_babi.setRotation(rotateValue);
                float fractionValue = animation.getAnimatedFraction();
                img_babi.setAlpha(fractionValue);
            }
        });
        rValue.setInterpolator(new DecelerateInterpolator());
        rValue.start();
    }

    //圆形旋转
    protected void circleAnimator() {
        width = ly_root.getWidth();
        height = ly_root.getHeight();
        final int R = width / 4;
        ValueAnimator tValue = ValueAnimator.ofFloat(0,
                (float) (2.0f * Math.PI));
        tValue.setDuration(1000);
        tValue.addUpdateListener(new ValueAnimator.AnimatorUpdateListener() {
            @Override
            public void onAnimationUpdate(ValueAnimator animation) {
                // 圆的参数方程 x = R * sin(t) y = R * cos(t)
                float t = (Float) animation.getAnimatedValue();
                int x = (int) (R * Math.sin(t) + width / 2);
                int y = (int) (R * Math.cos(t) + height / 2);
                moveView(img_babi, x, y);
            }
        });
        tValue.setInterpolator(new DecelerateInterpolator());
        tValue.start();
    }
}

好的,使用的流程非常简单,先创建ValueAnimator对象,调用ValueAnimator.ofInt/ofFloat 获得,然后设置动画持续时间,addUpdateListener添加AnimatorUpdateListener事件监听, 然后使用参数animationgetAnimatedValue()获得当前的值,然后我们可以拿着这个值 来修改View的一些属性,从而形成所谓的动画效果,接着设置setInterpolator动画渲染模式, 最后调用start()开始动画的播放~

卧槽,直线方程,圆的参数方程,我都开始方了,这不是高数的东西么, 挂科学渣连三角函数都忘了...

例子参考自github:MoveViewValueAnimator


3.ObjectAnimator简单使用

比起ValueAnimator,ObjectAnimator显得更为易用,通过该类我们可以直接 对任意对象的任意属性进行动画操作!没错,是任意对象,而不单单只是View对象, 不断地对对象中的某个属性值进行赋值,然后根据对象属性值的改变再来决定如何展现 出来!比如为TextView设置如下动画: ObjectAnimator.ofFloat(textview, "alpha", 1f, 0f);
这里就是不断改变alpha的值,从1f - 0f,然后对象根据属性值的变化来刷新界面显示,从而 展现出淡入淡出的效果,而在TextView类中并没有alpha这个属性,ObjectAnimator内部机制是: 寻找传输的属性名对应的get和set方法~,而非找这个属性值! 不信的话你可以到TextView的源码里找找是否有alpha这个属性! 好的,下面我们利用ObjectAnimator来实现四种补间动画的效果吧~

运行效果图

代码实现

布局直接用的上面那个布局,加了个按钮,把ImageView换成了TextView,这里就不贴代码了, 直接上MainActivity.java部分的代码,其实都是大同小异的~

public class MainActivity extends AppCompatActivity implements View.OnClickListener {
    private Button btn_one;
    private Button btn_two;
    private Button btn_three;
    private Button btn_four;
    private Button btn_five;
    private LinearLayout ly_root;
    private TextView tv_pig;
    private int height;
    private ObjectAnimator animator1;
    private ObjectAnimator animator2;
    private ObjectAnimator animator3;
    private ObjectAnimator animator4;
    private AnimatorSet animSet;

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

    private void bindViews() {
        ly_root = (LinearLayout) findViewById(R.id.ly_root);
        btn_one = (Button) findViewById(R.id.btn_one);
        btn_two = (Button) findViewById(R.id.btn_two);
        btn_three = (Button) findViewById(R.id.btn_three);
        btn_four = (Button) findViewById(R.id.btn_four);
        btn_five = (Button) findViewById(R.id.btn_five);
        tv_pig = (TextView) findViewById(R.id.tv_pig);

        height = ly_root.getHeight();
        btn_one.setOnClickListener(this);
        btn_two.setOnClickListener(this);
        btn_three.setOnClickListener(this);
        btn_four.setOnClickListener(this);
        btn_five.setOnClickListener(this);
        tv_pig.setOnClickListener(this);
    }

    //初始化动画
    private void initAnimator() {
        animator1 = ObjectAnimator.ofFloat(tv_pig, "alpha", 1f, 0f, 1f, 0f, 1f);
        animator2 = ObjectAnimator.ofFloat(tv_pig, "rotation", 0f, 360f, 0f);
        animator3 = ObjectAnimator.ofFloat(tv_pig, "scaleX", 2f, 4f, 1f, 0.5f, 1f);
        animator4 = ObjectAnimator.ofFloat(tv_pig, "translationY", height / 8, -100, height / 2);
    }

    @Override
    public void onClick(View v) {
        switch (v.getId()) {
            case R.id.btn_one:
                animator1.setDuration(3000l);
                animator1.start();
                break;
            case R.id.btn_two:
                animator2.setDuration(3000l);
                animator2.start();
                break;
            case R.id.btn_three:
                animator3.setDuration(3000l);
                animator3.start();
                break;
            case R.id.btn_four:
                animator4.setDuration(3000l);
                animator4.start();
                break;
            case R.id.btn_five:
                //将前面的动画集合到一起~
                animSet = new AnimatorSet();
                animSet.play(animator4).with(animator3).with(animator2).after(animator1);
                animSet.setDuration(5000l);
                animSet.start();
                break;
            case R.id.tv_pig:
                Toast.makeText(MainActivity.this, "不愧是coder-pig~", Toast.LENGTH_SHORT).show();
                break;
        }
    }
}

用法也非常简单,上面涉及到的组合动画我们下面讲~


4.组合动画与AnimatorListener

从上面两个例子中我们都体验了一把组合动画,用到了AnimatorSet这个类!

我们调用的play()方法,然后传入第一个开始执行的动画,此时他会返回一个Builder类给我们:

接下来我们可以调用Builder给我们提供的四个方法,来组合其他的动画:

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

嗯,很简单,接下来要说下动画事件的监听,上面我们ValueAnimator的监听器是 AnimatorUpdateListener,当值状态发生改变时候会回调onAnimationUpdate方法!

除了这种事件外还有:动画进行状态的监听~ AnimatorListener,我们可以调用addListener方法 添加监听器,然后重写下面四个回调方法:

  • onAnimationStart():动画开始
  • onAnimationRepeat():动画重复执行
  • onAnimationEnd():动画结束
  • onAnimationCancel():动画取消

没错,加入你真的用AnimatorListener的话,四个方法你都要重写,当然和前面的手势那一节一样, Android已经给我们提供好一个适配器类:AnimatorListenerAdapter,该类中已经把每个接口 方法都实现好了,所以我们这里只写一个回调方法也可以额!


5.使用XML来编写动画

使用XML来编写动画,画的时间可能比Java代码长一点,但是重用起来就轻松很多! 对应的XML标签分别为:<animator><objectAnimator><set> 相关的属性解释如下:

  • android:ordering:指定动画的播放顺序:sequentially(顺序执行),together(同时执行)
  • android:duration:动画的持续时间
  • android:propertyName="x":这里的x,还记得上面的"alpha"吗?加载动画的那个对象里需要 定义getx和setx的方法,objectAnimator就是通过这里来修改对象里的值的!
  • android:valueFrom="1" :动画起始的初始值
  • android:valueTo="0" :动画结束的最终值
  • android:valueType="floatType":变化值的数据类型

使用例子如下

从0到100平滑过渡的动画

<animator xmlns:android="http://schemas.android.com/apk/res/android"  
    android:valueFrom="0"  
    android:valueTo="100"  
    android:valueType="intType"/>

将一个视图的alpha属性从1变成0

<objectAnimator xmlns:android="http://schemas.android.com/apk/res/android"  
    android:valueFrom="1"  
    android:valueTo="0"  
    android:valueType="floatType"  
    android:propertyName="alpha"/>

set动画使用演示

<set android:ordering="sequentially" >
    <set>
        <objectAnimator
            android:duration="500"
            android:propertyName="x"
            android:valueTo="400"
            android:valueType="intType" />
        <objectAnimator
            android:duration="500"
            android:propertyName="y"
            android:valueTo="300"
            android:valueType="intType" />
    </set>
    <objectAnimator
        android:duration="500"
        android:propertyName="alpha"
        android:valueTo="1f" />
</set>

加载我们的动画文件

AnimatorSet set = (AnimatorSet)AnimatorInflater.loadAnimator(mContext, 
             R.animator.property_animator);  
animator.setTarget(view);  
animator.start();  

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

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

相关文章

白皮书精彩案例分享 | 数字孪生:让治水用水有了“智慧大脑”

山有百藏而不言&#xff0c;水润万物而不语。中国属于大河文明&#xff0c;农业历来在经济中占主导地位&#xff0c;其中水利灌溉是保证农业生产和提高农业产量的重要因素。 然而&#xff0c;由于过去水利工程建设缺乏预见性&#xff0c;传统水利工程在作出贡献的同时&#xf…

JavaScript 简单实现观察者模式和发布订阅模式

JavaScript 简单实现观察者模式和发布订阅模式 1. 观察者模式1.1 如何理解1.2 代码实现 2. 发布订阅模式2.1 如何理解2.2 代码实现 1. 观察者模式 1.1 如何理解 概念&#xff1a;观察者模式定义对象间的一种一对多的依赖关系&#xff0c;当一个对象的状态发生改变时&#xff…

重生之我要学C++第三天(类和对象)

我重生了&#xff0c;这篇文章就深入的探讨C中的类和对象。 一.类的引入和定义 类的引入&#xff1a;在C语言中&#xff0c;结构体内部只能定义变量或者结构体&#xff0c;C中对结构体进行了升级->类&#xff0c;C的类中既可以定义变量&#xff0c;又可以定义函数。类中的变…

TSINGSEE青犀视频安防监控视频平台EasyCVR新增密码复杂度提示

智能视频监控平台TSINGSEE青犀视频EasyCVR可拓展性强、视频能力灵活、部署轻快&#xff0c;可支持的主流标准协议有国标GB28181、RTSP/Onvif、RTMP等&#xff0c;以及支持厂家私有协议与SDK接入&#xff0c;包括海康Ehome、海大宇等设备的SDK等&#xff0c;能对外分发RTSP、RTM…

BERT模型和Big Bird模型对比

BERT模型简介 BERT模型是基于Transformers的双向编码器表示&#xff08;BERT&#xff09;&#xff0c;在所有层中调整左右情境&#xff08;学习上下层语义信息&#xff09;。 Transformer是一种深度学习组件&#xff0c;能够处理并行序列、分析更大规模的数据、加快模型训练速…

sftp和scp协议,哪个传大文件到服务器传输速率快?

环境&#xff1a; 1.Win scp 6.1.1 2.XFTP 7 3.9.6G压缩文件 4.Centos 7 5.联想E14笔记本Win10 6.HW-S1730S-S48T4S-A交换机 问题描述&#xff1a; sftp和scp协议&#xff0c;哪个传大文件到服务器速度快&#xff1f; 1.SFTP 基于SSH加密传输文件&#xff0c;可靠性高&am…

Profinet转EtherNet/IP网关连接AB PLC的应用案例

西门子S7-1500 PLC&#xff08;profinet&#xff09;与AB PLC以太网通讯&#xff08;EtherNet/IP&#xff09;。本文主要介绍捷米特JM-EIP-PN的Profinet转EtherNet/IP网关&#xff0c;连接西门子S7-1500 PLC与AB PLC 通讯的配置过程&#xff0c;供大家参考。 1, 新建工程&…

护网行动:ADSelfService Plus引领企业网络安全新纪元

随着信息技术的飞速发展&#xff0c;企业网络的重要性变得愈发显著。然而&#xff0c;随之而来的网络安全威胁也日益增多&#xff0c;网络黑客和恶意软件不断涌现&#xff0c;给企业的数据和机密信息带来巨大风险。在这个信息安全威胁层出不穷的时代&#xff0c;企业急需一款强…

API攻击原理,以及如何识别和预防

攻击者知道在针对API时如何避开WAF和API网关。以下是一些公司应对API攻击快速增长的示例。 5月初&#xff0c;Pen Test Partners 安全研究员 Jan Masters 发现&#xff0c;他竟然能够在未经身份验证的情况下&#xff0c;向Peloton的官方API提出可获取其它用户私人数据的请求&am…

TEE GP(Global Platform)功能认证产品

TEE之GP(Global Platform)认证汇总 一、功能认证产品介绍 选择Functional和TEE Initial Configuration v1.1&#xff0c;然后SEARCH&#xff0c;可以看到TEE对应的功能认证产品。 二、CK810MFT V3.8, ERAGON V3, ALIBABA CLOUD LINK TEE V1.2.0 参考&#xff1a; GlobalPlatf…

知乎高赞|什么是低代码,强烈推荐!

本文摘自知乎用户吴多益的文章《从实现原理看低代码》&#xff0c;与以往抽象的定义不同&#xff0c;本文是从代码的角度定义低代码&#xff0c;有非常高的学习价值&#xff01;欢迎大家去看原文。 在讨论各个低代码方案前&#xff0c;首先要明确「低代码」究竟是什么&#xff…

微信联系人批量删除功能如何操作?删除的联系人如何恢复?

继微信推出了朋友圈置顶功能后&#xff0c;微信又推出了"批量删除好友的功能" &#xff0c;具体的操作步骤如下&#xff1a; 第一步 是点击聊天界面上的搜索框"搜索" 第二步 "搜索"排序字母&#xff0c;点击"更多联系人" 第三步 搜…

GNN的一篇入门 :A Gentle Introduction to Graph Neural Networks

原文链接 A Gentle Introduction to Graph Neural Networks (distill.pub)https://distill.pub/2021/gnn-intro/ 内容简介&#xff1a;本文是“A Gentle Introduction to Graph Neural Networks”的阅读笔记&#xff0c;因为第一次接触GNN&#xff0c;很多深奥的概念不懂&…

a柱透明屏好处和挑战详解

a柱透明屏是一种新型的汽车技术&#xff0c;它可以将车辆的a柱部分变得透明&#xff0c;提高驾驶员的视野和安全性。这项技术的出现&#xff0c;将为驾驶员提供更好的驾驶体验和更高的安全性能。 a柱是汽车车身结构中的一部分&#xff0c;位于车辆前部&#xff0c;连接车顶和车…

wangEditor初探

1、前言 现有的Quill比较简单&#xff0c;无法满足业务需求&#xff08;例如SEO的图片属性编辑需求&#xff09; Quill已经有比较长的时间没有更新了&#xff0c;虽然很灵活&#xff0c;但是官方demo都没有一个。 业务前期也没有这块的需求&#xff0c;也没有考虑到这块的扩展…

总结 Android 开发中截取字符串的方法

string str”hello word”;int i5; 1 取字符串的前i个字符 strstr.Substring(0,i); // or strstr.Remove(i,str.Length-i);substring(start,end)&#xff1a;substring是截取2个位置之间及start-end之间的字符串2 去掉字符串的前i个字符&#xff1a; strstr.Remove(0,i); // or…

HTTP vs HTTPS: 网络安全的重要转变

文章目录 一、HTTP的缺点1.1 通信使用明文可能会被窃听1.2 不验证通信方的身份就可能遭遇伪装1.3 无法证明报文完整性&#xff0c;可能已遭篡改 二、 HTTP 加密 认证 完整性保护 HTTPS2.1 HTTPS 是身披 SSL 外壳的 HTTP2.2 HTTPS采用混合加密机制2.3 HTTPS存在的问题 一、HTT…

JavaScript --简介

目录 JS可以用来做什么&#xff1f; JS在前端中几种写法: 1. 文件引用&#xff1a; 2. 页面样式 3. 行内样式 集中常见的弹框: JS基本语法&#xff1a; 变量&#xff1a; 常量&#xff1a; 数据类型&#xff1a; 基本数据类型&#xff1a; 引用数据类型&#xff1a…

解决nginx和gateway网关跨域问题Access to XMLHttpRequest

一、为什么会出现跨域问题&#xff1f; 1、什么是跨域 跨域(Cross-Origin Resource Sharing,简称 CORS) 主要是浏览器的同源策略导致的。 同源策略要求浏览器发出的 AJAX 请求只能发给与请求页面域名相同的 API 服务器,如果发给其他域名就会产生跨域问题。 2、什么是同源策略&…

9.NIO非阻塞式网络通信入门

highlight: arduino-light Selector 示意图和特点说明 一个 I/O 线程可以并发处理 N 个客户端连接和读写操作&#xff0c;这从根本上解决了传统同步阻塞 I/O 一连接一线程模型。架构的性能、弹性伸缩能力和可靠性都得到了极大的提升。 服务端流程 1、当客户端连接服务端时&…