阅读 1529

Android修炼系列(十六),这些动画方式你都知道吗?

α.自动为布局更新添加动画

Android系统提供了可以在每次布局更改后运行的预加载动画。我们只需要在布局中设置一个属性,就能实现如下的动画效果:

2021-05-09 at 14.48.42.gif

关于addView和removeView的代码就不贴了,核心实现:

    <LinearLayout
        android:layout_width="match_parent"
        android:layout_height="match_parent"
        android:orientation="vertical"
        <!-- 自动预加载动画-->
        android:animateLayoutChanges="true"
        ... />
复制代码

β.使用过渡为布局变化添加动画效果

通过Android的过渡框架,我们只需要提供起始布局和结束布局,过渡框架就能自动帮我们给从起始布局到结束布局的运动添加动画效果,先来看个效果:

2021-05-09 at 17.44.14.gif

Android给我们提供了几种动画类型,具体见下:

WX20210509-200529@2x.png

在两种布局之间添加动画效果的基本流程如下所示:

  1. 为起始布局和结束布局分别创建Scene对象。通常起始布局是根据当前布局自动确定的。
  2. 创建一个Transition对象来定义所需要的动画类型。
  3. 调用TransitionManager.go(),系统会自动运行动画来交换布局。

transitions_diagram.png

好了,上面的效果,核心代码见下,Activity的主布局:

res/layout/anim_layout.xml

<RelativeLayout xmlns:android="http://schemas.android.com/apk/res/android"
    android:layout_width="match_parent"
    android:layout_height="match_parent" >
    ...
    <FrameLayout
        android:layout_width="wrap_content"
        android:layout_height="wrap_content"
        android:id="@+id/scene_root"
        ...>
        <include layout="@layout/starting_scene" />
    </FrameLayout>
    ...
</RelativeLayout>
复制代码

第一个场景的布局定义如下:

res/layout/starting_scene.xml

<RelativeLayout xmlns:android="http://schemas.android.com/apk/res/android"
    android:layout_width="wrap_content"
    android:layout_height="wrap_content"
    android:id="@+id/scene_container">
    <TextView
        android:id="@+id/tv_recommend_reduction"
        android:text="推荐"
        .../>
</RelativeLayout>
复制代码

第二个场景的布局定义如下:

res/layout/ending_scene.xml

<RelativeLayout xmlns:android="http://schemas.android.com/apk/res/android"
    android:layout_width="150dp"
    android:layout_height="72dp"
    android:id="@+id/scene_container">
    <ImageView
        android:id="@+id/tv_recommend_bigger"
        android:layout_width="32dp"
        android:layout_height="32dp"
        android:src="@mipmap/recommend" ... />
    <LinearLayout
        ... >
        <TextView
            android:id="@+id/tv_avoid_jam_bigger"
            android:text="躲避拥堵" ... />

        <TextView
            android:id="@+id/tv_prior_expressway_bigger"
            android:text="高速优先" ... />
    </LinearLayout>
</RelativeLayout>
复制代码

当然,我们除了可以在xml中创建layout以外,同样也可以直接动态代码生成layout,这里就不细说了。之后,我们就可以创建相应的Scene对象了:

    ViewGroup sceneRoot = (ViewGroup) findViewById(R.id.scene_root);
    Scene startScene = Scene.getSceneForLayout(sceneRoot, R.layout.starting_scene, this);
    Scene endingScene = Scene.getSceneForLayout(sceneRoot, R.layout.ending_scene, this);
复制代码

我们再定义场景之间的动画样式,这里就直接使用changeBounds:

    Transition transition = new ChangeBounds();
复制代码

当然,我们也可以在资源文件中指定动画样式,效果是一样的,创建资源xml:

res/transition/change_bounds_transition.xml

在代码中这样引用:

    Transition transition =
                TransitionInflater.from(this).inflateTransition(R.transition.change_bounds_transition);
复制代码

最后开始动画:

    TransitionManager.go(endingScene, transition);
复制代码

γ.矢量可绘制对象

矢量可绘制对象是一种无需像素化或进行模糊处理就能够缩放的可绘制对象。通过AnimatedVectorDrawable类(向后兼容的AnimatedVectorDrawableCompat),我们可以为矢量可绘制对象的属性添加动画效果,例如旋转或更改路径数据来将其变为其他图片。下面来看一个效果:

2021-05-09 at 22.14.50.gif

矢量可绘制对象添加动画效果,需要的3个xml文件:

  • 一个矢量可绘制对象,其中 < vector > 元素位于 res/drawable/
  • 一个添加动画效果之后的矢量可绘制对象,其中 位于 res/drawable/
  • 一个或多个对象Animator,其中< objectAnimator >元素位于 res/animator/

第一个< vector >文件见下,其中< group >元素定义一组路径或子组,而< path >元素定义要绘制的路径(path的获取方式见节末)。而android:name属性,是为了各个组和路径指定唯一的名称,以便我们可以通过Animator定义中引用它们:

res/drawable/vectordrawable.xml

<vector xmlns:android="http://schemas.android.com/apk/res/android"
    android:height="300dp"
    android:width="300dp"
    android:viewportHeight="600"
    android:viewportWidth="600">
    <group
        android:name="rotationGroup" >
        <path
            android:name="v"
            android:fillColor="#7AD6CA"
            android:pathData="m312.93248,211.45199l95,0l0,36l-95,0l0,-36z" />
    </group>
</vector>
复制代码

第二个< animated-vector >文件见下,按照矢量可绘制对象< vector >中的名称进行引用:

res/drawable/animatorvectordrawable.xml

<animated-vector xmlns:android="http://schemas.android.com/apk/res/android"
    android:drawable="@drawable/vectordrawable" >
    <target
        android:name="v"
        android:animation="@animator/path_morph" />
</animated-vector>
复制代码

第三个 < objectAnimator > 文件见下,将矢量可绘制对象的路径从一个形状变为另一个形状,注意这两个路径都必须兼容变形:

res/animator/path_morph.xml

<set xmlns:android="http://schemas.android.com/apk/res/android">
    <objectAnimator
        android:duration="1500"
        android:propertyName="pathData"
        android:valueFrom="m312.93248,211.45199l95,0l0,36l-95,0l0,-36z"
        android:valueTo="m338.93249,188.45199l40,0l0,91l-40,0l0,-91z"
        android:valueType="pathType" />
</set>
复制代码

最后获取对应的AnimatedVectorDrawable对象,并开始动画:

    ImageView imageView = findViewById(R.id.vector_image);
    imageView.setBackgroundResource(R.drawable.animatorvectordrawable);
    //  Android 5.0 (API 21) 及更高版本
    if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.LOLLIPOP) {
        AnimatedVectorDrawable rocketAnimation = (AnimatedVectorDrawable) imageView.getBackground();
        imageView.setOnClickListener((View) -> rocketAnimation.start());
    }
复制代码

对于矢量图的path,我们可以使用在线绘图工具editor.method.ac/ ,绘制完图形后,点击view->Source 查看图形path即可。或者直接使用 shapeshifter.design/ ,编辑完成后直接可以导出Animated Vector Drawable.

δ.裁剪视图动画

通过系统ViewAnimationUtils提供的方法,我们能很容易实现如下的效果:

2021-05-25 at 01.00.34.gif

其使用方法及参数说明见下:

    final View shape = findViewById(R.id.circle);
    Animator circularReveal = ViewAnimationUtils.createCircularReveal(
            shape, // 要想裁剪为圆形的View
            0, 0,  // 相对于视图来说,动画圆的中心的x,y坐标
            0,     // 动画圆开始的半径
                   // 动画圆结束的半径
            (float) Math.hypot(shape.getWidth(), shape.getHeight()));
    circularReveal.setDuration(3000);
    circularReveal.setInterpolator(new AccelerateDecelerateInterpolator());

    // start the animation
    circularReveal.start();
复制代码

这是我们的shape: View 见下:

    <View android:id="@+id/circle"
       android:layout_width="96dp"
       android:layout_height="96dp"
       android:layout_centerInParent="true"/>
复制代码

如想对于动画有个更具体的了解,可查看google developer 官网。下节我会再介绍下Activity间的过渡动画,敬请期待吧,demo 我上传道 github了。

本文到这里就结束了。如果本文对你有用,来点个赞吧,大家的肯定也是道爷的动力。

文章分类
Android
文章标签