Transition的使用分析

Transition的使用分析Transition 的使用分析 基于使用的角度稍作分析

Transition

概念

Transition中,有一些新的概念,例如Scene, Transition, TransitionSet, TransitionManager. 除了Scene, 其他的概念其实和简单的View动画差不多。

Scene

Scene的构造

Scene是一个新的概念,它被翻译成了“场景”,这个描述很准确,但是比较难以理解。下面通过源码分析

 / * Constructs a Scene which, when entered, will remove any * children from the sceneRoot container and add the layout * object as a new child of that container. * * @param sceneRoot The root of the hierarchy in which scene changes * and transitions will take place. * @param layout The view hierarchy of this scene, added as a child * of sceneRoot when this scene is entered. */ public Scene(ViewGroup sceneRoot, View layout) { mSceneRoot = sceneRoot; mLayout = layout; }

下面是Scene的另一个构造器

 / * Constructs a Scene which, when entered, will remove any * children from the sceneRoot container and will inflate and add * the hierarchy specified by the layoutId resource file. * * 

This method is hidden because layoutId-based scenes should be * created by the caching factory method {@link Scene#getCurrentScene(View)}.

* * @param sceneRoot The root of the hierarchy in which scene changes * and transitions will take place. * @param layoutId The id of a resource file that defines the view * hierarchy of this scene. * @param context The context used in the process of inflating * the layout resource. */
private Scene(ViewGroup sceneRoot, int layoutId, Context context) { mContext = context; mSceneRoot = sceneRoot; mLayoutId = layoutId; }

注释:构造一个场景,当进入这个场景时,将从场景的sceneRoot中移除其他所有的children, 并inflate和添加由layoutId指定的资源文件的view层。 这是一个私有的构造器,因为基于layoutId构造的场景,使用了缓存,即可以进行多次复用。

下面是会调用上面的私有构造器的一个静态方法

 / * Returns a Scene described by the resource file associated with the given * layoutId parameter. If such a Scene has already been created for * the given sceneRoot, that same Scene will be returned. * This caching of layoutId-based scenes enables sharing of common scenes * between those created in code and those referenced by {@link TransitionManager} * XML resource files. * * @param sceneRoot The root of the hierarchy in which scene changes * and transitions will take place. * @param layoutId The id of a standard layout resource file. * @param context The context used in the process of inflating * the layout resource. * @return The scene for the given root and layout id */ public static Scene getSceneForLayout(ViewGroup sceneRoot, int layoutId, Context context) { SparseArray 
  
    scenes = (SparseArray 
   
     ) sceneRoot.getTag( com.android.internal.R.id.scene_layoutid_cache); 
    if (scenes == 
    null) { scenes = 
    new SparseArray 
    
      (); sceneRoot.setTagInternal(com.android.internal.R.id.scene_layoutid_cache, scenes); } Scene scene = scenes.get(layoutId); 
     if (scene != 
     null) { 
     return scene; } 
     else { scene = 
     new Scene(sceneRoot, layoutId, context); scenes.put(layoutId, scene); 
     return scene; } } 
     
    
  
Scene的分析

下面是Scene.enter()方法

 / * Enters this scene, which entails changing all values that * are specified by this scene. These may be values associated * with a layout view group or layout resource file which will * now be added to the scene root, or it may be values changed by * an {@link #setEnterAction(Runnable)} enter action}, or a * combination of the these. No transition will be run when the * scene is entered. To get transition behavior in scene changes, * use one of the methods in {@link TransitionManager} instead. */ public void enter() { // Apply layout change, if any if (mLayoutId > 0 || mLayout != null) { // empty out parent container before adding to it getSceneRoot().removeAllViews(); if (mLayoutId > 0) { LayoutInflater.from(mContext).inflate(mLayoutId, mSceneRoot); } else { mSceneRoot.addView(mLayout); } } // Notify next scene that it is entering. Subclasses may override to configure scene. if (mEnterAction != null) { mEnterAction.run(); } setCurrentScene(mSceneRoot, this); }

下面是Scene.exit()方法

 / * Exits this scene, if it is the current scene * on the scene's {@link #getSceneRoot() scene root}. The current scene is * set when {@link #enter() entering} a scene. * Exiting a scene runs the {@link #setExitAction(Runnable) exit action} * if there is one. */ public void exit() { if (getCurrentScene(mSceneRoot) == this) { if (mExitAction != null) { mExitAction.run(); } } }

Transition

简单分析:Transition是一个抽象类,保留了两个抽象方法由子类实现captureStartValues(TransitionValues transitionValues)captureEndValues(TransitionValues transitionValues). 而Transition有一个常见的子类Visibility, Android中已经有的很多Transition的子类都是继承自这个Visibility.

直接继承自Transition的类:ChangeBounds, ChangeClipBounds, ChangeImageTransform, ChangeScroll, ChangeTransform, TransitionSet

继承自Visibility的类:Explode, Fade, Slide








下面是Visibility的注释

/ * This transition tracks changes to the visibility of target views in the * start and end scenes. Visibility is determined not just by the * {@link View#setVisibility(int)} state of views, but also whether * views exist in the current view hierarchy. The class is intended to be a * utility for subclasses such as {@link Fade}, which use this visibility * information to determine the specific animations to run when visibility * changes occur. Subclasses should implement one or both of the methods * {@link #onAppear(ViewGroup, TransitionValues, int, TransitionValues, int)}, * {@link #onDisappear(ViewGroup, TransitionValues, int, TransitionValues, int)} or * {@link #onAppear(ViewGroup, View, TransitionValues, TransitionValues)}, * {@link #onDisappear(ViewGroup, View, TransitionValues, TransitionValues)}. */ public abstract class Visibility extends Transition { 
    // 省略... }

注释:这个transition跟踪目标view在场景开始和场景结束时visibility的变化。Visibility不仅仅是决定view的visibility状态, 也决定了view是否存在在当前sceneRoot的view层次中。使用这个类想为其子类作为一个工具,例如Fade这种,使用view的visibility信息来决定当visibility发生变化时特定动画的运行。子类应该实现其中一个或成组的两个方法{@link #onAppear(ViewGroup, TransitionValues, int, TransitionValues, int)}, {@link #onDisappear(ViewGroup, TransitionValues, int, TransitionValues, int)}{@link #onAppear(ViewGroup, View, TransitionValues, TransitionValues)},{@link #onDisappear(ViewGroup, View, TransitionValues, TransitionValues)}.

下面是Transition的注释

/ * A Transition holds information about animations that will be run on its * targets during a scene change. Subclasses of this abstract class may * choreograph several child transitions ({@link TransitionSet} or they may * perform custom animations themselves. Any Transition has two main jobs: * (1) capture property values, and (2) play animations based on changes to * captured property values. A custom transition knows what property values * on View objects are of interest to it, and also knows how to animate * changes to those values. For example, the {@link Fade} transition tracks * changes to visibility-related properties and is able to construct and run * animations that fade items in or out based on changes to those properties. * * 

Note: Transitions may not work correctly with either {@link SurfaceView} * or {@link TextureView}, due to the way that these views are displayed * on the screen. For SurfaceView, the problem is that the view is updated from * a non-UI thread, so changes to the view due to transitions (such as moving * and resizing the view) may be out of sync with the display inside those bounds. * TextureView is more compatible with transitions in general, but some * specific transitions (such as {@link Fade}) may not be compatible * with TextureView because they rely on {@link ViewOverlay} functionality, * which does not currently work with TextureView.

* *

Transitions can be declared in XML resource files inside the res/transition * directory. Transition resources consist of a tag name for one of the Transition * subclasses along with attributes to define some of the attributes of that transition. * For example, here is a minimal resource file that declares a {@link ChangeBounds} transition: * * {@sample development/samples/ApiDemos/res/transition/changebounds.xml ChangeBounds} * *

This TransitionSet contains {@link android.transition.Explode} for visibility, * {@link android.transition.ChangeBounds}, {@link android.transition.ChangeTransform}, * and {@link android.transition.ChangeClipBounds} and * {@link android.transition.ChangeImageTransform}:

* * {@sample development/samples/ApiDemos/res/transition/explode_move_together.xml MultipleTransform} * *

Custom transition classes may be instantiated with a transition tag:

*
 
    

*

Custom transition classes loaded from XML should have a public constructor taking * a {@link android.content.Context} and {@link android.util.AttributeSet}.

* *

Note that attributes for the transition are not required, just as they are * optional when declared in code; Transitions created from XML resources will use * the same defaults as their code-created equivalents. Here is a slightly more * elaborate example which declares a {@link TransitionSet} transition with * {@link ChangeBounds} and {@link Fade} child transitions:

* * {@sample * development/samples/ApiDemos/res/transition/changebounds_fadeout_sequential.xml TransitionSet} * *

In this example, the transitionOrdering attribute is used on the TransitionSet * object to change from the default {@link TransitionSet#ORDERING_TOGETHER} behavior * to be {@link TransitionSet#ORDERING_SEQUENTIAL} instead. Also, the {@link Fade} * transition uses a fadingMode of {@link Fade#OUT} instead of the default * out-in behavior. Finally, note the use of the targets sub-tag, which * takes a set of {@link android.R.styleable#TransitionTarget target} tags, each * of which lists a specific targetId, targetClass, * targetName, excludeId, excludeClass, or * excludeName, which this transition acts upon. * Use of targets is optional, but can be used to either limit the time spent checking * attributes on unchanging views, or limiting the types of animations run on specific views. * In this case, we know that only the grayscaleContainer will be * disappearing, so we choose to limit the {@link Fade} transition to only that view.

* * Further information on XML resource descriptions for transitions can be found for * {@link android.R.styleable#Transition}, {@link android.R.styleable#TransitionSet}, * {@link android.R.styleable#TransitionTarget}, {@link android.R.styleable#Fade}, * {@link android.R.styleable#Slide}, and {@link android.R.styleable#ChangeTransform}. * */

public abstract class Transition implements Cloneable {
// 省略... }

TransitionManager

目前还不清楚TransitionManager的具体管理Transition的方式是什么。就使用而言,除了可以在代码中直接构造,还可以通过在xml中定义的方式来获取。

// transition-v21/manager <transitionManager xmlns:android="http://schemas.android.com/apk/res/android"> <transition  android:fromScene="@layout/card_one" android:toScene="@layout/card_two" android:transition="@transition/decrease"/> <transition  android:fromScene="@layout/card_two" android:toScene="@layout/card_one" android:transition="@android:transition/move"/>  
   transitionManager> // transition-v21/descrease <transitionSet xmlns:android="http://schemas.android.com/apk/res/android"> <fade android:fadingMode="fade_out"> <targets android:targetId="@id/red"/>  
   fade> <fade android:fadingMode="fade_out"> <targets android:targetId="@id/blue"/>  
   fade> <changeBounds> <targets android:targetId="@id/green"/>  
   changeBounds> <changeBounds> <targets android:targetId="@id/yellow"/>  
   changeBounds>  
   transitionSet> // layout/card_one <RelativeLayout  xmlns:android="http://schemas.android.com/apk/res/android" android:layout_width="wrap_content" android:layout_height="wrap_content"> <TextView  android:id="@+id/red" android:layout_width="120dp" android:layout_height="120dp" android:background="@android:color/holo_red_light"/> <TextView  android:id="@+id/blue" android:layout_width="120dp" android:layout_height="120dp" android:layout_toEndOf="@id/red" android:background="@android:color/holo_blue_bright"/> <TextView  android:id="@+id/green" android:layout_width="120dp" android:layout_height="120dp" android:layout_below="@id/red" android:background="@android:color/holo_green_light"/> <TextView  android:id="@+id/yellow" android:layout_width="120dp" android:layout_height="120dp" android:layout_alignTop="@id/green" android:layout_toEndOf="@id/green" android:background="@android:color/holo_orange_light"/>  
   RelativeLayout> // layout/card_two <RelativeLayout  xmlns:android="http://schemas.android.com/apk/res/android" android:layout_width="wrap_content" android:layout_height="wrap_content"> <TextView  android:id="@+id/green" android:layout_width="120dp" android:layout_height="120dp" android:background="@android:color/holo_green_light"/> <TextView  android:id="@+id/yellow" android:layout_width="120dp" android:layout_height="120dp" android:layout_alignTop="@id/green" android:layout_toEndOf="@id/green" android:background="@android:color/holo_orange_light"/>  
   RelativeLayout>

如上,在xml中定义一个TransitionManager, 在transitionManager中可以定义多个transition .

 if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.KITKAT) { TransitionManager transitionManager = TransitionInflater.from(this).inflateTransitionManager(R.transition.manger, mContainer); if (isFirst) { isFirst = false; transitionManager.transitionTo(Scene.getSceneForLayout(mContainer, R.layout.card_one, this)); } else { isFirst = true; transitionManager.transitionTo(Scene.getSceneForLayout(mContainer, R.layout.card_two, this)); } }

如上,在代码中inflate出在xml中定义的TransitionManager, 然后进行动画。

这里可以看一下TransitionInflater.inflateTransitionManager方法进行inflate的过程

private TransitionManager createTransitionManagerFromXml(XmlPullParser parser, AttributeSet attrs, ViewGroup sceneRoot) throws XmlPullParserException, IOException { // Make sure we are on a start tag. int type; int depth = parser.getDepth(); TransitionManager transitionManager = null; while (((type=parser.next()) != XmlPullParser.END_TAG || parser.getDepth() > depth) && type != XmlPullParser.END_DOCUMENT) { if (type != XmlPullParser.START_TAG) { continue; } String name = parser.getName(); if (name.equals("transitionManager")) { transitionManager = new TransitionManager(); } else if (name.equals("transition") && (transitionManager != null)) { loadTransition(attrs, sceneRoot, transitionManager); } else { throw new RuntimeException("Unknown scene name: " + parser.getName()); } } return transitionManager; } private void loadTransition(AttributeSet attrs, ViewGroup sceneRoot, TransitionManager transitionManager) throws Resources.NotFoundException { TypedArray a = mContext.obtainStyledAttributes(attrs, R.styleable.TransitionManager); int transitionId = a.getResourceId(R.styleable.TransitionManager_transition, -1); int fromId = a.getResourceId(R.styleable.TransitionManager_fromScene, -1); Scene fromScene = (fromId < 0) ? null: Scene.getSceneForLayout(sceneRoot, fromId, mContext); int toId = a.getResourceId(R.styleable.TransitionManager_toScene, -1); Scene toScene = (toId < 0) ? null : Scene.getSceneForLayout(sceneRoot, toId, mContext); if (transitionId >= 0) { Transition transition = inflateTransition(transitionId); if (transition != null) { if (toScene == null) { throw new RuntimeException("No toScene for transition ID " + transitionId); } if (fromScene == null) { transitionManager.setTransition(toScene, transition); } else { transitionManager.setTransition(fromScene, toScene, transition); } } } a.recycle(); }

分析:在inflate TransitionManager的过程中,实际上是直接构造TransitionManger的。对其中的transition标签进行处理,也是调用TransitionManager.setTransition(Scene, Scene, Transiton)方法。这个方法有一个重载方法。在上面可以看到,toScene是必须存在的,如果fromScene存在,则调用TransitionManager.setTransition(Scene, Scene, Transiton), 否则调用TransitionManager.setTransition(Scene, Transiton).

我们可以从中看到什么呢?这个需要从TransitionManager.transitionTo(Scene)中看出来




/ * Change to the given scene, using the * appropriate transition for this particular scene change * (as specified to the TransitionManager, or the default * if no such transition exists). * * @param scene The Scene to change to */ public void transitionTo(Scene scene) { // Auto transition if there is no transition declared for the Scene, but there is // a root or parent view changeScene(scene, getTransition(scene)); }

使用

TransitionManager.beginDelayedTransition(ViewGroup sceneRoot, Transition transition)

很大一部分动画其实都可以使用这个方法来进行动画。这个方法有一个重载方法TransitionManager.beginDelayedTransition(ViewGroup sceneRoot), 在这个重载方法中,使用一个默认的AutoTransition.

 public void transition(View view) { if (android.os.Build.VERSION.SDK_INT < Build.VERSION_CODES.KITKAT) { return; } final ViewGroup parent = (ViewGroup) findViewById(R.id.parent); final ViewGroup container = (ViewGroup) findViewById(R.id.container); TransitionManager.beginDelayedTransition(parent, new ChangeBounds()); container.removeViewAt(0); container.removeViewAt(0); }

如上,有一个id为parent的ViewGroup, 其中有一个id为container的ViewGroup, 在container中,还有多个children, 我们以id为parent的ViewGroup作为sceneRoot, 在改变其hirerarchy前调用TransitionManager.beginDelayedTransition(ViewGroup sceneRoot, Transition transition), 然后对其hirerarchy进行操作, 这样就完成了动画的操作。

这里需要说明一下,我们以id为parent的ViewGroup作为sceneRoot, 而操作的并不是它的直接children, 也产生了效果,说明操作的是以sceneRoot为根的整个hirerarchy.

对于原理,这里暂时放在后面进行分析。








TransitionManager.go(Scene, Transition)

 @TargetApi(Build.VERSION_CODES.LOLLIPOP) public void remove(View view) { final ViewGroup group = (ViewGroup) findViewById(R.id.container); final Scene scene = new Scene(group, group.getChildAt(0)); TransitionManager.go(scene); }

如上所示,我们将一个id为container的ViewGroup作为sceneRoot, 然后将其第一个child作为场景切换后添加到sceneRoot中的view, 然后调用TransitionManager.go(Scene)方法。当然,这样达到的效果和使用TransitionManager.beginDelayedTransition(ViewGroup sceneRoot, Transition transition)的效果是一样的。我们也可以像上面一样使用,保留id为container的第一个child, 移除其他所有的children, 在这些操作之前调用TransitionManager.beginDelayedTransition(ViewGroup sceneRoot, Transition transition)“`方法即可。

TransitionManager.transitionTo(Scene)

在上面我们已经演示了这个方法的使用。这里就不再赘述。

总结

参考

使用 Transition API为安卓应用创建动画

版权声明:本文内容由互联网用户自发贡献,该文观点仅代表作者本人。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如发现本站有涉嫌侵权/违法违规的内容, 请联系我们举报,一经查实,本站将立刻删除。

发布者:全栈程序员-站长,转载请注明出处:https://javaforall.net/218092.html原文链接:https://javaforall.net

(0)
上一篇 2026年3月18日 上午8:03
下一篇 2026年3月18日 上午8:04


相关推荐

  • Redis的五种数据结构的底层实现原理

    Redis的五种数据结构的底层实现原理

    2021年4月10日
    123
  • Pytest(15)pytest分布式执行用例[通俗易懂]

    Pytest(15)pytest分布式执行用例[通俗易懂]前言平常我们功能测试用例非常多时,比如有1千条用例,假设每个用例执行需要1分钟,如果单个测试人员执行需要1000分钟才能跑完当项目非常紧急时,会需要协调多个测试资源来把任务分成两部分,于是执行时间

    2022年7月29日
    12
  • 计算机设备问题代码43,设备管理器错误代码(代码43)的六种解决方法

    内容一、“由于此设备存在问题,Windows已将其停止(代码43)”),这是问题的原因原因分析:代码43错误是多个设备管理器错误代码之一。当设备管理器停止硬件设备时,会生成此错误,这可能是由硬件设备或设备驱动程序故障引起的。设备管理器错误代码(代码43)的详细信息可以在设备属性的“设备状态”区域中找到。引起问题的设备将在设备中用感叹号标记)设备管理器,如下图所示:有关如何解决此问题的信息,…

    2022年4月4日
    215
  • Java:Map转List (用stream实现)

    Java:Map转List (用stream实现)例子 Map Integer String mapDemo newHashMap lt gt map put 10 apple map put 20 orange map put 30 banana map put 40 watermelon map put 50 dragonfruit 一 Map Integer Object 转 List amp l Integer Object Integer String

    2026年3月16日
    1
  • Cts框架解析(12)-ITargetPreparer

    Cts框架解析(12)-ITargetPreparer

    2022年2月4日
    55
  • 前端低代码调研与总结

    近些年来,低代码的概念逐渐流行了起来,而低代码产品也越来越多的出现在我们的身边。低代码可以叫做可视化搭建,或者叫效能工具等等。像国外的Mendix,国内的宜搭、苍穹、简道云、amis等等。基于这种新型的开发方式,图形化的拖拉拽配置界面,并兼容了自定义的组件、代码扩展,确实在B端后台管理类网站建设中很大程度上的提升了效率。低代码平台能够高效且便捷,成本又低。就应用领域来讲已经很广泛了,例如营销领域,各种页面生产工具,非冰,乐高,宜搭,鲁班。还有电商类的公司都会给商家提供一个类似店铺装修的工具,小程序生产工具

    2022年4月13日
    51

发表回复

您的邮箱地址不会被公开。 必填项已用 * 标注

关注全栈程序员社区公众号