7.7 Android动画特效" class="reference-link">7.7 Android动画特效
通过前面的学习,我们已经基本掌握了如何在Android中使用各种不同的动画来实现各种酷炫的效果。然而在实际项目中,各种优秀的UI设计光靠程序员是远远不够的,一个靠谱的美工也是非常重要的。
下面给大家列举一些动画效果的实例,让大家熟悉如何通过Android的动画框架来创建赏心悦目的动画。
7.7.1 灵动菜单" class="reference-link">7.7.1 灵动菜单
图7.21中展示的是灵动菜单的效果图
图7.21 灵动菜单
当用户点击小红点后,弹出菜单,并带有一个缓冲的过渡动画,这也是Google在Material Design中所强调的动画过渡效果,如图7.22所示。
图7.22 菜单弹出效果
那么这样一个动画效果的菜单是怎么实现的呢?首先,它具有用户交互性,所以肯定不能使用视图动画而必须使用属性动画。其次,只需要针对每个不同的按钮设置不同的动画,并设置相应的差值器就可以实现展开、合拢效果了。理清思路后,实现就比较简单了,这里以展开动画为例,代码如下所示。
- private void startAnim() {
- ObjectAnimator animator0 = ObjectAnimator.ofFloat(
- mImageViews.get(0),
- "alpha",
- 1F,
- 0.5F);
- ObjectAnimator animator1 = ObjectAnimator.ofFloat(
- mImageViews.get(1),
- "translationY",
- 200F);
- ObjectAnimator animator2 = ObjectAnimator.ofFloat(
- mImageViews.get(2),
- "translationX",
- 200F);
- ObjectAnimator animator3 = ObjectAnimator.ofFloat(
- mImageViews.get(3),
- "translationY",
- -200F);
- ObjectAnimator animator4 = ObjectAnimator.ofFloat(
- mImageViews.get(4),
- "translationX",
- -200F);
- AnimatorSet set = new AnimatorSet();
- set.setDuration(500);
- set.setInterpolator(new BounceInterpolator());
- set.playTogether(
- animator0,
- animator1,
- animator2,
- animator3,
- animator4);
- set.start();
- mFlag = false;
- }
下面再为按钮设置点击事件即可完成整个功能。
- @Override
- public void onClick(View v) {
- switch (v.getId()) {
- case R.id.imageView_a:
- if (mFlag) {
- startAnim();
- } else {
- closeAnim();
- }
- break;
- default:
- Toast.makeText(PropertyTest.this, "" + v.getId(),
- Toast.LENGTH_SHORT).show();
- break;
- }
- }
7.7.2 计时器动画" class="reference-link">7.7.2 计时器动画
通过这个实例,我们来熟悉一下ValueAnimator的使用,要实现计时器的动画效果,方法有很多,这里只是为了演示ValueAnimator的效果,因而使用ValueAnimator来实现,程序运行效果如图7.23所示。
图7.23 计时器
当用户点击后,数字会不断增加,如图7.24所示。
图7.24 计时器增加
要完成以上两个效果是比较简单的,只需要借助ValueAnimator来实现数字的不断增加,并将值设置给TextView即可,代码如下所示。
- public void tvTimer(final View view) {
- ValueAnimator valueAnimator = ValueAnimator.ofInt(0, 100);
- valueAnimator.addUpdateListener(
- new ValueAnimator.AnimatorUpdateListener() {
- @Override
- public void onAnimationUpdate(ValueAnimator animation) {
- ((TextView) view).setText("$ " +
- (Integer) animation.getAnimatedValue());
- }
- });
- valueAnimator.setDuration(3000);
- valueAnimator.start();
- }
7.7.3 下拉展开动画" class="reference-link">7.7.3 下拉展开动画
下面再来演示一个ValueAnimator的小例子,这个例子来源于一个群友的问题,他希望能实现一个这样的效果:当点击一个View的时候,显示下面隐藏的一个View,要实现这个功能,需要将View的visibility属性由gone设置为visible即可,但是这个过程是瞬间完成的,如何让View在显示时增加一个动画效果呢?要实现这样的效果,需要让隐藏的View的高度不断发生变化,但不是迅速增大到目标值。所以使用ValueAnimator来模拟这个过程。首先,写一个简单的布局,两个LinearLayout,一个显示,一个隐藏。
- <?xml version="1.0" encoding="utf-8"?>
- <LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
- android:layout_width="match_parent"
- android:layout_height="wrap_content"
- android:orientation="vertical">
- <LinearLayout
- android:layout_width="fill_parent"
- android:layout_height="wrap_content"
- android:gravity="center_vertical"
- android:onClick="llClick"
- android:background="@android:color/holo_blue_bright"
- android:orientation="horizontal">
- <ImageView
- android:id="@+id/app_icon"
- android:layout_width="wrap_content"
- android:layout_height="wrap_content"
- android:layout_gravity="center"
- android:src="@drawable/ic_launcher" />
- <TextView
- android:layout_width="wrap_content"
- android:layout_height="wrap_content"
- android:layout_marginLeft="5dp"
- android:gravity="left"
- android:text="Click Me"
- android:textSize="30sp" />
- </LinearLayout>
- <LinearLayout
- android:id="@+id/hidden_view"
- android:layout_width="match_parent"
- android:layout_height="40dp"
- android:background="@android:color/holo_orange_light"
- android:gravity="center_vertical"
- android:orientation="horizontal"
- android:visibility="gone">
- <ImageView
- android:src="@drawable/ic_launcher"
- android:layout_width="wrap_content"
- android:layout_height="wrap_content"
- android:layout_gravity="center" />
- <TextView
- android:id="@+id/tv_hidden"
- android:layout_width="wrap_content"
- android:layout_height="match_parent"
- android:gravity="center"
- android:textSize="20sp"
- android:text="I am hidden" />
- </LinearLayout>
- </LinearLayout>
为了区分两个不同的LinearLayout,我们给它们设置了不同的背景颜色和显示文字。接下来,当点击上面的LinearLayout时,需要获取到隐藏的LinearLayout最终需要到达的一个高度,即我们的目标值,通过将布局文件中的dp值转化为像素值即可。
- mDensity = getResources().getDisplayMetrics().density;
- mHiddenViewMeasuredHeight = (int) (mDensity * 40 + 0.5);
40就是在XML文件中定义的布局高度。
然后给这个过程增加一个动画效果,前面分析了,需要使用ValueAnimator来创建一个从0到目标值的数值发生器,并由此来改变View的布局属性。
- ValueAnimator animator = ValueAnimator.ofInt(start, end);
- animator.addUpdateListener(new ValueAnimator.AnimatorUpdateListener() {
- @Override
- public void onAnimationUpdate(ValueAnimator valueAnimator) {
- int value = (Integer) valueAnimator.getAnimatedValue();
- ViewGroup.LayoutParams layoutParams = view.getLayoutParams();
- layoutParams.height = value;
- view.setLayoutParams(layoutParams);
- }
- });
通过这样一个简单的ValueAnimator,就可以非常方便地实现显示、隐藏的动画效果了,完整代码如下所示。
- package com.imooc.anim;
- import android.animation.Animator;
- import android.animation.AnimatorListenerAdapter;
- import android.animation.ValueAnimator;
- import android.app.Activity;
- import android.os.Bundle;
- import android.view.View;
- import android.view.ViewGroup;
- import android.widget.LinearLayout;
- public class DropTest extends Activity {
- private LinearLayout mHiddenView;
- private float mDensity;
- private int mHiddenViewMeasuredHeight;
- @Override
- protected void onCreate(Bundle savedInstanceState) {
- super.onCreate(savedInstanceState);
- setContentView(R.layout.drop);
- mHiddenView = (LinearLayout) findViewById(R.id.hidden_view);
- //获取像素密度
- mDensity = getResources().getDisplayMetrics().density;
- //获取布局的高度
- mHiddenViewMeasuredHeight = (int) (mDensity * 40 + 0.5);
- }
- public void llClick(View view) {
- if (mHiddenView.getVisibility() == View.GONE) {
- //打开动画
- animateOpen(mHiddenView);
- } else {
- //关闭动画
- animateClose(mHiddenView);
- }
- }
- private void animateOpen(final View view) {
- view.setVisibility(View.VISIBLE);
- ValueAnimator animator = createDropAnimator(
- view,
- 0,
- mHiddenViewMeasuredHeight);
- animator.start();
- }
- private void animateClose(final View view) {
- int origHeight = view.getHeight();
- ValueAnimator animator = createDropAnimator(view, origHeight, 0);
- animator.addListener(new AnimatorListenerAdapter() {
- public void onAnimationEnd(Animator animation) {
- view.setVisibility(View.GONE);
- }
- });
- animator.start();
- }
- private ValueAnimator createDropAnimator(
- final View view, int start, int end) {
- ValueAnimator animator = ValueAnimator.ofInt(start, end);
- animator.addUpdateListener(
- new ValueAnimator.AnimatorUpdateListener() {
- @Override
- public void onAnimationUpdate(ValueAnimator valueAnimator) {
- int value = (Integer) valueAnimator.getAnimatedValue();
- ViewGroup.LayoutParams layoutParams =
- view.getLayoutParams();
- layoutParams.height = value;
- view.setLayoutParams(layoutParams);
- }
- });
- return animator;
- }
- }
程序运行效果如图7.25所示。
图7.25 程序运行初始状态
当点击View时,会逐渐弹出下面隐藏的View,如图7.26所示。
图7.26 点击显示隐藏View
