SpringAnimation弹簧动画

Android最近更新了Support Library包,在25.3中新增了一个动画效果,名为SpringAnimation(弹簧动画)。

使用步骤一:

需要25的编译环境,同时要求最低版本为16及以上,所以使用上还是有一些限制。如果有特殊需要,可以通过tools:overrideLibrary来做对应的兼容适配。

build.xml:minSdkVersion 16compileSdkVersion 25compile 'com.android.support:support-dynamic-animation:25.3.0'

导入之后就能看到对应的动画类SpringAnimation

android.support.animation.SpringAnimation

使用步骤二:

SpringAnimation提供了两个构造方法:

public SpringAnimation(View v, ViewProperty property)public SpringAnimation(View v, ViewProperty property, float finalPosition)

分别是操作对应的View,对应的变化属性及最终的位置。

ViewProperty从现有支持的看,包括(Z轴的支持需要API >= 21):

TRANSLATION_XTRANSLATION_YTRANSLATION_ZSCALE_XSCALE_YROTATIONROTATION_XROTATION_YXYZALPHASCROLL_XSCROLL_Y

然后在SpringAnimation中有一个SpringForce对象,负责对应的变量设置及位置计算。其中包括两个个关键变量

  1. Stiffness刚度(劲度/弹性),刚度越大,形变产生的里也就越大,体现在效果上就是运动越快
  2. DampingRatio阻尼系数,系数越大,动画停止的越快。从理论上讲分为三种情况 Overdamped过阻尼(ζ > 1)、Critically damped临界阻尼(ζ = 1)、Underdamped欠阻尼状态(0<ζ <1)。

不过估计看到这,大家肯定还是一脸懵逼。所以,这里先稍微解释一下弹簧跟阻尼的概念:

  1. 从原理上看,当弹簧处于平衡位置(不受力的自然位置)时,如果受到挤压或者拉伸后,当放手后弹簧会恢复原状。
  2. 弹回来的过程是「弹性势能」转化成「动能」,当重新恢复到平衡点时,因为还有速度,所以会继续向前运动,这个时候「动能」又对应的转化为「弹性势能」。
  3. 如果能量转换没有损失,将不断的伸长缩短,也就是不断的来来回回。
  4. 但能量转换是有损失的,所以缩回来的距离会不断缩小,弹出去的距离也一样,这个每次损耗缩小的距离比,其实就是阻尼。

这样解释后,大家对于关键的参数应该有了一定的了解。那接下来我们就继续实战。

使用步骤三:

尝试模仿Dribbble上的一个作品,设计效果图如下:

分析整个动画效果,主体上分为两个部分:

  1. 各元素从下往上移动的弹簧效果;SpringAnimation可以实现对应的效果。
  2. 移动过程中的透明度变化;ValueAnimation用来实现透明度Alpha从0到1的变化,从而实现淡入的感觉。

然后简单的说明下SpringAnimation的使用方法:

SpringAnimation signUpBtnAnimY = new SpringAnimation(mSignUpBtn,SpringAnimation.TRANSLATION_Y,0);        signUpBtnAnimY.getSpring().setStiffness(SpringForce.STIFFNESS_VERY_LOW);signUpBtnAnimY.getSpring().setDampingRatio(SpringForce.DAMPING_RATIO_LOW_BOUNCY);signUpBtnAnimY.setStartVelocity(10000);signUpBtnAnimY.start();

之前介绍SpringAnimation的时候聊过了几个影响实际效果的因素,这里再根据具体使用来说明下:
(1) DampingRatio阻尼系数,通过getSpring().setDampingRatio方法来设置。
从效果上看,ζ = 0的时候就是无限来回运动,0< ζ <1的时候会出现来回减弱的振荡最后停止,ζ >= 1的时候会在靠近原位置的时候提前减速后停止。
在SpringForce中有对应的常量设置:

/*** Damping ratio for a very bouncy spring. Note for under-damped springs* (i.e. damping ratio < 1), the lower the damping ratio, the more bouncy the spring.*/public static final float DAMPING_RATIO_HIGH_BOUNCY = 0.2f;/*** Damping ratio for a medium bouncy spring. This is also the default damping ratio for spring* force. Note for under-damped springs (i.e. damping ratio < 1), the lower the damping ratio,* the more bouncy the spring.*/public static final float DAMPING_RATIO_MEDIUM_BOUNCY = 0.5f;/*** Damping ratio for a spring with low bounciness. Note for under-damped springs* (i.e. damping ratio < 1), the lower the damping ratio, the higher the bounciness.*/public static final float DAMPING_RATIO_LOW_BOUNCY = 0.75f;/*** Damping ratio for a spring with no bounciness. This damping ratio will create a critically* damped spring that returns to equilibrium within the shortest amount of time without* oscillating.*/public static final float DAMPING_RATIO_NO_BOUNCY = 1f;

(2) Stiffness刚度,通过getSpring().setStiffness方法来设置。
在SpringForce中有对应的常量设置:

/*** Stiffness constant for extremely stiff spring.*/public static final float STIFFNESS_HIGH = 10_000f;/*** Stiffness constant for medium stiff spring. This is the default stiffness for spring force.*/public static final float STIFFNESS_MEDIUM = 1500f;    /*** Stiffness constant for a spring with low stiffness.*/public static final float STIFFNESS_LOW = 200f;/*** Stiffness constant for a spring with very low stiffness.*/public static final float STIFFNESS_VERY_LOW = 50f;

(3) StartVelocity开始速度,单位是px/second. 正数是弹簧收缩的方向,负数则相反。

根据效果要求设置完对应的参数后,调用start方法后即可执行对应的动画。
需要注意的是SpringAnimation动画是无法设置执行时间的,所以如果有同期需要执行的动画,可以评估对应的执行时间或者通过updateListener来做对应的处理。

简略的实现

public class MainActivity extends AppCompatActivity {    private Button mSignUpBtn;    private ImageView mLeftLogoImg;    private ImageView mRightLogoImg;    private TextView mDescTitleTextView;    private LinearLayout mLettersLayout;    private LinearLayout mSignInLayout;    private ArrayList mLetterAnims;    @Override    protected void onCreate(Bundle savedInstanceState) {        super.onCreate(savedInstanceState);        // hide the status ui.        requestWindowFeature(Window.FEATURE_NO_TITLE);        getWindow().setFlags(WindowManager.LayoutParams.FLAG_FULLSCREEN,                WindowManager.LayoutParams.FLAG_FULLSCREEN);        setContentView(R.layout.activity_main);        // get the screen height.        DisplayMetrics dm = getResources().getDisplayMetrics();        int screenHeight = dm.heightPixels;        // letters about 'Converse'        mLettersLayout = (LinearLayout) findViewById(R.id.letter_layout);        mLetterAnims = new ArrayList<>();        for (int i = 0; i < mLettersLayout.getChildCount(); i++) {            View letterView = mLettersLayout.getChildAt(i);            letterView.setTranslationY(screenHeight);            SpringAnimation letterAnimY = new SpringAnimation(letterView, SpringAnimation.TRANSLATION_Y, 0);            letterAnimY.getSpring().setStiffness(SpringForce.STIFFNESS_VERY_LOW);            letterAnimY.getSpring().setDampingRatio(SpringForce.DAMPING_RATIO_LOW_BOUNCY);            mLetterAnims.add(letterAnimY);        }                // text about 'Native messaging'        mDescTitleTextView = (TextView) findViewById(R.id.desc_title_textview);        mDescTitleTextView.setTranslationY(500f);        mDescTitleTextView.setAlpha(0f);        final SpringAnimation descTitleAnimY = new SpringAnimation(mDescTitleTextView, DynamicAnimation.TRANSLATION_Y, 0);        descTitleAnimY.getSpring().setStiffness(SpringForce.STIFFNESS_VERY_LOW);        descTitleAnimY.getSpring().setDampingRatio(SpringForce.DAMPING_RATIO_LOW_BOUNCY);                final ValueAnimator descTitleAlphaAnim = ObjectAnimator.ofFloat(0f, 1f);        descTitleAlphaAnim.setDuration(300);        descTitleAlphaAnim.addUpdateListener(new ValueAnimator.AnimatorUpdateListener() {            @Override            public void onAnimationUpdate(ValueAnimator valueAnimator) {                mDescTitleTextView.setAlpha((Float) valueAnimator.getAnimatedValue());            }        });                // the button of sign up        mSignUpBtn = (Button) findViewById(R.id.sign_up_btn);        mSignUpBtn.setTranslationY(500f);        final SpringAnimation signUpBtnAnimY = new SpringAnimation(mSignUpBtn, SpringAnimation.TRANSLATION_Y, 0);        signUpBtnAnimY.getSpring().setStiffness(SpringForce.STIFFNESS_VERY_LOW);        signUpBtnAnimY.getSpring().setDampingRatio(SpringForce.DAMPING_RATIO_LOW_BOUNCY);        // the bottom text about 'Have an account? sign in'        mSignInLayout = (LinearLayout) findViewById(R.id.signin_layout);        mSignInLayout.setTranslationY(500f);        final SpringAnimation signInLayoutAnimY = new SpringAnimation(mSignInLayout, SpringAnimation.TRANSLATION_Y, 0);        signInLayoutAnimY.getSpring().setStiffness(SpringForce.STIFFNESS_VERY_LOW);        signInLayoutAnimY.getSpring().setDampingRatio(SpringForce.DAMPING_RATIO_LOW_BOUNCY);        // top logo by left        mLeftLogoImg = (ImageView) findViewById(R.id.left_logo_imageview);        mLeftLogoImg.setTranslationY(400f);        mLeftLogoImg.setAlpha(0f);        final SpringAnimation leftLogoAnimY = new SpringAnimation(mLeftLogoImg, SpringAnimation.TRANSLATION_Y, 0);        leftLogoAnimY.getSpring().setStiffness(SpringForce.STIFFNESS_VERY_LOW);        leftLogoAnimY.getSpring().setDampingRatio(SpringForce.DAMPING_RATIO_LOW_BOUNCY);        leftLogoAnimY.setStartVelocity(-2000);        // top logo by right        mRightLogoImg = (ImageView) findViewById(R.id.right_logo_imageview);        mRightLogoImg.setTranslationY(400f);        mRightLogoImg.setAlpha(0f);        final SpringAnimation rightLogoAnimY = new SpringAnimation(mRightLogoImg, SpringAnimation.TRANSLATION_Y, 0);        rightLogoAnimY.getSpring().setStiffness(SpringForce.STIFFNESS_VERY_LOW);        rightLogoAnimY.getSpring().setDampingRatio(SpringForce.DAMPING_RATIO_LOW_BOUNCY);        rightLogoAnimY.setStartVelocity(-2000);                final ValueAnimator logoAlphaAnim = ObjectAnimator.ofFloat(0f, 1f);        logoAlphaAnim.setDuration(600);        logoAlphaAnim.addUpdateListener(new ValueAnimator.AnimatorUpdateListener() {            @Override            public void onAnimationUpdate(ValueAnimator valueAnimator) {                mLeftLogoImg.setAlpha((Float) valueAnimator.getAnimatedValue());                mRightLogoImg.setAlpha((Float) valueAnimator.getAnimatedValue());            }        });        mRightLogoImg.postDelayed(new Runnable() {            @Override            public void run() {                leftLogoAnimY.start();                mRightLogoImg.postDelayed(new Runnable() {                    @Override                    public void run() {                        rightLogoAnimY.start();                    }                }, 150);                mDescTitleTextView.postDelayed(new Runnable() {                    @Override                    public void run() {                        descTitleAlphaAnim.setStartDelay(100);                        descTitleAlphaAnim.start();                        descTitleAnimY.start();                        signUpBtnAnimY.start();                        signInLayoutAnimY.start();                    }                }, 300);                for (final SpringAnimation letterAnim : mLetterAnims) {                    mLettersLayout.postDelayed(new Runnable() {                        @Override                        public void run() {                            letterAnim.start();                        }                    }, 12 * mLetterAnims.indexOf(letterAnim));                }                logoAlphaAnim.start();            }        }, 1000);    }}

最终实现的效果如下图,实现的有点粗糙,希望能给大家带来解决需求上的新思路。


原文链接: Android中弹簧动画的那些事 - SpringAnimation

更多相关文章

  1. Android(安卓)AnimationDrawable运行的几种方式(转)
  2. Android开发从初级到高级学习路线
  3. android 自定义Toast增加点击事件、Toast弹出隐藏动画、Toast宽
  4. Animation用法_animation动画效果
  5. [置顶] 【Android】 给我一个Path,还你一个酷炫动画
  6. Android:使用ViewFlipper实现上下滚动消息
  7. Android(安卓)自定义 MarqueeView 实现跑马灯 —— 原理篇
  8. Android4.0.3修改启动动画和开机声音
  9. Android系统移植与调试之------->如何修改Android启动动画和开机

随机推荐

  1. View的xml的属性作用大剖析
  2. TextView和EidtText使用技巧
  3. 《Android 应用 之路》 MPAndroidChart~B
  4. Android在全球的市场份额雄起
  5. Android ActionBar 作为导航条的一个Bug
  6. android TabHost小结
  7. Android EditText 限制输入数字和字母设
  8. android selector 背景选择器
  9. Android系列教程之十:Intents and Intent
  10. Dagger2使用