感觉流畅和逼真的动画倾向于使用户界面更具吸引力。 难怪Material Design如此重视它们!
但是,如果您曾经尝试创建此类动画,您就会知道Android SDK提供的简单动画师和插值器通常不够好。 因此,Android支持库的最新修订版附带了一个名为Dynamic Animation的物理模块。
使用动态动画,您可以创建基于物理的动画,这些动画与现实世界中对象的运动极为相似。 您还可以使他们实时响应用户的操作。 在本教程中,我将向您展示如何创建一些此类动画。
先决条件
要继续进行,请确保您具有以下条件:
- Android Studio 3.0 Canary 4或更高版本
- 运行Android 4.4或更高版本的设备或模拟器
1.添加依赖项
为了能够在项目中使用动态动画,必须将其作为implementation
依赖项添加到app
模块的build.gradle文件中:
implementation 'com.android.support:support-dynamic-animation:26.0.0-beta2'
在本教程中,我们将为ImageView
小部件设置动画。 当然,它必须显示一些图像,因此打开Vector Assets Studio,并在项目中添加以下“材质”图标:
- 情绪中立
- 情绪非常满意
它们是这样的:

为了获得最佳效果,建议您将图标的大小设置为56 x 56 dp 。
2.创建文件动画
当您在现实世界中猛扑一个物体时,就会赋予它巨大的动力。 由于动量不过是质量和速度的乘积,因此对象最初将具有较高的速度。 但是,由于摩擦,它逐渐减速直到完全停止移动。 使用Dynamic Animation的FlingAnimation
类,可以在应用程序内部模拟此行为。
为了演示起见,让我们现在创建一个包含可拖动ImageView
窗口小部件的布局,显示ic_sentiment_neutral_black_56dp图标,然后用户可以按下Button
窗口小部件以触发该拖动动画。 如果将它们都放置在RelativeLayout
小部件中,则布局XML文件将如下所示:
在上面的代码中,您可以看到Button
小部件具有onClick
属性。 通过单击Android Studio旁边显示的红色灯泡图标,可以在Activity
类内生成关联的点击事件处理程序:
public void flingIt(View view) {
// more code here
}
现在,您可以使用其构造函数创建FlingAnimation
类的新实例,该实例需要一个View
对象和一个可设置动画的属性的名称。 动态动画支持多种可动画设置的属性,例如缩放,平移,旋转和Alpha。
以下代码显示了如何创建一个FlingAnimation
实例,该实例可以使布局的ImageView
的X坐标动画化:
// Get a reference to the view
ImageView emoji = (ImageView)findViewById(R.id.emoji);
// Pass it to the constructor
FlingAnimation flingAnimation
= new FlingAnimation(emoji, DynamicAnimation.X);
默认情况下,将FlingAnimation
实例配置为使用0像素/秒作为其初始速度。 这意味着动画一开始就将停止。 为了模拟现实的猛击,您必须始终记住调用setStartVelocity()
方法并将较大的值传递给该方法。
此外,您必须了解没有摩擦,动画将不会停止。 因此,您还必须调用setFriction()
方法并将少量传递给它。
下面的代码配置FlingAnimation
实例,以使ImageView
不会超出用户屏幕的范围:
flingAnimation.setStartVelocity(500f);
flingAnimation.setFriction(0.5f);
此时,您可以简单地调用start()
方法来启动动画。
flingAnimation.start();
如果您现在运行该应用程序并按按钮,则应该能够看到猛击动画。
值得注意的是,在创建基于物理的动画时,您无需指定持续时间或最终值-当动画意识到其目标对象未在用户屏幕上显示任何可见的运动时,动画将自动停止。
3.模拟弹簧
动态动画使您可以轻松地将弹簧动力学添加到动画中。 换句话说,它可以帮助您创建动画,使小部件以自然的方式弹跳,拉伸和挤压。
为简单起见,让我们现在重新使用布局的ImageView
并对其应用基于spring的动画。 但是,要允许用户启动动画,您需要向布局添加另一个Button
小部件。
要创建基于弹簧的动画,必须使用SpringAnimation
类。 它的构造函数也希望有一个View
对象和一个可设置动画的属性。 以下代码创建一个SpringAnimation
实例,该实例配置为使ImageView
的x坐标动画化:
// Get a reference to the view
final ImageView emoji = (ImageView)findViewById(R.id.emoji);
// Pass it to the constructor
SpringAnimation springAnimation
= new SpringAnimation(emoji, DynamicAnimation.X);
要控制基于弹簧的动画的行为,您需要一个弹簧。 您可以使用SpringForce
类创建一个,该类允许您指定弹簧的静止位置,其阻尼比和刚度。 您可以将阻尼比视为一个常数,就像摩擦一样,它可以使动画变慢直到停止。 另一方面,刚度指定拉伸弹簧需要多少力。
如果听起来有点太复杂了,那么好消息是SpringForce
类提供了几个直观命名的常量,您可以使用它们来快速配置spring。 例如,以下代码创建了一个既有弹性又非常灵活的弹簧:
SpringForce springForce = new SpringForce();
springForce.setFinalPosition(emoji.getX());
springForce.setDampingRatio(SpringForce.DAMPING_RATIO_HIGH_BOUNCY);
springForce.setStiffness(SpringForce.STIFFNESS_LOW);
在上面的代码中,您可以看到我们已经将弹簧的最终静止位置的值设置为ImageView
的初始X坐标。 通过这种配置,您可以想象ImageView
固定在一条不可见的紧橡皮筋上,该橡皮筋在每次移动时都会将ImageView
快速拉回到其原始位置。
现在,您可以使用setSpring()
方法将spring与SpringAnimation
实例相关联。
springAnimation.setSpring(springForce);
最后,在开始动画之前,必须确保使用setStartVelocity()
方法为其赋予较大的初始速度。
springAnimation.setStartVelocity(2000f);
springAnimation.start();
如果您现在运行该应用程序,应该会看到类似以下内容:
4.听动画事件
使用动态动画库创建的动画必须始终从UI线程启动。 您还可以确保一旦调用start()
方法,它将立即start()
。 但是,它异步运行。 因此,如果希望在结束时收到通知,则必须使用addEndListener()
方法将OnAnimationEndListener
对象附加到该对象。
要查看侦听器的运行情况,让我们更改在上一步中基于弹簧的动画每次开始和结束时ImageView
显示的“材质”图标。 我建议您在动画开始时使用ic_sentiment_very_satisfied_black_56dp图标,在动画结束时使用ic_sentiment_neutral_black_56dp图标。 以下代码向您展示了如何:
// Change icon before animation starts
emoji.setImageResource(
R.drawable.ic_sentiment_very_satisfied_black_56dp);
// Start animation
springAnimation.start();
springAnimation.addEndListener(
new DynamicAnimation.OnAnimationEndListener() {
@Override
public void onAnimationEnd(DynamicAnimation animation,
boolean canceled,
float value, float velocity) {
// Change icon after animation ends
emoji.setImageResource(
R.drawable.ic_sentiment_neutral_black_56dp);
}
});
使用上面的代码,动画将如下所示:
5.动画多个属性
FlingAnimation
和SpringAnimation
类的构造函数只能接受一个可设置动画的属性。 如果要同时为多个属性设置动画,则可以创建可能繁琐的类的多个实例,也可以创建一个新的自定义属性来封装所有所需的属性。
要创建自定义的动画属性,必须创建FloatPropertyCompat
类的子类,该类具有两个抽象方法: setValue()
和getValue()
。 您可能已经猜到了,您可以在setValue()
方法中更新所有所需的可设置动画的属性的值。 但是,在getValue()
方法内部,必须仅返回任何一个属性的当前值。 由于此限制,通常必须确保封装属性的值彼此之间不是完全独立的。
例如,下面的代码向您展示如何创建一个名为scale
的自定义属性,该属性可以统一为小部件的SCALE_X
和SCALE_Y
属性设置动画:
FloatPropertyCompat scale =
new FloatPropertyCompat("scale") {
@Override
public float getValue(View view) {
// return the value of any one property
return view.getScaleX();
}
@Override
public void setValue(View view, float value) {
// Apply the same value to two properties
view.setScaleX(value);
view.setScaleY(value);
}
};
现在,自定义属性已准备就绪,您可以像使用任何其他可设置动画的属性一样使用它。 以下代码显示了如何使用它创建SpringAnimation
对象:
SpringAnimation stretchAnimation =
new SpringAnimation(emoji, scale);
创建使用自定义属性的动画时,最好还调用setMinimumVisibleChange()
方法并向其传递有意义的值,以确保动画不会消耗太多CPU周期。 对于缩放小部件的动画,可以使用以下代码:
stretchAnimation.setMinimumVisibleChange(
DynamicAnimation.MIN_VISIBLE_CHANGE_SCALE);
自定义属性动画如下所示:
结论
您现在知道了使用动态动画的基础知识。 使用您在本教程中学到的技术,即使您对牛顿物理学的知识很少,也可以在几分钟内创建令人信服的基于物理学的动画。
翻译自: https://code.tutsplus.com/tutorials/adding-physics-based-animations-to-android-apps--cms-29053