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编写)

  • 相关阅读:
    怎么为学生布置作业
    新学期的第一节Android课
    RUCSE小组博客列表
    test
    个人最终总结
    黄金点小游戏的设计与实现
    WordCount 程序的实现
    阅读下面程序,请回答如下问题:
    Visual Studio 2015的安装和简单的单元测试
    构建之法--软件工程学习随笔之二
  • 原文地址:https://www.cnblogs.com/everhad/p/6271238.html
Copyright © 2011-2022 走看看