热门标签 | HotTags
当前位置:  开发笔记 > 编程语言 > 正文

Android高级UI开发(二十四)属性动画之大师在流浪、小丑在天堂

昨天刷抖音的时候,被一个在路灯下诵读《战国策》、《尚书》、《左转》的流浪汉感动。今天为了致敬大师,以大师的图片作为素材来演示一下Android中的属性动画。Android的动画分为

昨天刷抖音的时候,被一个在路灯下诵读《战国策》、《尚书》、《左转》的流浪汉感动。

《Android高级UI开发(二十四)属性动画之大师在流浪、小丑在天堂》

今天为了致敬大师,以大师的图片作为素材来演示一下Android中的属性动画。

Android的动画分为补间动画、帧动画、属性动画。(本文中的实例工程下载地址:https://download.csdn.net/download/gaoxiaoweiandy/11033672)

补间动画:透明度、缩放、旋转、平移,通过设置动画的起点、终点、执行时间及插值器来计算某一时间点中相应的值,从而补充这个时间点上的动画,最终实现从起始点到终点的平滑过渡。它直接作用的对象是VIEW视图,并不是VIEW的某一个属性,如translationX属性、translationY属性。

帧动画:播放一组图片,我们小时候都玩过,就是不断翻书,每一页上的小人就会连贯的动起来。

属性动画:通过改变VIEW的属性,如X,Y,alpha,rotate属性来实现平移、透明度、旋转等。而且还可以自定义属性。补间动画与属性动画最大的区别是在平移上,一个按钮执行了补件动画的X平移后,你点击它是不会有反应的,只有点击按钮最开始的那个位置才会响应,这说明补间动画不是真正的平移,而是在目标位置重新绘制了一个该按钮的副本,并非真身。

今天我们来着重介绍一下属性动画。我们定义一个按钮,通过单击按钮来执行一些示例动画。

1. 布局:


xmlns:app="http://schemas.android.com/apk/res-auto"
xmlns:tools="http://schemas.android.com/tools"
android:layout_
android:layout_
tools:cOntext=".MainActivity">
android:id="@+id/btStartAnimate"
android:background="@mipmap/king"
android:layout_centerInParent="true"
android:OnClick="startAnimate"
android:layout_
android:layout_
android:text="start!"
/>

在此我们为Button定义了一个单击响应函数startAnimate,在这个函数里我们将演示一些属性动画的用法。

2. MainActivity.java代码

2.1 演示补间动画的“假移动”

《Android高级UI开发(二十四)属性动画之大师在流浪、小丑在天堂》

package com.example.propertyanimate;
import android.animation.Animator;
import android.animation.AnimatorSet;
import android.animation.ObjectAnimator;
import android.animation.PropertyValuesHolder;
import android.animation.TypeEvaluator;
import android.animation.ValueAnimator;
import android.graphics.PointF;
import android.media.tv.TvContract;
import android.support.v7.app.AppCompatActivity;
import android.os.Bundle;
import android.util.Log;
import android.view.View;
import android.view.animation.AccelerateDecelerateInterpolator;
import android.view.animation.AccelerateInterpolator;
import android.view.animation.Animation;
import android.view.animation.AnimationSet;
import android.view.animation.AnimationUtils;
import android.view.animation.AnticipateInterpolator;
import android.view.animation.BounceInterpolator;
import android.view.animation.CycleInterpolator;
import android.view.animation.OvershootInterpolator;
import android.widget.Button;
import android.widget.Toast;
import java.util.ArrayList;
public class MainActivity extends AppCompatActivity {
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_main);
}
/**
* 动画测试
* @param v: 就是布局里的BUTTON
*/
int position = 0;
public void startAnimate(final View v)
{
//补间动画START:
// 当按钮移动到目标位置时,点击按钮是不起作用的,即不会弹出“start Animate”,
//只有在原来的位置处单击才会响应,这说明它并没有真正的移动,只是在目标位置处又重新绘制了一个按钮。
Toast.makeText(this,"start Animate",Toast.LENGTH_LONG).show();
Animation anim = AnimationUtils.loadAnimation(this,R.anim.translate);
v.startAnimation(anim);
}

在此,我们在startAnimate里先演示了下补件动画,用来说明补件动画的平移是“假平移”。当第一次点击按钮时,按钮移动到了右下角,这时你在右下角点击该按钮时,该按钮没有任何响应(不弹出任何Toast提示),只有点击原位置处的时候才会有提示,这说明它还在原地。上面代码中的R.anim.translate动画XML文件如下:


xmlns:android="http://schemas.android.com/apk/res/android"
android:fromXDelta="0"
android:fromYDelta="0"
android:toXDelta="50%p"
android:toYDelta="50%p"
android:fillAfter="true"
android:duration="500">

2.2 用属性动画来实现平移

《Android高级UI开发(二十四)属性动画之大师在流浪、小丑在天堂》

ObjectAnimator objectAnimator = ObjectAnimator.ofFloat(v,"translationX",0,500);
objectAnimator.setDuration(2000);
objectAnimator.start();

我们可以把这几行代码放在startAnimate函数里,单击按钮来演示按钮水平移动。我们着重看一下ObjectAnimator.ofFloat函数的这几个参数:

v: 表示动画作用在按钮这个view上

translationX:  按钮的属性,表示水平位置。

0,500 这是一组浮点数,可以有多个参数。0表示按钮的起始位置、500表示相对于起始点的距离。

最终执行效果是:按钮从原来的位置水平向右移动500px。当我们第二次点击按钮时会重复执行此动画,但是还是从最初的位置水平右移500px,而不是从第一次执行的末尾位置开始向右移动500px。你可以这样给出这组数值0,100,500,按钮会先移动到100px处,然后再移动到500px处。注意:这里的距离都是相对于按钮最初起始点的相对位移。

除了X轴平移外,我们还可以实现以下与补件动画同样的动画效果:

1. Y轴平移

ObjectAnimator objectAnimatorY = ObjectAnimator.ofFloat(v,"translationY",0,500);
objectAnimatorY.setDuration(3000);
objectAnimatorY.start();

2. Z轴平移

《Android高级UI开发(二十四)属性动画之大师在流浪、小丑在天堂》

ObjectAnimator objectAnimatorZ = ObjectAnimator.ofFloat(v,"translationZ",0,20);
objectAnimatorZ.setDuration(3000);
objectAnimatorZ.start();

3. 旋转

《Android高级UI开发(二十四)属性动画之大师在流浪、小丑在天堂》

ObjectAnimator objectAnimatorRotation = ObjectAnimator.ofFloat(v,"rotationY",0,360);
objectAnimatorRotation.setDuration(5000);
objectAnimatorRotation.start();

绕Y轴旋转,当然你可以改成rotationX,rotationZ.

 

4. 缩放

《Android高级UI开发(二十四)属性动画之大师在流浪、小丑在天堂》

ObjectAnimator objectAnimatorScaleX = ObjectAnimator.ofFloat(v,"scaleX",0,1);
objectAnimatorScaleX.setDuration(5000);
objectAnimatorScaleX.start();

ObjectAnimator objectAnimatorScaleY = ObjectAnimator.ofFloat(v,"scaleY",0,1);
objectAnimatorScaleY.setDuration(5000);
objectAnimatorScaleY.start()

宽、高 缩放:从小变大。

 

2.3 同时执行多个动画

2.3.1 方法1:使用AnimatorSet

《Android高级UI开发(二十四)属性动画之大师在流浪、小丑在天堂》

//X
ObjectAnimator objectAnimatorX = ObjectAnimator.ofFloat(v,"translationX",0,500);
objectAnimatorX.setDuration(500);
//Y
ObjectAnimator objectAnimatorY = ObjectAnimator.ofFloat(v,"translationY",0,500);
objectAnimatorY.setDuration(500);
//将上面2个动画添加到集合里
ArrayList animatorArrayList = new ArrayList<>();
animatorArrayList.add(objectAnimatorX);
animatorArrayList.add(objectAnimatorY);
AnimatorSet animatiOnSet= new AnimatorSet();
//同时执行集合里的多个动画
animationSet.playTogether(animatorArrayList);
animationSet.start();

2.3.2 方法2:监听“属性值”时并行执行动画

ObjectAnimator animator = ObjectAnimator.ofFloat(v,"hello",0,100);
animator.setDuration(2000);
animator.addUpdateListener(new ValueAnimator.AnimatorUpdateListener() {
@Override
public void onAnimationUpdate(ValueAnimator valueAnimator) {
float value = (float)valueAnimator.getAnimatedValue();
//valueAnimator.getAnimatedFraction();实质就是value/100
v.setScaleX((float)(0 + value/100));
v.setScaleY((float)(0 + value/100));
}
});
animator.addListener(new Animator.AnimatorListener() {
@Override
public void onAnimationStart(Animator animator) {
Log.i("AnimatorListener","onAnimationStart");
}
@Override
public void onAnimationEnd(Animator animator) {
Log.i("AnimatorListener","onAnimationEnd");//只能监听到END
}
@Override
public void onAnimationCancel(Animator animator) {
Log.i("AnimatorListener","onAnimationCancel");
}
@Override
public void onAnimationRepeat(Animator animator) {
Log.i("AnimatorListener","onAnimationRepeat");
}
});
animator.start();

这里ObjectAnimator.ofFloat(v,&#8221;hello&#8221;,0,100)与之前的不一样,其中&#8221;hello&#8221;是一个VIEW不存在的属性,这个动画只关心值得变化:从。0到100。 为何ObjectAnimator可以这样用,是因为ObjectAnimator继承于ValueAnimator,而ValueAnimator就可以这样用,只关心值得变化。我们使用为ObjectAnimator对象设置如下监听器就可以监听动画执行情况,可以获取到某一时刻动画执行的进度值:

animator.addUpdateListener(new ValueAnimator.AnimatorUpdateListener() {
@Override
public void onAnimationUpdate(ValueAnimator valueAnimator) {
float value = (float)valueAnimator.getAnimatedValue();
//valueAnimator.getAnimatedFraction();实质就是value/100
v.setScaleX((float)(0 + value/100));
v.setScaleY((float)(0 + value/100));
}
});

在这里我们调用valueAnimator.getAnimatedValue()来获取动画执行过程中某一时间点的值。执行总时长为2000毫秒,由animator.setDuration(2000)决定。比如当动画执行到1秒时,这里的value一般情况应该是50. 我们在这个监听函数里主要并行执行宽、高两个缩放动画:v.setScaleX, v.setScaleY. 缩放的范围是0-1,某一时间点缩放的比例刚好是 (0-100)值动画执行的进度比例,即value/100,也就是valueAnimator.getAnimatedFraction(); 这里的运行效果是:一张图片从无到原始大小的放大效果。

2.3.3 方法3:使用ValueAnimator

原理同方法2一样,因为方法2中的ObjectAnimator继承于ValueAnimator。

//方法3:直接使用ValueAnimator,原理与方法2一样
ValueAnimator valueAnimator = ValueAnimator.ofFloat(0f,100f);
valueAnimator.setDuration(2000);
valueAnimator.addUpdateListener(new ValueAnimator.AnimatorUpdateListener() {
@Override
public void onAnimationUpdate(ValueAnimator valueAnimator) {
float value = (float)valueAnimator.getAnimatedValue();
//valueAnimator.getAnimatedFraction();实质就是value/100
v.setScaleX((float)(0 + value/100));
v.setScaleY((float)(0 + value/100));
}
});
valueAnimator.start();

这次使用的是ObjectAnimator的父类ValueAnimator,与ObjectAnimator的区别是 ofFloat函数不用传递“属性”了,在上一方法2中ObjectAnimator.ofFloat不得不传递一个参数:属性名。同样,在ValueAnimator中添加监听动画值得变化,即0-100在2000毫秒内的变化,原理和过程同方法2,在此不再赘述。

2.3.4 方法4:PropertyValuesHolder

创建多个PropertyValuesHolder,每一个PropertyValuesHolder关联一个动画。最后一起执行多个PropertyValuesHolder。

PropertyValuesHolder propertyValuesHolderScaleX = PropertyValuesHolder.ofFloat("scaleX",1f,0.7f,1f);
PropertyValuesHolder propertyValuesHolderScaleY = PropertyValuesHolder.ofFloat("scaleY",1f,0.7f,1f);
PropertyValuesHolder propertyValuesHolderAlpha = PropertyValuesHolder.ofFloat("alpha",1f,0.7f,1f);
ObjectAnimator objectAnimator = ObjectAnimator.ofPropertyValuesHolder(v,
propertyValuesHolderScaleX,
propertyValuesHolderScaleY,
propertyValuesHolderAlpha);
objectAnimator.setDuration(2000);
objectAnimator.addUpdateListener(new ValueAnimator.AnimatorUpdateListener() {
@Override
public void onAnimationUpdate(ValueAnimator valueAnimator) {
float fraction = valueAnimator.getAnimatedFraction();
float value = (float) valueAnimator.getAnimatedValue();
Log.i("PropertyValuesHolder","fraction="+fraction+",value="+value);
}
});
objectAnimator.start();

代码分析:

Step1.  我们在这里创建了3个PropertyValuesHolder,propertyValuesHolderScaleX,propertyValuesHolderScaleY,propertyValuesHolderAlhap,分别是代表缩放宽、高、透明度。

Step2.  使用ObjectAnimator.ofPropertyValuesHolder函数,以Step1中的3个PropertyValuesHolder为参数创建一个ObjectAnimator对象,这时ObjectAnimator相当于一个AnimateSet。

Step3. objectAnimator.start()启动动画,相当于同时执行了3个动画。

我们发现每一个PropertyValuesHolder动画都有3个值,10.7, 1这几个值叫关键帧。例如缩放:

PropertyValuesHolder.ofFloat(&#8220;scaleX&#8221;,1f,0.7f,1f);   表示图片先从原始大小缩放到原大小的70%,然后再恢复成原大小。我们通过objectAnimator.addUpdateListener(new ValueAnimator.AnimatorUpdateListener()){&#8230;.}函数中打印的日志可以看出:当动画执行到fraction  = 0.5时,即执行了一半时,图片会缩小到最中间的关键帧 0.7,即原大小的70%。也就是说从1到0.7与从0.7到1的缩放动画所经历的时间是相等的,因为它们是关键帧。

2.4 属性动画估值器

《Android高级UI开发(二十四)属性动画之大师在流浪、小丑在天堂》

为什么要用估值器,那我们先来看一下估值器有什么作用。我们发现上面的例子中,大多数属性都是VIEW自带的属性,而且动画执行过程中某一时刻对应的动画值都是由系统计算出来的,代码如下:

objectAnimator.setDuration(2000);
objectAnimator.addUpdateListener(new ValueAnimator.AnimatorUpdateListener() {
@Override
public void onAnimationUpdate(ValueAnimator valueAnimator) {
float fraction = valueAnimator.getAnimatedFraction();
float value = (float) valueAnimator.getAnimatedValue();
Log.i("PropertyValuesHolder","fraction="+fraction+",value="+value);
}
});
objectAnimator.start();

         安卓系统是通过valueAnimator.getAnimatedValue();这个API函数来获取2000毫秒期间某一时间点动画执行到的属性值。计算方法是:根据动画执行的进度比例(时间比例)来计算出当时的属性值。然而这种计算方法我们不能控制和修改。为了我们可以自定义某一时刻属性值的计算方法(算法),我们就得用估值器,利用来自定义值的计算方法。我们先看代码,用代码来讲解估值器如何使用,以下代码实现了一个抛物线动画,就是VIEW沿着抛物线的轨迹移动.

//估值器: 某一时间点的 属性值是 自定义计算出来的。
ValueAnimator valueAnimator = new ValueAnimator();
valueAnimator.setObjectValues(new PointF(0,0));
valueAnimator.setDuration(4000);
//设置一个估值器来计算控件的X坐标与Y坐标
valueAnimator.setEvaluator(new TypeEvaluator() {
@Override
public PointF evaluate(float fraction, PointF start, PointF end) {
PointF pointF = new PointF();
pointF.x = 100f * (fraction * 4);// s = vt; y = 1/2g t*t
pointF.y = 0.5f*100*(fraction*4)*(fraction*4);
Log.i("onAnimationUpdate","x0="+pointF.x+",y0="+pointF.y+",fraction="+fraction);
return pointF;
}
});
valueAnimator.addUpdateListener(new ValueAnimator.AnimatorUpdateListener() {
@Override
public void onAnimationUpdate(ValueAnimator animation) {
PointF pointF = (PointF) animation.getAnimatedValue();
v.setX(pointF.x);
v.setY(pointF.y);
Log.i("onAnimationUpdate","x="+pointF.x+",y="+pointF.y);
}
});
valueAnimator.start();

 第一步:valueAnimator.setObjectValues(new PointF(0,0));  设置一个动画的初始值为(0,0),表示VIEW的起始坐标(X=0,Y=0),在这里我们可以把值的类型设置为Point对象,这完全颠覆了我们之前只能设置float、int等基本类型的习惯。

 第二步:valueAnimator添加一个估值器:

valueAnimator.setEvaluator(new TypeEvaluator()

                         evaluate(float fraction,PonitF start, PointF end){&#8230;.}

                );

我们发现泛型参数是我们自己的 &#8220;属性对象类型&#8221;PointF. 然后重新evaluate函数,在这个函数内我们自己计算出我们的value;

PointF pointF = new PointF();
pointF.x = 100f * (fraction * 4);// s = vt; y = 1/2g t*t
pointF.y = 0.5f*100*(fraction*4)*(fraction*4);
Log.i("onAnimationUpdate","x0="+pointF.x+",y0="+pointF.y+",fraction="+fraction);
return pointF;

在这里我们new了一个PonitF,并计算它的x,y坐标,计算方法就是 x = vt(速度*时间), y = 1/2*g*t*t(g是个常量加速度)。最后将这个PointF当做一个Value返回。到时候我们的值监听器就会在某一时间进度上返回这个PointF,作为animation.getAnimatedValue()的返回值。值监听器的代码如下:

valueAnimator.addUpdateListener(new ValueAnimator.AnimatorUpdateListener() {
@Override
public void onAnimationUpdate(ValueAnimator animation) {
PointF pointF = (PointF) animation.getAnimatedValue();
v.setX(pointF.x);
v.setY(pointF.y);
Log.i("onAnimationUpdate","x="+pointF.x+",y="+pointF.y);
}
});

在动画值更新监听器里通过调用animation.getAnimatedValue()返回某一时刻的值:PointF。然后我们把View的坐标(x,y)设置成PointF中的(x,y). 最终VIEW的运行轨迹就是一条抛物线。

 

2.5  加速器

《Android高级UI开发(二十四)属性动画之大师在流浪、小丑在天堂》

//设置加速器
ObjectAnimator oa = ObjectAnimator.ofFloat(v, "translationY", 0f,300);
oa.setDuration(500);
//设置加速器---
// oa.setInterpolator(new AccelerateInterpolator(5));//加速
// oa.setInterpolator(new AccelerateDecelerateInterpolator());//先加速后减速
// oa.setInterpolator(new AnticipateInterpolator(8)); //
oa.setInterpolator(new OvershootInterpolator());//滑出
// oa.setInterpolator(new CycleInterpolator(4)); //振荡
// oa.setInterpolator(new BounceInterpolator()); //阻尼,回弹
oa.start();

通过setInterpolator函数来为动画设置加速器,比如第一个oa.setInterpolator(new AccelerateInterpolator(5));设置了一个“”速度增加“ 的加速器,动画的运行效果是平移速度越来越快。其他几个“加速器” 大家可以尝试运行一下看看效果。

 


推荐阅读
  • 今天就跟大家聊聊有关怎么在Android应用中实现一个换肤功能,可能很多人都不太了解,为了让大家更加了解,小编给大家总结了以下内容,希望大家根 ... [详细]
  • 在一对一直播源码使用过程中,有时会出现软键盘切换闪屏问题,就是当切换表情的时候屏幕会跳动,因此要对一对一直播源码表情面板无缝切换进行优化。 ... [详细]
  • 2021年最详细的Android屏幕适配方案汇总
    1Android屏幕适配的度量单位和相关概念建议在阅读本文章之前,可以先阅读快乐李同学写的文章《Android屏幕适配的度量单位和相关概念》,这篇文章 ... [详细]
  • 在Android开发中,使用Picasso库可以实现对网络图片的等比例缩放。本文介绍了使用Picasso库进行图片缩放的方法,并提供了具体的代码实现。通过获取图片的宽高,计算目标宽度和高度,并创建新图实现等比例缩放。 ... [详细]
  • 本文介绍了在开发Android新闻App时,搭建本地服务器的步骤。通过使用XAMPP软件,可以一键式搭建起开发环境,包括Apache、MySQL、PHP、PERL。在本地服务器上新建数据库和表,并设置相应的属性。最后,给出了创建new表的SQL语句。这个教程适合初学者参考。 ... [详细]
  • Android开发实现的计时器功能示例
    本文分享了Android开发实现的计时器功能示例,包括效果图、布局和按钮的使用。通过使用Chronometer控件,可以实现计时器功能。该示例适用于Android平台,供开发者参考。 ... [详细]
  • 在Xamarin XAML语言中如何在页面级别构建ControlTemplate控件模板
    本文介绍了在Xamarin XAML语言中如何在页面级别构建ControlTemplate控件模板的方法和步骤,包括将ResourceDictionary添加到页面中以及在ResourceDictionary中实现模板的构建。通过本文的阅读,读者可以了解到在Xamarin XAML语言中构建控件模板的具体操作步骤和语法形式。 ... [详细]
  • 前端开发工程师必读书籍有哪些值得推荐?我们直接进入代码复杂版式设置,如下所示,先写些标签,源码在这个链接里面:https://codepen.io/Shadid ... [详细]
  • android listview OnItemClickListener失效原因
    最近在做listview时发现OnItemClickListener失效的问题,经过查找发现是因为button的原因。不仅listitem中存在button会影响OnItemClickListener事件的失效,还会导致单击后listview每个item的背景改变,使得item中的所有有关焦点的事件都失效。本文给出了一个范例来说明这种情况,并提供了解决方法。 ... [详细]
  • Android Studio Bumblebee | 2021.1.1(大黄蜂版本使用介绍)
    本文介绍了Android Studio Bumblebee | 2021.1.1(大黄蜂版本)的使用方法和相关知识,包括Gradle的介绍、设备管理器的配置、无线调试、新版本问题等内容。同时还提供了更新版本的下载地址和启动页面截图。 ... [详细]
  • XML介绍与使用的概述及标签规则
    本文介绍了XML的基本概念和用途,包括XML的可扩展性和标签的自定义特性。同时还详细解释了XML标签的规则,包括标签的尖括号和合法标识符的组成,标签必须成对出现的原则以及特殊标签的使用方法。通过本文的阅读,读者可以对XML的基本知识有一个全面的了解。 ... [详细]
  • FeatureRequestIsyourfeaturerequestrelatedtoaproblem?Please ... [详细]
  • 本文介绍了Android 7的学习笔记总结,包括最新的移动架构视频、大厂安卓面试真题和项目实战源码讲义。同时还分享了开源的完整内容,并提醒读者在使用FileProvider适配时要注意不同模块的AndroidManfiest.xml中配置的xml文件名必须不同,否则会出现问题。 ... [详细]
  • Go GUIlxn/walk 学习3.菜单栏和工具栏的具体实现
    本文介绍了使用Go语言的GUI库lxn/walk实现菜单栏和工具栏的具体方法,包括消息窗口的产生、文件放置动作响应和提示框的应用。部分代码来自上一篇博客和lxn/walk官方示例。文章提供了学习GUI开发的实际案例和代码示例。 ... [详细]
  • 本文介绍了一款名为TimeSelector的Android日期时间选择器,采用了Material Design风格,可以在Android Studio中通过gradle添加依赖来使用,也可以在Eclipse中下载源码使用。文章详细介绍了TimeSelector的构造方法和参数说明,以及如何使用回调函数来处理选取时间后的操作。同时还提供了示例代码和可选的起始时间和结束时间设置。 ... [详细]
author-avatar
cecillalurw_689
这个家伙很懒,什么也没留下!
PHP1.CN | 中国最专业的PHP中文社区 | DevBox开发工具箱 | json解析格式化 |PHP资讯 | PHP教程 | 数据库技术 | 服务器技术 | 前端开发技术 | PHP框架 | 开发工具 | 在线工具
Copyright © 1998 - 2020 PHP1.CN. All Rights Reserved | 京公网安备 11010802041100号 | 京ICP备19059560号-4 | PHP1.CN 第一PHP社区 版权所有