Android动画 -- Property Animation(二)

本章内容

在《Android动画 – Property Animation(一)》中已经对属性动画进行了初步的认识和使用,这一篇是对属性动画的高级使用,其中包括了多属性动画布局动画(LayoutTransition),不过属性动画中的插值器(Interpolator)和估值器(TypeEvaluator)这两个很重要的属性不会在本篇中详细讲述,会额外开一篇来讲这两个。

多属性动画

在通过上一篇Android动画-Property Animation(一)中了解到多个动画一起执行可以使用XML去定义或者AnimatorSet,在 Property Animation 中还提供了其他的方法。

PropertyValuesHolder

PropertyValuesHolder的用法和AnimatorSet其实差不太多,它把动画分开来,然后塞给ObjectAnimator

Android动画 -- Property Animation(二)_第1张图片

public void start(View view) {
        PropertyValuesHolder alpha = PropertyValuesHolder.ofFloat("alpha", 0.3f, 1f);
        PropertyValuesHolder pvhX = PropertyValuesHolder.ofFloat("scaleX", 0.2f, 1.4f);
        PropertyValuesHolder pvhY = PropertyValuesHolder.ofFloat("scaleY", 0.2f, 1.4f);
        ObjectAnimator animator = ObjectAnimator.ofPropertyValuesHolder(imageView, alpha, pvhX, pvhY);
        animator.setInterpolator(new BounceInterpolator());
        animator.setDuration(2000);
        animator.start();
    }

ViewPropertyAnimator

和PropertyValuesHolder不同的是,ViewPropertyAnimator是不需要知道起始点,也不需要过程,它要的仅仅是结果,而且这个结果是一个 绝对结果,先看个效果:

Android动画 -- Property Animation(二)_第2张图片

在上图中,很明显我点击了多次 start 按钮,但是看到的效果只有第一次,那么来解释下什么是”绝对结果”

public void start(View view) {
        ViewPropertyAnimator animate = imageView.animate();
        animate.alpha(0.3f);
        animate.rotationX(30);
        animate.scaleX(1.4f);
        animate.scaleY(1.4f);
        animate.setInterpolator(new BounceInterpolator());
        animate.setDuration(2000);
        animate.start();

    }

ViewPropertyAnimator是从控件本身获取到的,上图中我想要的结果是:
透明度:30%
向屏幕内侧与X轴平面成 30°
X轴、Y轴分别放大道 1.4倍
那么当第一次动画结束,已经达到我想要的效果了,那么之后的点击都不会再看到动画效果,这个就是所谓的 绝对结果
想看到 相对结果 ,可以让方法里面的变量随着点击次数变化,这里可以通过ViewPropertyAnimator的两个不同的API translationYBy(float) translationY(float),来看看相对结果,顺便认识下这两个API的不同。

translationY(float)

translationYBy(float)

上面的效果中,我都点了多次,只有translationYBy这个看到了多次效果

public void start(View view) {
        ViewPropertyAnimator animate = imageView.animate();
        animate.alpha(0.3f);
        animate.rotationX(30);
//      animate.translationY(250);
        animate.translationYBy(250);
        animate.scaleX(1.4f);
        animate.scaleY(1.4f);
        animate.setInterpolator(new BounceInterpolator());
        animate.setDuration(2000);
        animate.start();
    }

那么很明显了,translationY 这个API,它是相对于控件初始原点(即控件左上角),位移到Y轴250处,这个坐标点是一个绝对的,因为控件初始原点是绝对的。而 translationYBy 不同,它是以控件当前位置的原点作比较,随意会一直位移下去。最后,ViewPropertyAnimator 这个动画是没有setRepeat的方法,它的用法决定了这一点。

Keyframe

keyFrame是一个 时间/值 对,通过它可以定义一个在特定时间的特定状态,即关键帧。这里的时间 key,是在 0-1之间取值,上面动画可以看到在不同的时间段上设置差异较大的值,动画快慢也不同了。

以第一个改变View 宽高的代码为例:

public void start(View view) {

        Keyframe kf0 = Keyframe.ofInt(0, 400);
        Keyframe kf1 = Keyframe.ofInt(0.25f, 200);
        Keyframe kf2 = Keyframe.ofInt(0.5f, 400);
        Keyframe kf4 = Keyframe.ofInt(0.75f, 100);
        Keyframe kf3 = Keyframe.ofInt(1f, 500);

        PropertyValuesHolder pvhRotation = PropertyValuesHolder.ofKeyframe("height", kf0, kf1, kf2, kf4, kf3);
        PropertyValuesHolder pvhRotation2 = PropertyValuesHolder.ofKeyframe("width", kf0, kf1, kf2, kf4, kf3);
        ObjectAnimator rotationAnim = ObjectAnimator.ofPropertyValuesHolder(view, pvhRotation, pvhRotation2);
        rotationAnim.setDuration(3000);
        rotationAnim.start();

    }

代码的意思是:
动画初始:宽高都是400
动画1/4:宽高变成200
动画1/2:宽高变成400
动画3/4:宽高变成100
动画结束:宽高变成500

布局动画 LayoutTransition

这一部分参考了 hongyang 大神的Android 属性动画(Property Animation) 完全解析 (下),这块儿的布局动画确实应用场景不是太多,在APP里面数据多,一般都会用 ListView GridView ViewPager 之类,所以这种动画的情景就不多见了。

APPEARING        
当一个元素变为Visible时对其应用的动画

CHANGE_APPEARING   
当一个元素变为Visible时,因系统要重新布局有一些元素需要移动,这些要移动的元素应用的动画

DISAPPEARING      
当一个元素变为InVisible时对其应用的动画

CHANGE_DISAPPEARING 
当一个元素变为Gone时,因系统要重新布局有一些元素需要移动,这些要移动的元素应用的动画

Android动画 -- Property Animation(二)_第3张图片

在这个动画里面,不管是删除子View,还是添加子View,不管是父布局、还是自布局都有动画,这是因为我把上述的四中属性全部添加进去了,不过子View的添加、删除动画,这里是我自定义的,直接看代码。

public class LayoutTransitionActivity extends AppCompatActivity {

    private GridLayout gridLayout, gridLayout2;

    private int gridlayout_count, gridlayout2_count;

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

        gridLayout = (GridLayout) findViewById(R.id.gridLayout);
        gridLayout2 = (GridLayout) findViewById(R.id.gridLayout2);

        LayoutTransition transition = new LayoutTransition();
        transition.setAnimator(LayoutTransition.CHANGE_APPEARING,
                transition.getAnimator(LayoutTransition.CHANGE_APPEARING));
        transition.setAnimator(LayoutTransition.APPEARING,
                getChildObjectAnimator(0f, 1f));
        transition.setAnimator(LayoutTransition.DISAPPEARING,
                getChildObjectAnimator(1f, 0f));
        transition.setAnimator(LayoutTransition.CHANGE_DISAPPEARING,
                transition.getAnimator(LayoutTransition.CHANGE_DISAPPEARING));
        gridLayout.setLayoutTransition(transition);
        gridLayout2.setLayoutTransition(transition);
    }

    public void add(View view) {
        createButton();
    }

    public void add2(View view) {
        createButton2();
    }

    public void createButton() {
        final Button button = new Button(this);
        button.setText((++gridlayout_count) + "");
        gridLayout.addView(button, Math.min(1, gridLayout.getChildCount()));
        button.setOnClickListener(new View.OnClickListener() {
            @Override
            public void onClick(View v) {
                gridLayout.removeView(button);
            }
        });
    }

    public void createButton2() {
        final Button button = new Button(this);
        button.setText((++gridlayout2_count) + "");
        gridLayout2.addView(button, Math.min(1, gridLayout2.getChildCount()));
        button.setOnClickListener(new View.OnClickListener() {
            @Override
            public void onClick(View v) {
                gridLayout2.removeView(button);
            }
        });
    }

    private ObjectAnimator getChildObjectAnimator(float start, float end) {
        ObjectAnimator objectAnimator = ObjectAnimator.ofFloat(this, "scaleX", start, end);
        objectAnimator.setDuration(200);
        return objectAnimator;
    }

}

很明显的

LayoutTransition transition = new LayoutTransition();
        transition.setAnimator(LayoutTransition.CHANGE_APPEARING,
                transition.getAnimator(LayoutTransition.CHANGE_APPEARING));
        transition.setAnimator(LayoutTransition.APPEARING,
                getChildObjectAnimator(0f, 1f));
        transition.setAnimator(LayoutTransition.DISAPPEARING,
                getChildObjectAnimator(1f, 0f));
        transition.setAnimator(LayoutTransition.CHANGE_DISAPPEARING,
                transition.getAnimator(LayoutTransition.CHANGE_DISAPPEARING));
        gridLayout.setLayoutTransition(transition);
        gridLayout2.setLayoutTransition(transition);

这一块代码是对布局动画的初始化,父布局的添加删除动画是系统默认的,子View的添加删除动画默认是淡入、淡出,这里我设置成了自己定义的缩放效果,最后再设置给两个gridLayout。

不单单是GridLayout,我还写了个简单的 LinearLayout,

Android动画 -- Property Animation(二)_第4张图片

其实都是一样的。

以上就是本章内容了,多属性动画还好,布局动画就有些局限性了,就如上面的LinearLayout展示的,如果有这样的UI,我们肯定会用ListView RecyclerView这些,不单单是因为写起来简单,还因为控件的复用性,LinearLayout就做不到。

源码下载

你可能感兴趣的:(android,动画)