`
从此醉
  • 浏览: 1089797 次
  • 性别: Icon_minigender_1
  • 来自: US
社区版块
存档分类
最新评论

Android动画进阶—使用开源动画库nineoldandroids

 
阅读更多
转载请注明出处:http://blog.csdn.net/singwhatiwanna/article/details/17639987

前言

Android系统支持原生动画,这为应用开发者开发绚丽的界面提供了极大的方便,有时候动画是很必要的,当你想做一个滑动的特效的时候,如果苦思冥想都搞不定,那么你可以考虑下动画,说不定动画轻易就搞定了。下面再简单回顾下Android中的动画,本文后面会介绍一个稍微复杂点的动画,先上效果图


动画分类

View动画:也叫渐变动画,针对View的动画,主要支持平移、旋转、缩放、透明度

Drawable动画:也叫帧动画,主要是设置View的背景,可以以动画的形式为View设置多张背景

对象属性动画(Android3.0新加入):可以对对象的属性进行动画而不仅仅是View,动画默认时间间隔300ms,默认帧率10ms/帧。其可以达到的效果是:在一个时间间隔内完成对象从一个属性值到另一个属性值的改变,因此,属性动画几乎是无所不能的,只要对象有这个属性,它都能实现动画效果,但是属性动画从Android3.0才有,这就严重制约了属性动画的使用,这就是开源动画库nineoldandroids的作用,采用nineoldandroids,可以在3.0以前的系统上使用属性动画,nineoldandroids的网址是:http://nineoldandroids.com。说到属性动画,就不得不提到插值器(TimeInterpolator)和估值算法(TypeEvaluator),下面介绍。

TimeInterpolator和TypeEvaluator

TimeInterpolator中文翻译为时间插值器,它的作用是根据时间流逝的百分比来计算出当前属性值改变的百分比,系统预置的有LinearInterpolator(线性插值器:匀速动画)、AccelerateDecelerateInterpolator(加速减速插值器:动画两头慢中间快)和DecelerateInterpolator(减速插值器:动画越来越慢)等;TypeEvaluator的中文翻译为类型估值算法,它的作用是根据当前属性改变的百分比来计算改变后的属性值,系统预置的有IntEvaluator(针对整型属性)、FloatEvaluator(针对浮点型属性)和ArgbEvaluator(针对Color属性)。可能这么说还有点晦涩,没关系,下面给出一个实例就很好理解了。


看上述动画,很显然上述动画是一个匀速动画,其采用了线性插值器和整型估值算法,在40ms内,View的x属性实现从0到40的变换,由于动画的默认刷新率为10ms/帧,所以该动画将分5帧进行,我们来考虑第三帧(x=20 t=20ms),当时间t=20ms的时候,时间流逝的百分比是0.5 (20/40=0.5),意味这现在时间过了一半,那x应该改变多少呢,这个就由插值器和估值算法来确定。拿线性插值器来说,当时间流逝一半的时候,x的变换也应该是一半,即x的改变是0.5,为什么呢?因为它是线性插值器,是实现匀速动画的,下面看它的源码:

public class LinearInterpolator implements Interpolator {

    public LinearInterpolator() {
    }
    
    public LinearInterpolator(Context context, AttributeSet attrs) {
    }
    
    public float getInterpolation(float input) {
        return input;
    }
}

很显然,线性插值器的返回值和输入值一样,因此插值器返回的值是0.5,这意味着x的改变是0.5,这个时候插值器的工作就完成了。

具体x变成了什么值,这个需要估值算法来确定,我们来看看整型估值算法的源码:

public class IntEvaluator implements TypeEvaluator<Integer> {

    public Integer evaluate(float fraction, Integer startValue, Integer endValue) {
        int startInt = startValue;
        return (int)(startInt + fraction * (endValue - startInt));
    }
}

上述算法很简单,evaluate的三个参数分别表示:估值小数、开始值和结束值,对应于我们的例子就分别是:0.5,0,40。根据上述算法,整型估值返回给我们的结果是20,这就是(x=20 t=20ms)的由来。

说明:属性动画要求该属性有set方法和get方法(可选);插值器和估值算法除了系统提供的外,我们还可以自定义,实现方式也很简单,因为插值器和估值算法都是一个接口,且内部都只有一个方法,我们只要派生一个类实现接口就可以了,然后你就可以做出千奇百怪的动画效果。具体一点就是:自定义插值器需要实现Interpolator或者TimeInterpolator,自定义估值算法需要实现TypeEvaluator。还有就是如果你对其他类型(非int、float、color)做动画,你必须要自定义类型估值算法。

nineoldandroids介绍

其功能和android.animation.*中的类的功能完全一致,使用方法完全一样,只要我们用nineoldandroids来编写动画,就可以在所有的Android系统上运行。比较常用的几个动画类是:ValueAnimator、ObjectAnimator和AnimatorSet,其中ObjectAnimator继承自ValueAnimator,AnimatorSet是动画集,可以定义一组动画。使用起来也是及其简单的,下面举几个小栗子。

栗子1:改变一个对象(myObject)的 translationY属性,让其沿着Y轴向上平移一段距离:它的高度,该动画在默认时间内完成,动画的完成时间是可以定义的,想要更灵活的效果我们还可以定义插值器和估值算法,但是一般来说我们不需要自定义,系统已经预置了一些,能够满足常用的动画。

ObjectAnimator.ofFloat(myObject, "translationY", -myObject.getHeight()).start();

栗子2:改变一个对象的背景色属性,典型的情形是改变View的背景色,下面的动画可以让背景色在3秒内实现从0xFFFF8080到0xFF8080FF的渐变,并且动画会无限循环而且会有反转的效果

ValueAnimator colorAnim = ObjectAnimator.ofInt(this, "backgroundColor", /*Red*/0xFFFF8080, /*Blue*/0xFF8080FF);
colorAnim.setDuration(3000);
colorAnim.setEvaluator(new ArgbEvaluator());
colorAnim.setRepeatCount(ValueAnimator.INFINITE);
colorAnim.setRepeatMode(ValueAnimator.REVERSE);
colorAnim.start();

栗子3:动画集合,5秒内对View的旋转、平移、缩放和透明度都进行了改变

AnimatorSet set = new AnimatorSet();
set.playTogether(
    ObjectAnimator.ofFloat(myView, "rotationX", 0, 360),
    ObjectAnimator.ofFloat(myView, "rotationY", 0, 180),
    ObjectAnimator.ofFloat(myView, "rotation", 0, -90),
    ObjectAnimator.ofFloat(myView, "translationX", 0, 90),
    ObjectAnimator.ofFloat(myView, "translationY", 0, 90),
    ObjectAnimator.ofFloat(myView, "scaleX", 1, 1.5f),
    ObjectAnimator.ofFloat(myView, "scaleY", 1, 0.5f),
    ObjectAnimator.ofFloat(myView, "alpha", 1, 0.25f, 1)
);
set.setDuration(5 * 1000).start();

栗子4:下面是个简单的调用方式,其animate方法是nineoldandroids特有的

Button myButton = (Button)findViewById(R.id.myButton);

//Note: in order to use the ViewPropertyAnimator like this add the following import:
//  import static com.nineoldandroids.view.ViewPropertyAnimator.animate;
animate(myButton).setDuration(2000).rotationYBy(720).x(100).y(100);

栗子5:一个采用nineoldandroids实现的稍微复杂点的动画

布局xml如下:

<?xml version="1.0" encoding="utf-8"?>
<FrameLayout xmlns:android="http://schemas.android.com/apk/res/android"
    android:layout_width="match_parent"
    android:layout_height="match_parent" >

    <Button
        android:id="@+id/menu"
        style="@style/MenuStyle"
        android:background="@drawable/menu" />

    <Button
        android:id="@+id/item1"
        style="@style/MenuItemStyle"
        android:background="@drawable/circle1"
        android:visibility="gone" />

    <Button
        android:id="@+id/item2"
        style="@style/MenuItemStyle"
        android:background="@drawable/circle2"
        android:visibility="gone" />

    <Button
        android:id="@+id/item3"
        style="@style/MenuItemStyle"
        android:background="@drawable/circle3"
        android:visibility="gone" />

    <Button
        android:id="@+id/item4"
        style="@style/MenuItemStyle"
        android:background="@drawable/circle4"
        android:visibility="gone" />

    <Button
        android:id="@+id/item5"
        style="@style/MenuItemStyle"
        android:background="@drawable/circle5"
        android:visibility="gone" />
</FrameLayout>

代码如下:

public class MainActivity extends Activity implements OnClickListener {

    private static final String TAG = "MainActivity";

    private Button mMenuButton;
    private Button mItemButton1;
    private Button mItemButton2;
    private Button mItemButton3;
    private Button mItemButton4;
    private Button mItemButton5;

    private boolean mIsMenuOpen = false;

    @Override
    protected void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        setContentView(R.layout.activity_main);

        initView();
    }

    @TargetApi(Build.VERSION_CODES.HONEYCOMB)
    private void initView() {
        mMenuButton = (Button) findViewById(R.id.menu);
        mMenuButton.setOnClickListener(this);

        mItemButton1 = (Button) findViewById(R.id.item1);
        mItemButton1.setOnClickListener(this);

        mItemButton2 = (Button) findViewById(R.id.item2);
        mItemButton2.setOnClickListener(this);

        mItemButton3 = (Button) findViewById(R.id.item3);
        mItemButton3.setOnClickListener(this);

        mItemButton4 = (Button) findViewById(R.id.item4);
        mItemButton4.setOnClickListener(this);

        mItemButton5 = (Button) findViewById(R.id.item5);
        mItemButton5.setOnClickListener(this);
    }

    @Override
    public boolean onCreateOptionsMenu(Menu menu) {
        mMenuButton.performClick();
        getMenuInflater().inflate(R.menu.main, menu);
        return false;
    }

    @Override
    public void onClick(View v) {
        if (v == mMenuButton) {
            if (!mIsMenuOpen) {
                mIsMenuOpen = true;
                doAnimateOpen(mItemButton1, 0, 5, 300);
                doAnimateOpen(mItemButton2, 1, 5, 300);
                doAnimateOpen(mItemButton3, 2, 5, 300);
                doAnimateOpen(mItemButton4, 3, 5, 300);
                doAnimateOpen(mItemButton5, 4, 5, 300);
            } else {
                mIsMenuOpen = false;
                doAnimateClose(mItemButton1, 0, 5, 300);
                doAnimateClose(mItemButton2, 1, 5, 300);
                doAnimateClose(mItemButton3, 2, 5, 300);
                doAnimateClose(mItemButton4, 3, 5, 300);
                doAnimateClose(mItemButton5, 4, 5, 300);
            }
        } else {
            Toast.makeText(this, "你点击了" + v, Toast.LENGTH_SHORT).show();
        }

    }

    /**
     * 打开菜单的动画
     * @param view 执行动画的view
     * @param index view在动画序列中的顺序
     * @param total 动画序列的个数
     * @param radius 动画半径
     */
    private void doAnimateOpen(View view, int index, int total, int radius) {
        if (view.getVisibility() != View.VISIBLE) {
            view.setVisibility(View.VISIBLE);
        }
        double degree = Math.PI * index / ((total - 1) * 2);
        int translationX = (int) (radius * Math.cos(degree));
        int translationY = (int) (radius * Math.sin(degree));
        Log.d(TAG, String.format("degree=%f, translationX=%d, translationY=%d",
                degree, translationX, translationY));
        AnimatorSet set = new AnimatorSet();
        //包含平移、缩放和透明度动画
        set.playTogether(
                ObjectAnimator.ofFloat(view, "translationX", 0, translationX),
                ObjectAnimator.ofFloat(view, "translationY", 0, translationY),
                ObjectAnimator.ofFloat(view, "scaleX", 0f, 1f),
                ObjectAnimator.ofFloat(view, "scaleY", 0f, 1f),
                ObjectAnimator.ofFloat(view, "alpha", 0f, 1));
        //动画周期为500ms
        set.setDuration(1 * 500).start();
    }

    /**
     * 关闭菜单的动画
     * @param view 执行动画的view
     * @param index view在动画序列中的顺序
     * @param total 动画序列的个数
     * @param radius 动画半径
     */
    private void doAnimateClose(final View view, int index, int total,
            int radius) {
        if (view.getVisibility() != View.VISIBLE) {
            view.setVisibility(View.VISIBLE);
        }
        double degree = Math.PI * index / ((total - 1) * 2);
        int translationX = (int) (radius * Math.cos(degree));
        int translationY = (int) (radius * Math.sin(degree));
        Log.d(TAG, String.format("degree=%f, translationX=%d, translationY=%d",
                degree, translationX, translationY));
        AnimatorSet set = new AnimatorSet();
      //包含平移、缩放和透明度动画
        set.playTogether(
                ObjectAnimator.ofFloat(view, "translationX", translationX, 0),
                ObjectAnimator.ofFloat(view, "translationY", translationY, 0),
                ObjectAnimator.ofFloat(view, "scaleX", 1f, 0f),
                ObjectAnimator.ofFloat(view, "scaleY", 1f, 0f),
                ObjectAnimator.ofFloat(view, "alpha", 1f, 0f));
        //为动画加上事件监听,当动画结束的时候,我们把当前view隐藏
        set.addListener(new AnimatorListener() {
            @Override
            public void onAnimationStart(Animator animator) {
            }

            @Override
            public void onAnimationRepeat(Animator animator) {
            }

            @Override
            public void onAnimationEnd(Animator animator) {
                view.setVisibility(View.GONE);
            }

            @Override
            public void onAnimationCancel(Animator animator) {
            }
        });

        set.setDuration(1 * 500).start();
    }
}
代码下载:http://download.csdn.net/detail/singwhatiwanna/6782865
分享到:
评论

相关推荐

    android开源动画库 nineoldandroids-2.4.0.jar

    《Android开源动画库NineOldAndroids详解》 在Android开发中,动画效果是提升用户体验的重要手段之一。然而,由于Android系统版本的多样性,使得在低版本设备上实现平滑动画成为一个挑战。为了解决这个问题,开发者...

    开源动画库nineoldandroids介绍

    使用开源动画库nineoldandroids来写Android动画,博客地址:http://blog.csdn.net/singwhatiwanna/article/details/17639987,请参看了解更多信息

    Android高手进阶教程

    Android是一个基于Linux内核的开源操作系统,由应用程序层、应用程序框架层、系统库和服务层以及Linux内核四大部分构成。在进阶学习中,理解这些层次之间的交互关系至关重要,这对于优化应用性能和解决问题非常有...

    Android动画库nineoldandroids-2.4.0.jar

    做Android开发的同学很多都知道或者使用过一个动画库,那就是NineOldAndroids,它的作者及其牛X,好几个著名的开源库都是他的作品,具体大家可以看他的 JakeWharton 。简单来说,NineOldAndroids是一个向下兼容的...

    android NineOldAndroids3.0+动画源码.rar

    NineOldAndroids是一款著名的开源库,专为Android开发者设计,用于在Android 2.1(API级别7)及更低版本上实现与Android 3.0(API级别11)及更高版本相同的动画效果。这个库由Jake Wharton开发,目的是解决Android...

    Android动画集合开源APP

    BaseAnimation,如果有新版本会自动提醒大家更新,也希望大家多多贡献自己的动画xml文件或者其他源码,一起来打造这个开源的app 博客地址:http://blog.csdn.net/duguang77/article/details/18230867

    Android NineOldAndroids3.0+动画-IT计算机-毕业设计.zip

    NineOldAndroids是一个著名的开源库,专为Android平台设计,用于在旧版本的Android系统上实现新版本的动画效果。这个库由Jake Wharton开发,它使得开发者可以在Android 2.1(API级别7)及以上的系统上使用Android ...

    安卓动画效果相关-Loadinganimations各种加载动画的开源库.rar

    5. **兼容性**:Android平台有着广泛的设备和系统版本,所以加载动画库应该考虑兼容性问题。开发者需要检查这些开源库是否支持Android的不同API级别,以便应用于更广泛的用户群体。 6. **设计原则**:优秀的加载...

    Android动画效果集合开源APP BaseAnimation源码

    这里面据说有200多种动画效果 我没看全过 就是朋友需要 让我帮忙下载 结果下载下来之后各种问题 跑不起来 是14年的项目 相当老 用eclipse开发的 我各种调调改改 终于在as3.0上跑起来了 为了帮助有需要的人 减少大家...

    NineOldAndroids Demo及jar

    在Android开发中,动画库是非常重要的组成部分,可以极大地提升用户体验。NineOldAndroids库通过提供与新版本Android兼容的动画API,使得开发者无需担心版本兼容性问题,能够在更广泛的设备上实现流畅、丰富的动画...

    开源框架,属性动画 nineoldandroids -2.4

    开源框架在IT行业中扮演着重要的角色,特别是在Android开发领域,其中`NineOldAndroids`是一个著名的库,专门针对属性动画(Property Animation)提供兼容性支持。这个库由Jake Wharton开发,目的是使得Android 2.1...

    Android代码-[安卓开源]安卓动画合集大全加站长代码库.zip

    本资源“[安卓开源]安卓动画合集大全加站长代码库.zip”提供了丰富的Android动画示例,帮助开发者深入理解并掌握Android动画系统。 1. **Android 动画种类** Android动画分为两种主要类型:视图动画(View ...

    Lottie动画库的使用1

    Lottie动画库是一个强大的跨平台工具,它允许开发者在Android、iOS以及React Native应用程序中轻松地实现复杂的动画效果。这个库的核心功能是能够解析Adobe After Effects (AE)软件中创建的动画,并将其实时渲染出来...

    Android进阶学习的项目,包括《Android开发艺术探索》学习笔记、优秀开源库学习理解等~.zip

    项目工程资源经过严格测试可直接运行成功且功能正常的情况才上传,可轻松copy复刻,拿到资料包后可轻松复现出一样的项目,本人系统开发经验充足(全栈开发),有任何使用问题欢迎随时与我联系,我会及时为您解惑,...

    Android-一个开源的MaterialDesign文件管理器适用于Android5.0

    【Android 开源 MaterialDesign 文件管理器】 在 Android 平台上,Material Design 是一套官方推荐的设计规范,它提供了统一且富有表现力的用户界面设计指南。这个开源项目——"Android-一个开源的MaterialDesign...

    NineOldAndroids3.0+动画.rar

    NineOldAndroids是一个开源库,由Jake Wharton开发,它使得Android开发者能够在API level 3(Android 1.5 Cupcake)及更高版本上使用Android 4.0(API level 14,Ice Cream Sandwich)引入的动画特性。这个库的主要...

    BaseAnimation1 2版本 Android动画集合开源APP

    BaseAnimation 如果有新版本会自动提醒大家更新 也希望大家多多贡献自己的动画xml文件或者其他源码 一起来打造这个开源的app 1 增加Splash动画 中心打开式效果 2 增加Flip折叠效果的集合 13种 3 增加NineOld...

    Android属性动画3.0以下支持包

    NineOldAndroids是由Jake Wharton开发的一个开源库,其主要目标就是为Android 2.1(API级别7)及以上版本提供属性动画的兼容性。这个库模仿了Android 3.0及以上版本的属性动画API,使得开发者可以在旧版Android设备...

    Android动画加载库AVLoadingIndicatorView.zip

    `AVLoadingIndicatorView`是一个专门为Android平台设计的开源加载动画库,它提供了丰富的加载动画效果,使得开发者在实现加载提示时有了更多的选择。这个库的出现,使得在应用程序中集成各种酷炫的加载指示器变得...

    Android非常实用的开源项目框架

    7. **UI动画库**:如Lottie,用于加载和播放SVG格式的动画。 8. **测试框架**:如JUnit、Espresso,用于编写和执行单元测试、UI测试。 9. **日志库**:如 Timber,提供更友好的日志打印机制。 10. **事件总线**:...

Global site tag (gtag.js) - Google Analytics