Android 5.0之后。Transition 被更多的应用起来,support也对其越来越多得支持。这种大背景下,各种动画的学习便是必不可少的了。而其中Transition便是其中的佼佼者。
下面是比较详细的介绍和应用实践,我也是主要通过这个项目学习,但强烈建议,只先通看文档,别上来直接就照着写(还有看完后,记得回来啊!!!):
看完后,我那叫一个心血澎湃。但按住心情,现在才是真正意义上的开始学习。
我学习的过程,大概分以下几部分,可以做个参考,也非常欢迎大家的质疑和讨论。
先运行Demo或者Examples代码,运行结果ok后,认真研读代码。我始终认为,读一个轻量,但完整的工程代码,是快速学习的最有效的途径
对关键API(类和方法)的学习。知其然,也必须,知其所以然
尝试代码。写代码千万不要粘贴,千万不要忘记配置文件,android中千万不要忘记style等
Activity跳转
1)Activity跳转都是需要添加window
属性。
配置文件:
<style name="MaterialAnimations" parent="@style/Theme.AppCompat.Light.NoActionBar"> ... //添加window开启Transtions动画属性 <item name="android:windowContentTransitions">true</item> //是否覆盖执行,其实可以理解成是否同步执行还是顺序执行 <item name="android:windowAllowEnterTransitionOverlap">false</item> <item name="android:windowAllowReturnTransitionOverlap">false</item> ...</style>
当然,也可以直接在代码里写
getWindow().requestFeature(Window.FEATURE_CONTENT_TRANSITIONS);
不过不建议在代码里写,因为现在使用Support v7 比较多,所以,不是直接原生的Activity,会又很多莫名其妙的错误。
2)配置进出动画
private void setupWindowAnimations() { // Re-enter transition is executed when returning to this activity Slide slideTransition = new Slide(); slideTransition.setSlideEdge(Gravity.LEFT); slideTransition.setDuration(getResources().getInteger(R.integer.anim_duration_long)); getWindow().setReenterTransition(slideTransition); // 5.0以后的方法 getWindow().setExitTransition(slideTransition); // 5.0以后的方法}
3)启动页面跳转
这里需要注意的跳转的时候一定一定一定要配置ActivityOption。
// 配置这个option必不可少ActivityOptions options = ActivityOptions.makeSceneTransitionAnimation(activity,view,shareName); startActivity(intent,options.toBundle());
当然,现在用support已经成为主流,下面是v4提供的支持类,用法相同
// 配置这个option必不可少ActivityOptionsCompat options = ActivityOptionsCompat.makeSceneTransitionAnimation(activity,view,shareName); startActivity(intent,options.toBundle());
好了,基本Activity的跳转就实现了。官方提供了三种实践,Explode, Slide 和Fade,分别是上下拉开,上下左右滑入滑出,以及淡出淡入等。
ShareElement共享元素
不同界面的相同元素,进行跳转的时候,共联跳转,看起来简直是舒服到不行。也是我学习TransitionAnimation最大的动力。
Material-Animations中有非常详细的介绍实现。
我整体试了一下,发觉使用Fragment
比使用Activity
动画不仅连贯很多,而且整体感更强。而且,Fragment
的API对Transtion更加的亲善。
// Transition for fragment1Slide slideTransition = new Slide(Gravity.LEFT); slideTransition.setDuration(getResources().getInteger(R.integer.anim_duration_long));// Create fragment and define some of it transitionsSharedElementFragment1 sharedElementFragment1 = SharedElementFragment1.newInstance(sample);// 下面这几个方法都是Fragment的方法,可见Google已经建议使用Fragment,进行共享元素跳转是相当好的体验sharedElementFragment1.setReenterTransition(slideTransition); sharedElementFragment1.setExitTransition(slideTransition); sharedElementFragment1.setSharedElementEnterTransition(new ChangeBounds()); getSupportFragmentManager().beginTransaction() .replace(R.id.sample2_content, sharedElementFragment1) .commit();
Google建议,使用Fragment去承载UI界面,Activity主要承载操作Fragment。因此,关于跳转,也强烈建议使用上面的实践。
View动画
Scenes
在一个界面内(Activity or Fragment)实现动画,就需要scenes
,这个scene,翻译成视图,结果图都觉得很别扭,我就这么解释,scene是一种静态或者结果状态,就是最后是啥样子的。如果是位移动画,相当于初始位置的图,和运动完后的位置图。然后由,TransitionManager
最后调用,go方法,他就跑起来了。
详细请看项目中的AnimationsActivity2
类,核心代码是:
cene1 = 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); View button1 = findViewById(R.id.sample3_button1); button1.setOnClickListener(new View.OnClickListener() { @Override public void onClick(View v) { TransitionManager.go(scene1, new ChangeBounds()); } }); View button2 = findViewById(R.id.sample3_button2); button2.setOnClickListener(new View.OnClickListener() { @Override public void onClick(View v) { TransitionManager.go(scene2, TransitionInflater.from(AnimationsActivity2.this).inflateTransition(R.transition.slide_and_changebounds)); } }); View button3 = findViewById(R.id.sample3_button3); button3.setOnClickListener(new View.OnClickListener() { @Override public void onClick(View v) { TransitionManager.go(scene3, TransitionInflater.from(AnimationsActivity2.this).inflateTransition(R.transition.slide_and_changebounds_sequential)); } }); View button4 = findViewById(R.id.sample3_button4); button4.setOnClickListener(new View.OnClickListener() { @Override public void onClick(View v) { TransitionManager.go(scene4, TransitionInflater.from(AnimationsActivity2.this).inflateTransition(R.transition.slide_and_changebounds_sequential_with_interpolators)); } });
View 属性改变引起的动画
这个就有点变态了,你更改了空间的layout属性,告诉Transtion一声,他就做成动画反馈在界面上。听起来就很叼!
实现起来非常非常的简单,简单到想哭。
private void changeLayout() { // 声明,由Transition接管界面变化的意思,这里的viewRoot相当于根视图, // 也就是说,想改变layout布局的view,父类要先被接管 TransitionManager.beginDelayedTransition(viewRoot); ViewGroup.LayoutParams params = square.getLayoutParams(); if (sizeChanged) { params.width = savedWidth; } else { savedWidth = params.width; params.width = 200; } sizeChanged = !sizeChanged; square.setLayoutParams(params); }private void changePosition() { TransitionManager.beginDelayedTransition(viewRoot); LinearLayout.LayoutParams lp = (LinearLayout.LayoutParams) square.getLayoutParams(); if (positionChanged) { lp.gravity = Gravity.CENTER; } else { lp.gravity = Gravity.LEFT; } positionChanged = !positionChanged; square.setLayoutParams(lp); }
相信,介于此,5.0后的android上很多用户体验能提升一大截。
Reveal,揭示动画
这也是相当符合设计一种动画,通过一点引发触动(可以是接触点,也可以位移后触动),然后由点及面,揭示(展现)内容。
一般都是通过shared elements一起组合使用,完成界面跳转。
Circular Reveal 即通过原点,然后由点及面逐步揭示,而且还有一个弧度,视觉体验感非常好。
这里要用到ViewAnimationUtils
类(这个类里承载了很多动画,非常还用!)
// 揭示原点,这里的点取的是控件的中点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();
如果不是由控件引起的,而是由点击(其实就是触摸)引起的,只需要传入接触点的x,y坐标就好了。
@Overridepublic 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(); }
刚才提到的改变layout属性,就可以改变动画其实也是可以用到圆揭示:
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);
总结
写的比较简单,很多东西还是对API的熟悉和使用。
Material 正在改变Android,相信以后的UI无论从实感还是物理感,都会更加贴合用户,贴近使用。