Android开源:Material-Animations - 过场动画
xckz1777
8年前
<h2>Material-Animaltion</h2> <p>这是GitHub上的一个开源项目, 演示View的平移、缩放动画,activity进入和退出动画,界面间元素共享。</p> <h2>Andorid Transitions Framework</h2> <p>作用</p> <ul> <li> <p>可以在activity之间跳转的时候添加动画</p> </li> <li> <p>动画共享元素之间的转换活动</p> </li> <li> <p>activity中布局元素的过渡动画。</p> </li> </ul> <h2>1. Transitions between Activitys</h2> <ul> <li>Animate existing activity layout content</li> </ul> <p style="text-align:center"><img src="https://simg.open-open.com/show/980e3445d4b03bae04fb5199898e3d57.png"></p> <p>当过渡从activity到activity内容布局是根据定义的过渡动画。有三个预定义的转换android.transition上可用。转换可以使用:Explode,Slide和Fade。所有这些转换跟踪更改目标的可见性活动视图布局和动画那些观点遵循转换规则。</p> <table> <thead> <tr> <th>Explode</th> <th>Slide</th> <th>Fade</th> </tr> </thead> <tbody> <tr> <td><img src="https://simg.open-open.com/show/953043508ec0675668ebb5d421b352aa.gif"></td> <td><img src="https://simg.open-open.com/show/f5550f38e4efed7415e9d6645976c9dd.gif"></td> <td><img src="https://simg.open-open.com/show/87a2d447822aac140fdd2f02d48aa57a.gif"></td> </tr> </tbody> </table> <p>现这些效果可以通过xml方式或者在直接在类中实现,下面是Fade的实现方式。</p> <p>### 说明:Fade</p> <ol> <li>如果是xml方式实现,首先在/res下创建transition文件夹。</li> </ol> <p>res/transition/slide_from_right</p> <pre> <code class="language-java"><?xml version = 1.0 encoding = "utf-8"?> <transitionSet xmls:android = "http://schemas.android.com/apk/res/android"> <slide duration = "500" slideEage = "left"/> </transitionSet></code></pre> <p>2.如果直接用代码实现,可以如下实现</p> <p>MainActivity</p> <pre> <code class="language-java">Slide slideTracition = newSlide(); slideTracition.setSlideEdge(Gravity.LEFT); slideTracition.setDuration(getResources().getInteger(R.integer.anim_duration_long));</code></pre> <ol> <li>实现</li> </ol> <p>MianActivity.onCreat();</p> <p>设置MainActivty的进出动画代码如下</p> <pre> <code class="language-java">private void setupWindowAnimations() { Transition slideTracition = TransitionInflater.from(this).inflateTransition(R.transition.slide_from_left); getWindow().setEnterTransition(slideTracition); getWindow().setExitTransition(slideTracition); //getWindow().setReenterTransition(buildExitTransition()); }</code></pre> <ol> <li>分析Fade的步骤是怎么发生的 <ol> <li>ActivatyA 启动 ActivityB</li> <li>Transition Framework找到一个ExitTransition,并将其应用于所有可见视图。</li> <li>在返回之前过渡框架执行进入和退出分别反向动画(如果我们定义的输出returnTransition和reenterTransition,这些都已经转而执行)</li> </ol> </li> </ol> <h3>ReturnTransition&ReenterTransition</h3> <p>返回和重新输入转换分别是Enter和Exit的反向动画。</p> <ul> <li>EnterTransition < - > ReturnTransition</li> <li>ExitTransition < - > ReenterTransition</li> </ul> <p>如果未定义返回或重新输入,Android将按照你之前设定的默认的版本。但是如果你定义它们,你可以有不同的转换进入和退出活动。(如下图)</p> <p style="text-align:center"><img src="https://simg.open-open.com/show/e7c0bb918deeb87d6920a2daa7f2f69b.png"></p> <ul> <li>当你从ActivityB返回到ActivityA的时候。需要重新制定ActivityB的退出动画,可以通过如下方式</li> </ul> <pre> <code class="language-java">Visiable slide = new Slide(); slide.setDuraing(500); getWindow.setReturnTransition(slide); //别直接调用finish(); finshAfterTransition();</code></pre> <p>效果图</p> <table> <thead> <tr> <th>Without Return Transition</th> <th>With Return Transition</th> </tr> </thead> <tbody> <tr> <td><img src="https://simg.open-open.com/show/87a2d447822aac140fdd2f02d48aa57a.gif" alt="Android开源:Material-Animations - 过场动画" width="368" height="654"></td> <td><img src="https://simg.open-open.com/show/0019673badd6730bad261b11951a4d48.gif"></td> </tr> </tbody> </table> <h2>2.Share elements between Activity(元素共享)</h2> <p>共享元素过度动画的背后是通过过度动画将两个不同布局中的不同view关联起来。Transition框架知道用适当的动画向用户展示从一个view向另外 一个view过度。请记住:共享元素过度的过程中,view并没有真正从一个布局跑到另外一个布局,整个过程基本都是在后一个布局中完成的。</p> <p style="text-align:center"><img src="https://simg.open-open.com/show/82c2047b6a7a5ba2c0b0a8802aaeae7d.png"></p> <h3>a) 允许过度动画</h3> <p>需要在/res/style.xml添加</p> <pre> <code class="language-java"><item name="android:windowContentTransitions">true</item></code></pre> <h3>b) 在对应的xml文件指定TransitionName属性</h3> <p>res/layout/activity</p> <ul> <li>指定ImageView和TextView</li> </ul> <p>注意:这里必须为共享的俩个元素指定同一个TransitionName,不然不会出现共享效果</p> <pre> <code class="language-java"><ImageView android:id="@+id/square_blue" style="@style/MaterialAnimations.Icon.Big" android:src="@drawable/circle_24dp" android:transitionName="@string/square_blue_name" /></code></pre> <pre> <code class="language-java"><TextView android:id="@+id/title" style="@style/MaterialAnimations.TextAppearance.Title.Invers" android:layout_width="wrap_content" android:layout_height="wrap_content" android:layout_gravity="center_vertical|start" android:text="@{sharedSample.name}" android:transitionName="@string/sample_blue_title" /></code></pre> <h3>c) 启动Activity</h3> <pre> <code class="language-java">Intent intent = new Intent(activity,target); ActivityOptionCompat option = ActiviyoptionCampat.makeSceneTransitionAnimation(activity, new Pair<View, String>(viewHolder.binding.sampleIcon, activity.getString(R.string.square_blue_name)), new Pair<View, String>(viewHolder.binding.sampleName, activity.getString(R.string.sample_blue_title))); startActivity(intent,option.toBundle());</code></pre> <p>这段代码将生成这个美丽的过渡动画:</p> <p style="text-align:center"><img src="https://simg.open-open.com/show/cfe68086932fa14146d04d097da6949f.gif"></p> <h2>那么在Fragment之间怎么实现呢?</h2> <h3>a) 允许过度动画</h3> <p>需要在/res/style.xml添加</p> <pre> <code class="language-java"><item name="android:windowContentTransitions">true</item></code></pre> <h3>b) 在对应的xml文件指定TransitionName属性</h3> <p>之前的俩个步骤和Activity之间共享元素的没有太大差别。</p> <h3>c) 通过Shared Element方式启动fragment</h3> <pre> <code class="language-java">private void addNextFragment(Sample sample, ImageView blue, boolean b) { SharedElementFragment2 elementFragment2 = SharedElementFragment2.newInstance(sample); Slide slide = new Slide(); slide.setDuration(getResources().getInteger(R.integer.anim_duration_medium)); slide.setSlideEdge(Gravity.RIGHT); ChangeBounds changeBounds = new ChangeBounds(); changeBounds.setDuration(getResources().getInteger(R.integer.anim_duration_medium)); elementFragment2.setEnterTransition(slide); elementFragment2.setAllowEnterTransitionOverlap(b); elementFragment2.setAllowReturnTransitionOverlap(b); elementFragment2.setSharedElementEnterTransition(changeBounds); getFragmentManager().beginTransaction().replace(R.id.sample2_content, elementFragment2).addToBackStack(null).addSharedElement(blue, getString(R.string.square_blue_name)).commit(); }</code></pre> <p>效果如下</p> <p style="text-align:center"><img src="https://simg.open-open.com/show/71f0c6cdfea1c825fbe338d7ab0c378e.gif"></p> <h2>3.动画视图布局元素</h2> <p>上面俩种方式都是运用于过度动画,那Transition FrameWork也可以被用做于改变布局中的某个特定的View,比如修改View的位置或者大小。我们需要确定想改变的结果即可。</p> <h3>1.我们需要告诉framework我们需要改动界面的Ui</h3> <pre> <code class="language-java">TransitionManager.beginDelayedTransition(viewRoot);</code></pre> <ul> <li>这里的viewRoot是需要改变view当前所在的根布局</li> </ul> <h3>2.改变View的属性</h3> <ul> <li>改变大小</li> </ul> <pre> <code class="language-java">ViewGroup.LayoutParams params = view.getLayoutParams(); params.witdh = 200; view.setlayoutParams(params);</code></pre> <ul> <li>改变位置</li> </ul> <pre> <code class="language-java">ViewGroup.LayoutParams params = view.getLayoutParams(); params.gravity = Gravity.Left; view.setlayoutParams(params);</code></pre> <table> <thead> <tr> <th>Size</th> <th>Position</th> </tr> </thead> <tbody> <tr> <td><img src="https://simg.open-open.com/show/6aeafab77cd0091cf7326b4a1cb068a9.gif"></td> <td><img src="https://simg.open-open.com/show/6c1f6fba0f3a4d138bb78799e70ed096.gif"></td> </tr> </tbody> </table> <h2>4.共享元素+循环动画</h2> <p>循环显示只是一个动画显示或隐藏一组UI元素。它可以自API21或以上使用。</p> <p style="text-align:center"><img src="https://simg.open-open.com/show/ddd9e5abef07bcaaea07f251b779e90a.gif"></p> <p>上图发生了几个步骤?了解了之前的共享元素后我们知道。</p> <ul> <li>黄色的小球共享于MainActivity和RevealActivity。</li> <li>当共享动画结束之后,在RevealActivity中发生了俩组动画效果 <ul> <li>Toolbar上</li> <li>底部四个球执行了特定的动画</li> </ul> </li> </ul> <p>那我们应该如何监听共享元素的动画结束的时刻。</p> <pre> <code class="language-java">private void setupEnterAnimations() { Transition transtion = TransitionInflater.from(this).inflateTransition(R.transition.changebounds_with_arcmotion); getWindow().setSharedElementEnterTransition(transtion); transtion.addListener(new Transition.TransitionListener() { @Override public void onTransitionEnd(Transition transition) { transition.removeListener(this); hideTarget(); animateRevealShow(toolbar); animateButtonIn(); } }); }</code></pre> <p>res/transition/changebounds_with_arcmotion.xml</p> <pre> <code class="language-java"><?xml version="1.0" encoding="utf-8"?> <transitionSet xmlns:android="http://schemas.android.com/apk/res/android" android:duration="@integer/anim_duration_long" android:interpolator="@android:interpolator/decelerate_cubic" > <changeBounds> <arcMotion android:maximumAngle="90" android:minimumHorizontalAngle="90" android:minimumVerticalAngle="0"/> </changeBounds> </transitionSet></code></pre> <p>ToolBar Animation</p> <pre> <code class="language-java">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(); }</code></pre> <p>四个小球的浮现的动画</p> <pre> <code class="language-java">private void animateButtonIn() { for (int i = 0; i < bgViewGroup.getChildCount(); i++) { View child = bgViewGroup.getChildAt(i); child.animate() .setStartDelay(100 + i * DELAY) .setInterpolator(new AccelerateInterpolator()) .alpha(1) .scaleX(1) .scaleY(1); } }</code></pre> <h3>另一种效果</h3> <p>红色小球的效果</p> <p style="text-align:center"><img src="https://simg.open-open.com/show/727ddc44745e1a2df57fc8fe5803ff8d.gif"></p> <p>红球运动轨迹 res/transition/changebounds_with_arcmotion.xml(这里不是共享动画)</p> <p>res/transition/changebounds_with_arcmotion.xml</p> <pre> <code class="language-java"><?xml version="1.0" encoding="utf-8"?> <transitionSet xmlns:android="http://schemas.android.com/apk/res/android" android:duration="@integer/anim_duration_long" android:interpolator="@android:interpolator/decelerate_cubic" > <changeBounds> <arcMotion android:maximumAngle="90" android:minimumHorizontalAngle="90" android:minimumVerticalAngle="0"/> </changeBounds> </transitionSet></code></pre> <p>点击红色小球时 执行 revealRed()</p> <ul> <li> </li> </ul> <pre> <code class="language-java">private void revealRed() { final ViewGroup.LayoutParams params = btnRed.getLayoutParams(); Transition transition = TransitionInflater.from(this).inflateTransition(R.transition.changebounds_with_arcmotion); transition.addListener(new Transition.TransitionListener() { @Override public void onTransitionStart(Transition transition) { } @Override public void onTransitionEnd(Transition transition) { animateRevealColor(bgViewGroup, R.color.sample_red); body.setText(R.string.reveal_body3); body.setTextColor(ContextCompat.getColor(RevealActivity.this, R.color.theme_red_background)); btnRed.setLayoutParams(params); } @Override public void onTransitionCancel(Transition transition) { } @Override public void onTransitionPause(Transition transition) { } @Override public void onTransitionResume(Transition transition) { } }); TransitionManager.beginDelayedTransition(bgViewGroup, transition); final RelativeLayout.LayoutParams relativeRarams = new RelativeLayout.LayoutParams(ViewGroup.LayoutParams.WRAP_CONTENT, ViewGroup.LayoutParams.WRAP_CONTENT); relativeRarams.addRule(RelativeLayout.CENTER_IN_PARENT); btnRed.setLayoutParams(relativeRarams); } private void animateRevealColor(ViewGroup viewRoot, @ColorRes int color) { int cx = (viewRoot.getLeft() + viewRoot.getRight()) / 2; int cy = (viewRoot.getTop() + viewRoot.getBottom()) / 2; animateRevealColorFromCoordinates(viewRoot, color, cx, cy); } private Animator animateRevealColorFromCoordinates(ViewGroup root, @ColorRes int color, int cx, int cy) { int finalRadius = Math.max(root.getWidth(), root.getHeight()); final Animator animator = ViewAnimationUtils.createCircularReveal(root, cx, cy, 0, finalRadius); root.setBackgroundColor(ContextCompat.getColor(this, color)); animator.setDuration(getResources().getInteger(R.integer.anim_duration_long)); animator.setInterpolator(new AccelerateInterpolator()); animator.start(); return animator; }</code></pre> <h2>更多信息</h2> <ul> <li>如果你想深入了解和学习更多的有关于过度动画(Transition Framework)的内容。可以访问 Alex Lockwood 的帖子 <a href="/misc/goto?guid=4959737694307800702" rel="nofollow,noindex">http : //www.androiddesignpatterns.com/2014/12/activity-fragment-transitions-in-android-lollipop-part1.html</a></li> <li>惊人的存储库与许多材料设计样本由Saul Molinero: <a href="/misc/goto?guid=4959737694386667529" rel="nofollow,noindex">https://github.com/saulmm/Android-Material-Examples</a></li> <li>Chet Hasse视频详细讲解了过渡框架: <a href="/misc/goto?guid=4959624983912744370" rel="nofollow,noindex">https://www.油Tube.com/watch?v=S3H7nJ4QaD8</a></li> </ul> <p> </p>