Android动画之属性动画(下)

前言

       前面我们已经完整的讲述了属性动画的实现,我们已经学会了怎么实现动画,如果没有属性我们也学会了怎么添加属性,还学习了用ValueAnimator来实现动画。

Evaluator

       这里我们来学习剩下的属性,首先我们来看看Evaluator,Evaluator是什么?他有什么用?

       Evaluator翻译为求值器,或者表达式,反正只可意会不可言传,就是给一个表达式,计算对应的值,他主要有以下属性:

1, 返回动画当前时间点的属性值
2,全部继承自TypeEvaluator
3,系统实现了IntEvaluator,FloatEvaluator,ArgbEvaluator
4,Api21实现了PointFEvaluator

       我们解释一下上面的属性,首先表达式可以计算当前时间点的值,并且所有的Evaluator都继承自TypeEvaluator,从这一点就可以知道,我们可以自己来实现Evaluator,目前已经有几个默认的实现,我们前面所实现的动画都是ofFloat,ofInt没有实现过其他类型的动画,这是因为float,int的表达式系统已经默认实现,如果有一个新的表达式不属于上面的任何属性,我们怎么办?

       我们可以自定义Evaluator,我们接下来实现一个自定义的Evaluator。我们来实现一个抛物线的效果,效果如下图:

这里写图片描述

       一个view从0这个位置移动到600这个位置,一个抛物线的效果,表达式如下:

y=1150x2+4x

       我们根据上述的表达式来实现动画,动画下过如下:

private void setCustomEvaluator() {
    ValueAnimator animator = new ValueAnimator();
    float offsetX = love.getX();
    float offsetY = love.getY();
    animator.setDuration(2000);
    animator.setObjectValues(new PointF(offsetX, offsetY), new PointF(TRANS_X + offsetX, offsetY));
    animator.setEvaluator(new TypeEvaluator<PointF>() {
        @Override
        public PointF evaluate(float fraction, PointF startValue, PointF endValue) {
            PointF pointF = new PointF();
            float d = fraction * TRANS_X;
            pointF.x = startValue.x + d;
            pointF.y = startValue.y + (1.0f / 150f) * d * d - 4 * d;
            return pointF;
        }
    });
    animator.addUpdateListener(new ValueAnimator.AnimatorUpdateListener() {
        @Override
        public void onAnimationUpdate(ValueAnimator animation) {
            PointF pointF = (PointF) animation.getAnimatedValue();
            love.setX(pointF.x);
            love.setY(pointF.y);
        }
    });
    animator.start();
}

       这里我们需要注意的几点是第一个设置好Evaluator,第二设置ObjectValues,之后在onAnimationUpdate回调中获取得到的值,Evaluator中就是我们根据我们的函数计算得到的值。这样我们的自定义动画就已经实现完成了。

Keyframe

       这里的第二个需要讲解的知识点是Keyframe,Keyframe的意思如下:

1, KeyFrame是一个时间/值对
2, KeyFrame之间可以定义不同的Interpolator

       这里我们用Keyframe来实现动画:

private void keyFrame() {
    Keyframe keyframe1 = Keyframe.ofFloat(0f, 0f);
    Keyframe keyframe2 = Keyframe.ofFloat(0.5f, 360f);
    Keyframe keyframe3 = Keyframe.ofFloat(1.0f, 0f);
    PropertyValuesHolder pvh = PropertyValuesHolder.ofKeyframe("rotation", keyframe1, keyframe2, keyframe3);
    ObjectAnimator rotate = ObjectAnimator.ofPropertyValuesHolder(love, pvh);
    rotate.setDuration(2000);
    rotate.start();
}

       我们先定义几个Keyframe,最后将Keyframe使用到PropertyValuesHolder中,最后又将PropertyValuesHolder使用到ObjectAnimator中,上诉定义了三个Keyframe,比如我们实现一个旋转的动画,第一个关键帧为Keyframe.ofFloat(0f, 0f)表示第0时角度为0,剩下的一半的时间角度360,最终位置的角度为0度。

       其实这里的Keyframe我们前面已经有过使用,比如:

ObjectAnimator scaleX = ObjectAnimator.ofFloat(love, "scaleX", 1.0f, 2.0f);

       这里的1.0f与2.0f就表示开始的关键帧与结束的关键帧,只是这里只有两帧。

XML实现

       前面我们已经完全用代码实现了动画,但是动画有两种实现方式,还有一种实现方式就是用xml来实现。接下来我们就用xml来实现一个动画:

       1:首先定义一个动画资源,放置到res/animator文件夹下,定义一个trans_scale.xml:

<?xml version="1.0" encoding="utf-8"?>
<set xmlns:android="http://schemas.android.com/apk/res/android"
     android:ordering="sequentially">

    <objectAnimator
        android:duration="2000"
        android:propertyName="translationY"
        android:valueTo="-200f"
        android:valueType="floatType"></objectAnimator>

    <set android:ordering="together">
        <objectAnimator
            android:duration="2000"
            android:propertyName="scaleX"
            android:valueTo="2.0f"
            android:valueType="floatType"></objectAnimator>
        <objectAnimator
            android:duration="2000"
            android:propertyName="scaleY"
            android:valueTo="2.0f"
            android:valueType="floatType"></objectAnimator>

    </set>

</set>

       这里如果是set我们需要设置ordering属性,主要有together并且与sequentially串行,每一属性需要设置valueType。

       2:代码中进行加载:

private void compositeAnimXml(){
    Animator animator = AnimatorInflater.loadAnimator(this, R.animator.trans_scale);
    animator.setTarget(love);
    animator.start();
}

       这样我们就用xml实现了动画。

Layout Animations

       Layout Animations为布局动画,控制view加入与删除自身的动画与周围view的动画。主要有以下五种效果:

1, LayoutTransition.APPEARING,对出现的view设置动画
2, LayoutTransition.CHANGE_APPEARING,其他view设置动画
3, LayoutTransition.DISAPPEARING,对消失的view设置动画
4, LayoutTransition. CHANGE_DISAPPEARING ,其他view设置动画
5, LayoutTransition. CHANGE ,其他view设置动画

       上面的五种效果,后面都已经分别做了解释,这里我们用一个demo来演示一下:

       定义布局文件如下:

<?xml version="1.0" encoding="utf-8"?>
<LinearLayout
    android:id="@+id/root"
    xmlns:android="http://schemas.android.com/apk/res/android"
    xmlns:tools="http://schemas.android.com/tools"
    android:layout_width="match_parent"
    android:layout_height="match_parent"
    android:orientation="vertical"
    android:paddingBottom="@dimen/activity_vertical_margin"
    android:paddingLeft="@dimen/activity_horizontal_margin"
    android:paddingRight="@dimen/activity_horizontal_margin"
    android:paddingTop="@dimen/activity_vertical_margin"
    tools:context="com.netease.study.ui.animation.LayoutAnimationActivity">

    <Button
        android:id="@+id/add_view"
        android:layout_width="wrap_content"
        android:layout_height="wrap_content"
        android:text="addView"/>

</LinearLayout>

       代码实现如下:

public class LayoutAnimationActivity extends AppCompatActivity implements View.OnClickListener {

    public static void start(Context context) {
        Intent intent = new Intent();
        intent.setClass(context, LayoutAnimationActivity.class);
        context.startActivity(intent);
    }

    LinearLayout root;

    int i = 0;

    @Override
    protected void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        setContentView(R.layout.activity_layout_animation);
        findViews();
        setViewListener();
    }

    private void findViews() {
        root = (LinearLayout) findViewById(R.id.root);
        root.setLayoutTransition(getLayoutTransition());
    }

    private LayoutTransition getLayoutTransition() {
        LayoutTransition transition = new LayoutTransition();
        transition.setAnimator(LayoutTransition.APPEARING, transition.getAnimator(LayoutTransition.APPEARING));
        transition.setAnimator(LayoutTransition.CHANGE_APPEARING, transition.getAnimator(LayoutTransition
                .CHANGE_APPEARING));
        transition.setAnimator(LayoutTransition.CHANGE_DISAPPEARING, transition.getAnimator(LayoutTransition
                .CHANGE_DISAPPEARING));
        transition.setAnimator(LayoutTransition.DISAPPEARING, transition.getAnimator(LayoutTransition.DISAPPEARING));
        transition.setAnimator(LayoutTransition.CHANGING, transition.getAnimator(LayoutTransition.CHANGING));
        return transition;
    }

    private void setViewListener() {
        findViewById(R.id.add_view).setOnClickListener(this);
    }

    @Override
    public void onClick(View v) {
        switch (v.getId()) {
            case R.id.add_view:
                addViews();
                break;
        }
    }

    private void addViews() {
        final Button button = new Button(this);
        button.setText("button=" + i++);
        button.setOnClickListener(new View.OnClickListener() {
            @Override
            public void onClick(View v) {
                root.removeView(button);
            }
        });
        root.addView(button);
    }
}

private void compositeAnimXml(){
    Animator animator = AnimatorInflater.loadAnimator(this, R.animator.love_f);
    animator.setTarget(love);
    animator.start();
}

       我们对线性布局添加button,对布局设置LayoutTransition,这样就实现了布局动画,最常用的效果就是比如listview中加载item时设置动画。

转场动画

       在文章的最后我们来讲述一下转场动画,什么是转场动画,通俗的来说,就是页面切换的效果。

为什么要设置?

       由于不同的手机打开关闭的效果不一致,各个手机都有不同的效果,效果不是很好,我们可以统一来控制页面打开关闭的效果,保证所有手机效果的统一性,提升用户体验。

怎么设置?

       首先定义进入退出的动画,第一个种方式就是采用style来实现,主要是来复写android:windowAnimationStyle节点,系统默认有如下可以设置的属性:

<style name="Animation.Activity">
    <item name="activityOpenEnterAnimation">@anim/activity_open_enter</item>
    <item name="activityOpenExitAnimation">@anim/activity_open_exit</item>
    <item name="activityCloseEnterAnimation">@anim/activity_close_enter</item>
    <item name="activityCloseExitAnimation">@anim/activity_close_exit</item>
    <item name="taskOpenEnterAnimation">@anim/task_open_enter</item>
    <item name="taskOpenExitAnimation">@anim/task_open_exit</item>
    <item name="launchTaskBehindTargetAnimation">@anim/launch_task_behind_target</item>
    <item name="launchTaskBehindSourceAnimation">@anim/launch_task_behind_source</item>
    <item name="taskCloseEnterAnimation">@anim/task_close_enter</item>
    <item name="taskCloseExitAnimation">@anim/task_close_exit</item>
    <item name="taskToFrontEnterAnimation">@anim/task_open_enter</item>
    <item name="taskToFrontExitAnimation">@anim/task_open_exit</item>
    <item name="taskToBackEnterAnimation">@anim/task_close_enter</item>
    <item name="taskToBackExitAnimation">@anim/task_close_exit</item>
    <item name="wallpaperOpenEnterAnimation">@anim/wallpaper_open_enter</item>
    <item name="wallpaperOpenExitAnimation">@anim/wallpaper_open_exit</item>
    <item name="wallpaperCloseEnterAnimation">@anim/wallpaper_close_enter</item>
    <item name="wallpaperCloseExitAnimation">@anim/wallpaper_close_exit</item>
    <item name="wallpaperIntraOpenEnterAnimation">@anim/wallpaper_intra_open_enter</item>
    <item name="wallpaperIntraOpenExitAnimation">@anim/wallpaper_intra_open_exit</item>
    <item name="wallpaperIntraCloseEnterAnimation">@anim/wallpaper_intra_close_enter</item>
    <item name="wallpaperIntraCloseExitAnimation">@anim/wallpaper_intra_close_exit</item>
    <item name="fragmentOpenEnterAnimation">@animator/fragment_open_enter</item>
    <item name="fragmentOpenExitAnimation">@animator/fragment_open_exit</item>
    <item name="fragmentCloseEnterAnimation">@animator/fragment_close_enter</item>
    <item name="fragmentCloseExitAnimation">@animator/fragment_close_exit</item>
    <item name="fragmentFadeEnterAnimation">@animator/fragment_fade_enter</item>
    <item name="fragmentFadeExitAnimation">@animator/fragment_fade_exit</item>
</style>

       这里定义了页面进入的动画,页面退出的动画等等,由于不同的手机默认实现的效果不同,因此我们可以复写上述这些属性,达到统一的效果。

       我们首先定义如下三个动画:
       水平进入的动画base_slide_right_in.xml

<?xml version="1.0" encoding="utf-8"?>
<set
  xmlns:android="http://schemas.android.com/apk/res/android">
    <translate android:interpolator="@android:anim/decelerate_interpolator" android:duration="300"
               android:fromXDelta="100.0%" android:toXDelta="0.0%" />
</set>

       水平退出的动画base_slide_right_out.xml

<?xml version="1.0" encoding="utf-8"?>
<set
    xmlns:android="http://schemas.android.com/apk/res/android">
    <translate
        android:duration="300"
        android:fromXDelta="0.0%"
        android:interpolator="@android:anim/accelerate_interpolator"
        android:toXDelta="100.0%"/>
</set>

       原地不动的动画base_stay_orig.xml

<?xml version="1.0" encoding="utf-8"?>
<set
    xmlns:android="http://schemas.android.com/apk/res/android">
    <translate
        android:duration="300"
        android:fromXDelta="0.0%"
        android:interpolator="@android:anim/decelerate_interpolator"
        android:toXDelta="0.0%"/>
</set>

       之后我们复写android:windowAnimationStyle属性,我们定义一个新的style,设置到android:windowAnimationStyle中:

<style name="WindowAnimation" parent="@android:style/Animation">
    <item name="android:activityOpenEnterAnimation">@anim/base_slide_right_in</item>
    <item name="android:activityOpenExitAnimation">@anim/base_stay_orig</item>
    <item name="android:activityCloseEnterAnimation">@anim/base_stay_orig</item>
    <item name="android:activityCloseExitAnimation">@anim/base_slide_right_out</item>
    <item name="android:taskOpenEnterAnimation">@anim/base_slide_right_in</item>
    <item name="android:taskOpenExitAnimation">@anim/base_stay_orig</item>
    <item name="android:taskCloseEnterAnimation">@anim/base_stay_orig</item>
    <item name="android:taskCloseExitAnimation">@anim/base_slide_right_out</item>
    <item name="android:taskToFrontEnterAnimation">@anim/base_slide_right_in</item>
    <item name="android:taskToFrontExitAnimation">@anim/base_stay_orig</item>
    <item name="android:taskToBackEnterAnimation">@anim/base_stay_orig</item>
    <item name="android:taskToBackExitAnimation">@anim/base_slide_right_out</item>
</style>

       这样页面转场的动画就已经实现完成了,不过遗憾的是,有的手机不起作用。。。。我们就需要采用另外一种方式来实现了,我们调用overridePendingTransition:

       overridePendingTransition函数如下:

public void overridePendingTransition(int enterAnim, int exitAnim) {
    try {
        ActivityManagerNative.getDefault().overridePendingTransition(
                mToken, getPackageName(), enterAnim, exitAnim);
    } catch (RemoteException e) {
    }
}

       定义一个进入的动画,和一个退出的动画

这里需要注意的是overridePendingTransition有非常严格的调用条件,它必须在startActivity或者finish之后调用

       调用方式如下:

@Override
public void finish() {
    super.finish();
    customExit();
}

private void customExit() {
    overridePendingTransition(android.R.anim.fade_in, android.R.anim.fade_out);
}

总结

       到此属性动画就已经实现完成了,我们学习了动画怎么实现,怎么自定义Evaluator,怎么实现布局动画。

  • 1
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值