zoukankan      html  css  js  c++  java
  • 札记:翻译-使用Scene和Transition实现【场景切换】动画效果

    简述:transitions framework

    下面翻译transition为“过渡”,强调动画过程的含义,不过更多时候使用transition单词本身。
    Android 4.4.2 (API level 19) 引入了过渡框架,它用来在两个view hierarchies(就是ViewGroup实例)切换时执行改变动画。它通过动态修改views对象的某些property值来实现动画,实际上就是用的属性动画。框架内建了一些transition效果,也可以自定义。并且可以监听transition执行过程/生命周期(Lifecycle callbacks)各种回调。

    框架概览

    在UI改变时——view hierarchy(视图层级) 发生变化,过渡框架对包含的部分改变了的view对象执行属性动画
    来表达这样的视觉提示(visual cues)。

    特性

    过渡框架包括下面这些特性:

    • 动画组Group-level animations
      视图层级发生变化时,可以同时执行多个动画,它们形成动画组,类似AnimationSet

    • Transition-based animation
      Runs animations based on the changes between starting and ending view property values.

    • Built-in animations
      Includes predefined animations for common effects such as fade out or movement.

    • Resource file support 支持资源文件方式的使用
      Loads view hierarchies and built-in animations from layout resource files.

    • Lifecycle callbacks
      Defines callbacks that provide finer control over the animation and hierarchy change process.

    框架结构

    下图是过渡框架涉及的相关概念和之间的关系:

    Relationships in the transitions framework

    • Scene
      一个Scene表示一个view hierarchy,存储对应的views及其property值。
      一个Scene必须关联到一个scene-root,它是一个ViewGroup,正是对应view hierarchy要添加到的容器。
      Scene可以从layout文件创建(只能是整个布局文件对应的view hierarchy),或者代码生成的View/ViewGroup。
      如果仅指定目标Scene,那么框架使用当前scene-root对应的view hierarchy就作为开始的Scene。

    • Transition
      表示两个Scene之间的过渡动画,它保存了使用到的animations。
      TransitionManager用来执行专场效果。
      TransitionListener用来实现对过渡过程的监听。

    存在的限制

    SurfaceView非UI线程不保证同步;
    AdapterView管理childView行为冲突;
    TextView改变尺寸时显示问题;
    TextureView非预期效果;

    创建Scene

    过渡涉及startScene和endScene,它们使用同一个sceneRoot,也就是变化的view hierarchy是在一个ViewGroup下的。

    startScene可以不指定,那么就默认是当前view hierarchy。
    如果连续执行多个Scene之前的切换,那么上一个endScene就是当前的startScene。

    可以通过代码或layout文件创建Scene。

    • 代码文件,生成需要的ViewGroup对象即可,适合“动态创建”界面这样的需求。
    Scene mScene;
    
    // Obtain the scene root element
    mSceneRoot = (ViewGroup) mSomeLayoutElement;
    
    // Obtain the view hierarchy to add as a child of
    // the scene root when this scene is entered
    mViewHierarchy = (ViewGroup) someOtherLayoutElement;
    
    // Create a scene
    mScene = new Scene(mSceneRoot, mViewHierarchy);
    
    • layout文件
    /**
     * Returns a Scene described by the resource file associated with the given
     * <code>layoutId</code> parameter. If such a Scene has already been created for
     * the given <code>sceneRoot</code>, 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);
    

    NOTE:
    The framework creates the scene from the entire view hierarchy in the file; you can not create a scene from part of a layout file.

    Create Scene Actions

    使用Scene.setExitAction() or Scene.setEnterAction() ,传递定义的Runnable实例。
    可以执行一些操作,比如修改ListView等。

    Note:
    Do not use scene actions to pass data between views in the starting and ending scenes. For more information, see Defining Transition Lifecycle Callbacks.

    应用Transition

    创建好startScene、endScene后,在改变UI的逻辑的地方,就可以执行Transition来开启专场动画了。

    创建Transition

    • 资源文件
      在res/transition/目录下,创建表示Transition的文件,资源文件的方式更加复用而且简单。
      这种方式和定义动画文件的机制类似。

    如res/transition/fade_transition.xml:

    <fade xmlns:android="http://schemas.android.com/apk/res/android" />
    

    然后加载它:

    Transition mFadeTransition =
            TransitionInflater.from(this).
            inflateTransition(R.transition.fade_transition);
    
    • 代码方式
      例如:
    Transition mFadeTransition = new Fade();
    

    预提供的类型有ChangeBounds, ChangeClipBounds, ChangeImageTransform, ChangeTransform, TransitionSet, Visibility详见API文档。

    开启过渡

    使用下面的方法TransitionManager.go()

    /**
      * Convenience method to simply change to the given scene using
      * the given transition.
      *
      * <p>Passing in <code>null</code> for the transition parameter will
      * result in the scene changing without any transition running, and is
      * equivalent to calling {@link Scene#exit()} on the scene root's
      * current scene, followed by {@link Scene#enter()} on the scene
      * specified by the <code>scene</code> parameter.</p>
      *
      * @param scene The Scene to change to
      * @param transition The transition to use for this scene change. A
      * value of null causes the scene change to happen with no transition.
      */
     public static void go(Scene scene, Transition transition)
    

    它有一个不含参数transition的重载,默认会使用AutoTransition

    选择指定的目标view

    默认情况下Transition的执行是针对Scene关联的view hierarchy中的所有views执行的。
    可以通过Transition的方法removeTarget()、addTarget()来指定它影响的view。
    比如若ListView不能通过框架正常完成过渡效果,那么移除它。

    动画组:同时执行多个过渡效果

    <transitionSet xmlns:android="http://schemas.android.com/apk/res/android"
        android:transitionOrdering="sequential">
        <fade android:fadingMode="fade_out" />
        <changeBounds />
        <fade android:fadingMode="fade_in" />
    </transitionSet>
    

    对应一个TransitionSet ,它是Transition的子类。

    单Scene的过渡

    如果UI在变化前后差距很小,例如就是同一个ViewGroup的简单的addView()/removeView()那么,此时可以不去执行像startScene、endScene那样的变化,使用delayed transition来在view hierarchy变化的时候开启过渡:

    // Start recording changes to the view hierarchy
    TransitionManager.beginDelayedTransition(mRootView, mFade);
    
    // Add the new TextView to the view hierarchy
    mRootView.addView(mLabelText);
    
    // When the system redraws the screen to show this update,
    // the framework will animate the addition as a fade in
    

    Transition Lifecycle Callbacks

    调用public Transition addListener (Transition.TransitionListener listener)
    回调方法见Transition.TransitionListener

    自定义Transition

    A custom transition, like one of the built-in transition types, applies animations to child views of both the starting and ending scenes. Unlike built-in transition types, however, you have to provide the code that captures property values and generates animations. You may also want to define a subset of target views for your animation.

    定义Transition子类

    public class CustomTransition extends Transition {
    
        @Override
        public void captureStartValues(TransitionValues values) {}
    
        @Override
        public void captureEndValues(TransitionValues values) {}
    
        @Override
        public Animator createAnimator(ViewGroup sceneRoot,
                                       TransitionValues startValues,
                                       TransitionValues endValues) {}
    }
    

    方法captureStartValues()和captureEndValues()用来获取startScene、endScene关联的view的property值。可以对关心的view的属性值进行记录。

    类似下面这样:

    public class CustomTransition extends Transition {
    
        // Define a key for storing a property value in
        // TransitionValues.values with the syntax
        // package_name:transition_class:property_name to avoid collisions
        private static final String PROPNAME_BACKGROUND =
                "com.example.android.customtransition:CustomTransition:background";
    
        @Override
        public void captureStartValues(TransitionValues transitionValues) {
            // Call the convenience method captureValues
            captureValues(transitionValues);
        }
    
    
        // For the view in transitionValues.view, get the values you
        // want and put them in transitionValues.values
        private void captureValues(TransitionValues transitionValues) {
            // Get a reference to the view
            View view = transitionValues.view;
            // Store its background property in the values map
            transitionValues.values.put(PROPNAME_BACKGROUND, view.getBackground());
        }
        ...
    }
    

    TransitionValues是一个非常简单的数据结构,它保存和view关联的属性值,startScene和endScene分别使用一个TransitionValues对象来记录view hierarchy的各个view的状态。

    要记录的“property_name”需要保存唯一,因为可能和其它Transition冲突,建议格式是:
    package_name:transition_name:property_name

    创建自定义Animator

    过渡框架中的Transition使用属性动画作为动画机制的实现。
    子自定义Transition重写createAnimator()方法来提供需要的动画逻辑,工作就是根据startValues和endValues来创建Animator。
    针对有些view的过渡只在endScene或者startScene中有,所以startValues和endValues可能为null。

    框架会为每一个动画的view创建一个Animator,也就是调用一次createAnimator()。

    下面是一个案例ChangeColor.java:

    public class ChangeColor extends Transition {
    
        /** Key to store a color value in TransitionValues object */
        private static final String PROPNAME_BACKGROUND = "customtransition:change_color:background";
    
        // BEGIN_INCLUDE (capture_values)
        /**
         * Convenience method: Add the background Drawable property value
         * to the TransitionsValues.value Map for a target.
         */
        private void captureValues(TransitionValues values) {
            // Capture the property values of views for later use
            values.values.put(PROPNAME_BACKGROUND, values.view.getBackground());
        }
    
        @Override
        public void captureStartValues(TransitionValues transitionValues) {
            captureValues(transitionValues);
        }
    
        // Capture the value of the background drawable property for a target in the ending Scene.
        @Override
        public void captureEndValues(TransitionValues transitionValues) {
            captureValues(transitionValues);
        }
        // END_INCLUDE (capture_values)
    
        // BEGIN_INCLUDE (create_animator)
        // Create an animation for each target that is in both the starting and ending Scene. For each
        // pair of targets, if their background property value is a color (rather than a graphic),
        // create a ValueAnimator based on an ArgbEvaluator that interpolates between the starting and
        // ending color. Also create an update listener that sets the View background color for each
        // animation frame
        @Override
        public Animator createAnimator(ViewGroup sceneRoot,
                                       TransitionValues startValues, TransitionValues endValues) {
            // This transition can only be applied to views that are on both starting and ending scenes.
            if (null == startValues || null == endValues) {
                return null;
            }
            // Store a convenient reference to the target. Both the starting and ending layout have the
            // same target.
            final View view = endValues.view;
            // Store the object containing the background property for both the starting and ending
            // layouts.
            Drawable startBackground = (Drawable) startValues.values.get(PROPNAME_BACKGROUND);
            Drawable endBackground = (Drawable) endValues.values.get(PROPNAME_BACKGROUND);
            // This transition changes background colors for a target. It doesn't animate any other
            // background changes. If the property isn't a ColorDrawable, ignore the target.
            if (startBackground instanceof ColorDrawable && endBackground instanceof ColorDrawable) {
                ColorDrawable startColor = (ColorDrawable) startBackground;
                ColorDrawable endColor = (ColorDrawable) endBackground;
                // If the background color for the target in the starting and ending layouts is
                // different, create an animation.
                if (startColor.getColor() != endColor.getColor()) {
                    // Create a new Animator object to apply to the targets as the transitions framework
                    // changes from the starting to the ending layout. Use the class ValueAnimator,
                    // which provides a timing pulse to change property values provided to it. The
                    // animation runs on the UI thread. The Evaluator controls what type of
                    // interpolation is done. In this case, an ArgbEvaluator interpolates between two
                    // #argb values, which are specified as the 2nd and 3rd input arguments.
                    ValueAnimator animator = ValueAnimator.ofObject(new ArgbEvaluator(),
                            startColor.getColor(), endColor.getColor());
                    // Add an update listener to the Animator object.
                    animator.addUpdateListener(new ValueAnimator.AnimatorUpdateListener() {
                        @Override
                        public void onAnimationUpdate(ValueAnimator animation) {
                            Object value = animation.getAnimatedValue();
                            // Each time the ValueAnimator produces a new frame in the animation, change
                            // the background color of the target. Ensure that the value isn't null.
                            if (null != value) {
                                view.setBackgroundColor((Integer) value);
                            }
                        }
                    });
                    // Return the Animator object to the transitions framework. As the framework changes
                    // between the starting and ending layouts, it applies the animation you've created.
                    return animator;
                }
            }
            // For non-ColorDrawable backgrounds, we just return null, and no animation will take place.
            return null;
        }
        // END_INCLUDE (create_animator)
    
    }
    

    自定义Transition的使用和内建的Fade、ChangeBounds等是完全相同的。

    (本文使用Atom编写)

  • 相关阅读:
    Photoshop 基础七 位图 矢量图 栅格化
    Photoshop 基础六 图层
    Warfare And Logistics UVALive
    Walk Through the Forest UVA
    Airport Express UVA
    Guess UVALive
    Play on Words UVA
    The Necklace UVA
    Food Delivery ZOJ
    Brackets Sequence POJ
  • 原文地址:https://www.cnblogs.com/everhad/p/6271238.html
Copyright © 2011-2022 走看看