5

Principle & Android

 3 years ago
source link: https://zhuanlan.zhihu.com/p/42504867
Go to the source link to view the article. You can view the picture content, updated content and better typesetting reading experience. If the link is broken, please click the button below to view the snapshot at that time.

Principle & Android

斯拉夫传承者,嘴炮学家

今天裴秀问了我一个问题:Android 里的动画效果一般是怎么开发的?我马上陷入了沉思:其实我们一般都是找个和设计稿差不多的 Interpolator 怼上去,再凭感觉调一下。但作为一个有理想有追求的工程师,我怎么从来没有想过解决这个问题呢(又不是不能用。

于是我研究了一下知乎设计统一使用的动画输出软件 Principle ,发现可以使用 Android SDK 21+ 提供的 PathInterpolatorfacebook/rebound 实现对应的动画曲线 a.k.a 缓动函数

v2-9b251dd0b1871cbfb67eb1675820bc25_720w.jpg
Principle 中提供的六种动画曲线( None 不算

简单介绍一下 PathInterpolator ,它可以让动画效果按照你勾勒的曲线(使用 Path 承载)运行,非常直观。可以看到除 Spring 以外,其余五种曲线其实都是三次贝塞尔曲线,PathInterpolator 的构造方法天然支持贝塞尔曲线的控制点的坐标作为输入:

官方文档:https://goo.gl/bPsMpf

对于 Android 5.0 以下的系统可以使用 support-v4 提供的 PathInterpolatorCompat.

对于 Spring 动画,则简单封装了一个动画类来简化 facebook/rebound 的使用:

/*
 * Copyright 2018 Zhihu Inc.
 *
 * Licensed under the Apache License, Version 2.0 (the "License");
 * you may not use this file except in compliance with the License.
 * You may obtain a copy of the License at
 *
 *     http://www.apache.org/licenses/LICENSE-2.0
 *
 * Unless required by applicable law or agreed to in writing, software
 * distributed under the License is distributed on an "AS IS" BASIS,
 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
 * See the License for the specific language governing permissions and
 * limitations under the License.
 */

package com.zhihu.android.bootstrap.animation;

import android.support.annotation.FloatRange;
import android.support.annotation.NonNull;
import android.support.annotation.Nullable;
import com.facebook.rebound.Spring;
import com.facebook.rebound.SpringConfig;
import com.facebook.rebound.SpringListener;
import com.facebook.rebound.SpringSystem;

/**
 * @author mthli @ Zhihu Inc.
 * @since 12-04-2018
 */
public final class PrincipleSpring implements SpringListener {
    @SuppressWarnings("WeakerAccess")
    public interface PrincipleSpringListener {
        /**
         * @param value Spring 动画开始运行时的起始值
         */
        void onPrincipleSpringStart(float value);

        /**
         * @param value Spring 动画停止运行时的终点值
         */
        void onPrincipleSpringStop(float value);

        /**
         * @param value Spring 动画运行时的计算值
         */
        void onPrincipleSpringUpdate(float value);
    }

    private final Spring mSpring;
    private PrincipleSpringListener mListener;
    private boolean mIsActivate;

    public PrincipleSpring(@FloatRange(from = 0.0F) float tension, @FloatRange(from = 0.0F) float friction) {
        mSpring = SpringSystem.create().createSpring()
                .setSpringConfig(new SpringConfig(tension, friction))
                .addListener(this);
    }

    /**
     * @param listener 设置 Spring 动画的监听器
     * @return 当前 PrincipleSpring 实例,可以用于链式调用
     */
    public PrincipleSpring setListener(@Nullable PrincipleSpringListener listener) {
        mListener = listener;
        return this;
    }

    /**
     * 正向运行 Spring 动画;
     * 一般调用这个方法运行 Spring 动画即可
     */
    public void start() {
        mSpring.setEndValue(1.0F);
    }

    /**
     * 按照曲线倒过来运行 Spring 动画;
     * 如果你 {@link PrincipleSpring#start()} 之后想要倒过来运行的话
     */
    public void reset() {
        mSpring.setEndValue(0.0F);
    }

    /**
     * 取消(并重置)Spring 动画
     */
    public void cancel() {
        mSpring.setCurrentValue(0.0F);
    }

    /**
     * 获取 Spring 动画的 tension 值
     */
    @FloatRange(from = 0.0F)
    public float getTension() {
        return (float) mSpring.getSpringConfig().tension;
    }

    /**
     * @return 获取 Spring 动画 friction 值
     */
    @FloatRange(from = 0.0F)
    public float getFriction() {
        return (float) mSpring.getSpringConfig().friction;
    }

    @Override
    public void onSpringEndStateChange(@NonNull Spring spring) {
        // DO NOTHING
    }

    @Override
    public void onSpringActivate(@NonNull Spring spring) {
        if (!mIsActivate) {
            mIsActivate = true;
            if (mListener != null) {
                mListener.onPrincipleSpringStart((float) spring.getCurrentValue());
            }
        }
    }

    @Override
    public void onSpringAtRest(@NonNull Spring spring) {
        if (mIsActivate) {
            mIsActivate = false;
            if (mListener != null) {
                mListener.onPrincipleSpringStop((float) spring.getCurrentValue());
            }
        }
    }

    @Override
    public void onSpringUpdate(@NonNull Spring spring) {
        if (mListener != null) {
            mListener.onPrincipleSpringUpdate((float) spring.getCurrentValue());
        }
    }
}

综上,我们可以直接使用 Principle 中调好的参数。

(这个问题居然困扰了我两年,淦

万锐:你说这算不算设计驱动开发?

明亮:你说是就是咯:)


About Joyk


Aggregate valuable and interesting links.
Joyk means Joy of geeK