利用Transition制作炫酷的切换动画

前言   

  

    使用Transition动画框架,可以帮你做到:


    1不同Activity切换的时候,根据每个activity对应的layout内容的不同做整体的场景变换的动画。


     2 不同activity切换的时候,不同activity对应的layout有相同的元素,比如activity1中有一个button,activity2有一个相同的button,transition框架可以在整体场景变换的同时,特别照顾到这个button,让动画变换的过程中,这个button享有视觉连贯性。


    3 在相同的activity里,当同一个view内容发生变化,比如在代码中removie或者add了某个ui元素,或者更改了某个已有元素的尺寸,颜色信息,Transition动画框架也可以根据这个变化做动画变换。



 不同Activity切换的Transition动画


      当从activity1跳转到activity2的时候,1会执行exitTransition, 而2会执行enterTransition,你可以在代码中使用


 
  
getWindow().setExitTransition(slide/fade...);
getWindow().setEnterTransition(slide/fade...);


 
  为这个activity设置它进入和退出时需要用到的过场动画(就是方法参数),谷歌为我们预设了几个transition,分别是Explode,Slide,Fade。可以看一下他们的效果 
  

Explode Slide Fade
transition_explode transition_slide transition_fade

在定义Explode,Slide,Fade这几个动画效果的时候,可以使用xml也可以直接在代码中定义

  1 使用xml

   在res/transtion文件夹下定义一个xml文件

res/transition/activity_fade.xml

res/transition/activity_slide.xml

  
 
  
 然后需要在代码中使用TransitionInflator方法来加载这个xml文件生成对应的动画效果。

MainActivity.java
    @Override
    protected void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        setContentView(R.layout.activity_transition);
        setupWindowAnimations();
    }

    private void setupWindowAnimations() {
        Slide slide = TransitionInflater.from(this).inflateTransition(R.transition.activity_slide);
        getWindow().setExitTransition(slide);
    }



TransitionActivity.java
    @Override
    protected void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        setContentView(R.layout.activity_transition);
        setupWindowAnimations();
    }

    private void setupWindowAnimations() {
        Fade fade = TransitionInflater.from(this).inflateTransition(R.transition.activity_fade);
        getWindow().setEnterTransition(fade);
    }


2 在代码中定义

MainActivity.java
    @Override
    protected void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        setContentView(R.layout.activity_transition);
        setupWindowAnimations();
    }

    private void setupWindowAnimations() {
        Slide slide = new Slide();
        slide.setDuration(1000);
        getWindow().setExitTransition(slide);
    }
TransitionActivity.java
    @Override
    protected void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        setContentView(R.layout.activity_transition);
        setupWindowAnimations();
    }

    private void setupWindowAnimations() {
        Fade fade = new Fade();
        fade.setDuration(1000);
        getWindow().setEnterTransition(fade);
    }


效果如下


在这个过程中 


首先Actvity1启动了Activity2

Transition框架发现Activity1退出,为activity1里的可见ui组件执行Slide动画

Transition框架发现Activity2退出,为activity2里的可见ui组件执行Fade动画

当按下返回键,如果我们没有为其设置returnTransition和reenterTransition选项的话,Transition框架会对应的activity执行我们为其设置的enter和exit动画运动过程相反的动画作为默认选项。

3 ReturnTransition & ReenterTransition

他们是enterTransition和exitTransition的对应
利用Transition制作炫酷的切换动画_第1张图片
我们把之前那个例子改一下,为TransitionActivity添加一个return动画,用的是Slide,那么当我们按下返回键的时候,会看到一个slide效果,而不是fade(之前设置的enter动画的倒放)效果。
TransitionActivity.java
    @Override
    protected void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        setContentView(R.layout.activity_transition);
        setupWindowAnimations();
    }

    private void setupWindowAnimations() {
        Fade fade = new Fade();
        fade.setDuration(1000);
        getWindow().setEnterTransition(fade);

        Slide slide = new Slide();
        slide.setDuration(1000);
        getWindow().setReturnTransition(slide);        
    }
下面可以看到 没有设置return动画和设置了return动画的区别

Without Return Transition With Return Transition
Enter: Fade In Enter: Fade In
Exit: Fade Out Exit: Slide out
transition_fade 利用Transition制作炫酷的切换动画_第2张图片




二  Activity之间的享元切换


你可以通过为两个不同activity中某个view设置一个唯一的统一的标识,告诉Transition这个view是这两aictivity共享的元素,变换的时候要额外照顾到,那么transition动画框架就会在场景切换的时候,让这个view顺滑的从前一个activity转移到另一个activity(注意,这里的转移并不是真正的转移,这两个item并不是同一个对象,他们是彼此独立的两个view)


利用Transition制作炫酷的切换动画_第3张图片

1 设置windowContentTransition


首先要对app的style文件做一些修改

values/styles.xml

还可以在这里定义整个app默认的   enter, exit 和 shared element transitions动画


2在布局文件中为享元view定义相同的Transition Name,通过android:transitioName来定义,享元之间id和属性可以不同,但是TransitionName必须相同。

layout/activity_a.xml

layout/activity_b.xml


c 通过享元方式启动另一个activity

通过ActivityOptions.makeSceneTransitionAnimation()方法来定义享元具体是哪个view和Transition Name

MainActivity.java
blueIconImageView.setOnClickListener(new View.OnClickListener() {
    @Override
    public void onClick(View v) {
        Intent i = new Intent(MainActivity.this, SharedElementActivity.class);

        View sharedView = blueIconImageView;
        String transitionName = getString(R.string.blue_name);

        ActivityOptions transitionActivityOptions = ActivityOptions.makeSceneTransitionAnimation(MainActivity.this, sharedView, transitionName);
        startActivity(i, transitionActivityOptions.toBundle());
    }
});

最终效果如下图

利用Transition制作炫酷的切换动画_第4张图片




三 在Fragment之间进行享元切换动画

前两步和上面基本相同

1 设置windowContentTransition

values/styles.xml
2 在布局文件中为享元view定义相同的Transition Name
layout/fragment_a.xml

layout/fragment_b.xml

3 享元方式启动另一个Fragment

FragmentB fragmentB = FragmentB.newInstance(sample);

// Defines enter transition for all fragment views
Slide slideTransition = new Slide(Gravity.RIGHT);
slideTransition.setDuration(1000);
sharedElementFragment2.setEnterTransition(slideTransition);

// Defines enter transition only for shared element
ChangeBounds changeBoundsTransition = TransitionInflater.from(this).inflateTransition(R.transition.change_bounds);
fragmentB.setSharedElementEnterTransition(changeBoundsTransition);

getFragmentManager().beginTransaction()
        .replace(R.id.content, fragmentB)
        .addSharedElement(blueView, getString(R.string.blue_name))
        .commit();

最终结果如下


利用Transition制作炫酷的切换动画_第5张图片


四 叠加Transition动画效果


你可以设置是否让exit动画和enter动画叠加在一起执行

如果设置为true 那么enter动画会立刻执行

如果设置为false 那么exter动画会等到exit动画执行完毕之后再执行

这对Fragment和Activity之间的享元切换都有效


FragmentB fragmentB = FragmentB.newInstance(sample);

// Defines enter transition for all fragment views
Slide slideTransition = new Slide(Gravity.RIGHT);
slideTransition.setDuration(1000);
sharedElementFragment2.setEnterTransition(slideTransition);

// Defines enter transition only for shared element
ChangeBounds changeBoundsTransition = TransitionInflater.from(this).inflateTransition(R.transition.change_bounds);
fragmentB.setSharedElementEnterTransition(changeBoundsTransition);

// Prevent transitions for overlapping
fragmentB.setAllowEnterTransitionOverlap(overlap);
fragmentB.setAllowReturnTransitionOverlap(overlap);

getFragmentManager().beginTransaction()
        .replace(R.id.content, fragmentB)
        .addSharedElement(blueView, getString(R.string.blue_name))
        .commit();

动画是否叠加的具体区别见下面的动图

Overlap True Overlap False
Fragment_2 appears on top of Fragment_1 Fragment_2 waits until Fragment_1 is gone
利用Transition制作炫酷的切换动画_第6张图片 利用Transition制作炫酷的切换动画_第7张图片
五 在同一个Activity中,根据Activity布局文件中view元素的变动来进行Transition动画


1 Scene


在activity对应的布局文件中,定义一个占位用的视图,一般用framelayout,称之为稍后场景变换动画执行

所在的根视图rootView,然后在layout文件夹下,新建几个layout文件,每一个layout文件对应的视图都是一个scene,

可以这么理解,我们打算在Activity对应的界面视图的上半部分放置即将运行的动画,这个上半部分我们用帧布局

占位,称之为rootView,然后动画效果是一个小球从左边移动到右边,那么我们只需要在layout文件夹下面

定义两个layout文件,一个layout文件中,小球在左边,另一个文件中小球在右边。这两个layout,在代码中通过

Scene.getSceneForLayout(rootView,R.layout.***,this)生成一个Scene,此时,rootView和每一个scene的链接关系

就建立起来了。在通过TransitionManager.go方法就可以让动画在rootView中运行起来。


还是看一下代码吧

scene1 = Scene.getSceneForLayout(sceneRoot, R.layout.activity_animations_scene1, this);
scene2 = Scene.getSceneForLayout(sceneRoot, R.layout.activity_animations_scene2, this);
scene3 = Scene.getSceneForLayout(sceneRoot, R.layout.activity_animations_scene3, this);
scene4 = Scene.getSceneForLayout(sceneRoot, R.layout.activity_animations_scene4, this);

(...)

@Override
public void onClick(View v) {
    switch (v.getId()) {
        case R.id.button1:
            TransitionManager.go(scene1, new ChangeBounds());
            break;
        case R.id.button2:
            TransitionManager.go(scene2, TransitionInflater.from(this).inflateTransition(R.transition.slide_and_changebounds));
            break;
        case R.id.button3:
            TransitionManager.go(scene3, TransitionInflater.from(this).inflateTransition(R.transition.slide_and_changebounds_sequential));
            break;
        case R.id.button4:
            TransitionManager.go(scene4, TransitionInflater.from(this).inflateTransition(R.transition.slide_and_changebounds_sequential_with_interpolators));
            break;  
    }
}

上面的代码就会在同一个Activity中的四个Scene中间生成对应的动画,而且每个动画都不同

利用Transition制作炫酷的切换动画_第8张图片


2 布局文件中View属性的变动


Transition动画还可以检测到同一个布局文件中某些视图元素的属性变化,然后做相应的动画效果,你可以随意在代码中修改

你需要的一切变动,比如颜色,大小,位置之类的,之后的动画Transition会自动帮你实现。

做到这些 只需要

a 先调用BeginDelayTransition方法

TransitionManager.beginDelayedTransition(sceneRoot);

b 更改layout布局中的view属性

ViewGroup.LayoutParams params = greenIconView.getLayoutParams();
params.width = 200;
greenIconView.setLayoutParams(params);

更改greenIconView的width时,会触发view的onMeasure()方法,此时Transition框架会记录下该view的起始width值并

做出相应的动画

利用Transition制作炫酷的切换动画_第9张图片


六 享元动画+circle Reveal动画


享元方式的过场动画前面已经提到了,而CircleReveal是属性动画的一种,和享元切换一样都是安卓5.0之后引入的新特性

,对了忘了提,上面享元切换和这个circleReveal动画在执行前最好都加上一个版本判断

if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.LOLLIPOP){

}

CircleReveal实现圆形缩放效果,可用来突出显示某个部分。他和享元切换一起使用可以引导用户的视觉焦点,给他的点击以反馈。

利用Transition制作炫酷的切换动画_第10张图片

上面的动画 依次执行了

从MainActivity到RevealActivity的一个享元切换动画

在RevealActivity中有一个监听器,当它监听到RevealActivity中的享元切换动画执行完之后,为Toobar执行了一个CircleReveal属性动画

同时为RevealActivity中其他视图执行了一个放大的属性动画

Listen to shared element enter transition end
Transition transition = TransitionInflater.from(this).inflateTransition(R.transition.changebounds_with_arcmotion);
getWindow().setSharedElementEnterTransition(transition);
transition.addListener(new Transition.TransitionListener() {
    @Override
    public void onTransitionEnd(Transition transition) {
        animateRevealShow(toolbar);
        animateButtonsIn();
    }

    (...)

});


Reveal Toolbar
private void animateRevealShow(View viewRoot) {
    int cx = (viewRoot.getLeft() + viewRoot.getRight()) / 2;
    int cy = (viewRoot.getTop() + viewRoot.getBottom()) / 2;
    int finalRadius = Math.max(viewRoot.getWidth(), viewRoot.getHeight());

    Animator anim = ViewAnimationUtils.createCircularReveal(viewRoot, cx, cy, 0, finalRadius);
    viewRoot.setVisibility(View.VISIBLE);
    anim.setDuration(1000);
    anim.setInterpolator(new AccelerateInterpolator());
    anim.start();
}
Scale up activity layout views
private void animateButtonsIn() {
    for (int i = 0; i < bgViewGroup.getChildCount(); i++) {
        View child = bgViewGroup.getChildAt(i);
        child.animate()
                .setStartDelay(100 + i * DELAY)
                .setInterpolator(interpolator)
                .alpha(1)
                .scaleX(1)
                .scaleY(1);
    }
}

更多CircleReveal动画的例子

你可以随意设置你想要的Reveal动画,但前提是这些动画可以有效的告知用户 app对用户的点击都做了什么反馈



1 从目标视图中心开始动画

利用Transition制作炫酷的切换动画_第11张图片

int cx = (viewRoot.getLeft() + viewRoot.getRight()) / 2;
int cy = viewRoot.getTop();
int finalRadius = Math.max(viewRoot.getWidth(), viewRoot.getHeight());

Animator anim = ViewAnimationUtils.createCircularReveal(viewRoot, cx, cy, 0, finalRadius);
viewRoot.setBackgroundColor(color);
anim.start();

2 从目标视图顶部开始 并结合其他属性动画

利用Transition制作炫酷的切换动画_第12张图片

int cx = (viewRoot.getLeft() + viewRoot.getRight()) / 2;
int cy = (viewRoot.getTop() + viewRoot.getBottom()) / 2;
int finalRadius = Math.max(viewRoot.getWidth(), viewRoot.getHeight());

Animator anim = ViewAnimationUtils.createCircularReveal(viewRoot, cx, cy, 0, finalRadius);
viewRoot.setBackgroundColor(color);
anim.addListener(new AnimatorListenerAdapter() {
    @Override
    public void onAnimationEnd(Animator animation) {
        animateButtonsIn();
    }
});
anim.start();


3 从点击位置开始动画

利用Transition制作炫酷的切换动画_第13张图片

@Override
public boolean onTouch(View view, MotionEvent motionEvent) {
    if (motionEvent.getAction() == MotionEvent.ACTION_DOWN) {
        if (view.getId() == R.id.square_yellow) {
            revealFromCoordinates(motionEvent.getRawX(), motionEvent.getRawY());
        }
    }
    return false;
}

private Animator animateRevealColorFromCoordinates(int x, int y) {
    float finalRadius = (float) Math.hypot(viewRoot.getWidth(), viewRoot.getHeight());

    Animator anim = ViewAnimationUtils.createCircularReveal(viewRoot, x, y, 0, finalRadius);
    viewRoot.setBackgroundColor(color);
    anim.start();
}


4 结合Transition动画


利用Transition制作炫酷的切换动画_第14张图片

Transition transition = TransitionInflater.from(this).inflateTransition(R.transition.changebounds_with_arcmotion);
transition.addListener(new Transition.TransitionListener() {
    @Override
    public void onTransitionEnd(Transition transition) {
        animateRevealColor(bgViewGroup, R.color.red);
    }
    (...)

});
TransitionManager.beginDelayedTransition(bgViewGroup, transition);
RelativeLayout.LayoutParams layoutParams = new RelativeLayout.LayoutParams(RelativeLayout.LayoutParams.WRAP_CONTENT, RelativeLayout.LayoutParams.WRAP_CONTENT);
layoutParams.addRule(RelativeLayout.CENTER_IN_PARENT);
btnRed.setLayoutParams(layoutParams);



你可能感兴趣的:(Android,app优化)