//////////////////////////////////////////////////////////////////////////////// // // Licensed to the Apache Software Foundation (ASF) under one or more // contributor license agreements. See the NOTICE file distributed with // this work for additional information regarding copyright ownership. // The ASF licenses this file to You under the Apache License, Version 2.0 // (the "License"); you may not use this file except in compliance with // the License. You may obtain a copy of the License at // // http://www.apache.org/licenses/LICENSE-2.0 // // Unless required by applicable law or agreed to in writing, software // distributed under the License is distributed on an "AS IS" BASIS, // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. // See the License for the specific language governing permissions and // limitations under the License. // //////////////////////////////////////////////////////////////////////////////// package spark.transitions { import flash.display.DisplayObject; import flash.display.DisplayObjectContainer; import flash.display.Stage; import flash.events.Event; import flash.events.EventDispatcher; import flash.geom.Matrix; import flash.geom.Point; import flash.geom.Rectangle; import mx.core.FlexGlobals; import mx.core.IVisualElementContainer; import mx.core.UIComponent; import mx.core.mx_internal; import mx.effects.IEffect; import mx.effects.Parallel; import mx.events.EffectEvent; import mx.events.FlexEvent; import mx.geom.TransformOffsets; import mx.managers.SystemManager; import spark.components.ActionBar; import spark.components.Group; import spark.components.TabbedViewNavigator; import spark.components.View; import spark.components.ViewNavigator; import spark.components.supportClasses.ButtonBarBase; import spark.components.supportClasses.ViewNavigatorBase; import spark.effects.Animate; import spark.effects.Fade; import spark.effects.animation.MotionPath; import spark.effects.animation.SimpleMotionPath; import spark.effects.easing.IEaser; import spark.effects.easing.Sine; import spark.primitives.BitmapImage; import spark.utils.BitmapUtil; use namespace mx_internal; /** * Dispatched when the transition starts. * * @eventType mx.events.FlexEvent.TRANSITION_START * * @langversion 3.0 * @playerversion AIR 2.5 * @productversion Flex 4.5 */ [Event(name="transitionStart", type="mx.events.FlexEvent")] /** * Dispatched when the transition completes. * * @eventType mx.events.FlexEvent.TRANSITION_START * * @langversion 3.0 * @playerversion AIR 2.5 * @productversion Flex 4.5 */ [Event(name="transitionEnd", type="mx.events.FlexEvent")] /** * The ViewTransitionBase class is the base class for all view transitions. * It is not intended to be used as a transition on its own. * In addition to providing common convenience and helper methods used by * view transitions, this class provides a default action bar transition * sequence. * *
When a view transition is initialized, the owning view navigator
* sets the startView
and endView
properties
* to the views the transition animates.
* The navigator
property is
* set to the view navigator.
The lifecycle of a transition is as follows:
*captureStartValues()
method.
* When this method is called, the navigator is currently in the
* start state.
* At this time, the transition should capture any start values
* or bitmaps that it requires. captureEndValues()
method is called.
* At this time, the transition captures any properties or
* bitmaps representations from the pending view.prepareForPlay()
method is then called,
* which allows the transition to perform any further preparations,
* such as preparing a Spark effects sequence,
* or positioning transient elements on the display list.play()
method is called by the navigator
* to perform the actual transition.start
* event is dispatched.end
event.Note:Create and configure view transitions in ActionScript; * you cannot create them in MXML.
* * @langversion 3.0 * @playerversion AIR 2.5 * @productversion Flex 4.5 */ public class ViewTransitionBase extends EventDispatcher { //-------------------------------------------------------------------------- // // Constants // //-------------------------------------------------------------------------- /** * Constant used in tandem with the actionBarTransitionMode property to * hint the default action bar transition behavior. */ mx_internal static const ACTION_BAR_MODE_FADE:String = "fade"; /** * Constant used in tandem with the actionBarTransitionMode property to * hint the default action bar transition behavior. */ mx_internal static const ACTION_BAR_MODE_FADE_AND_SLIDE:String = "fadeAndSlide"; /** * Constant used in tandem with the actionBarTransitionMode property to * hint the default action bar transition behavior. */ mx_internal static const ACTION_BAR_MODE_NONE:String = "none"; //-------------------------------------------------------------------------- // // Constructor // //-------------------------------------------------------------------------- /** * Constructor. * * @langversion 3.0 * @playerversion AIR 2.5 * @productversion Flex 4.5 */ public function ViewTransitionBase() { super(); } //-------------------------------------------------------------------------- // // Variables // //-------------------------------------------------------------------------- /** * @private * Flag set when we've determined that we need to transition the navigator * in its entirety and cannot transition the control bars independently. */ protected var consolidatedTransition:Boolean = false; /** * @private * startView's action bar height. */ private var cachedActionBarHeight:Number; /** * @private * startView's action bar width. */ private var cachedActionBarWidth:Number; /** * @private * Transient display object used to hold temporary bitmap snapshots * during transition. */ private var transitionGroup:Group; /** * @private * Flag to assist with cleanup of any constructs used only for vertical * transitions (e.g. clipping masks). */ private var verticalTransition:Boolean; /** * @private * Flag used to determine whether the transition should wait a frame when * it receives the EFFECT_END event. This is true by default. It is only * set to false when the endTransitions() method is called. */ private static var renderLastFrame:Boolean = true; /** * @private * Private vector that stores all the active transitions. */ private static var activeTransitions:Vector.true
, the UIComponent.suspendBackgroundProcessing()
* method is invoked prior to the transition playing.
* This disables Flex's layout manager and improving performance.
* Upon completion of the transition,
* the layout manager function is restored by a call to the
* UIComponent.resumeBackgroundProcessing()
method.
*
* @default false
*
* @see mx.core.UIComponent#suspendBackgroundProcessing()
* @see mx.core.UIComponent#resumeBackgroundProcessing()
*
* @langversion 3.0
* @playerversion AIR 2.5
* @productversion Flex 4.5
*/
public function get suspendBackgroundProcessing():Boolean
{
return _suspendBackgroundProcessing;
}
/**
* @private
*/
public function set suspendBackgroundProcessing(value:Boolean):void
{
_suspendBackgroundProcessing = value;
}
//----------------------------------
// transitionControlsWithContent
//----------------------------------
private var _transitionControlsWithContent:Boolean;
/**
* When set to true
, the primary view transition
* is used to transition the view navigator in its entirety,
* including the action bar.
* Specific transitions for the action bar are not performed.
* Because the tab bar is associated with the entire application,
* and not a view, view transitions do not affect it.
*
* Note that even when set to false
, there are cases
* where its not feasible to transition the action bar.
* For example, when the action bar does not exist in one of
* the two views, or if the action bar changes size.
start
event.
*
* @langversion 3.0
* @playerversion AIR 2.5
* @productversion Flex 4.5
*/
public function play():void
{
if (effect)
{
activeTransitions.push(this);
effect.addEventListener(EffectEvent.EFFECT_END, effectComplete);
// Dispatch TRANSITION_START.
if (hasEventListener(FlexEvent.TRANSITION_START))
dispatchEvent(new FlexEvent(FlexEvent.TRANSITION_START));
if (navigator && navigator.stage && navigator.stage.hasEventListener(FlexEvent.TRANSITION_START))
navigator.stage.dispatchEvent(new FlexEvent(FlexEvent.TRANSITION_START));
effect.play();
}
else
transitionComplete();
}
/**
* Called by the ViewNavigator during the preparation phase
* of a transition.
* This method gives the transition the chance to create and
* configure the underlying IEffect instance, or to add any transient
* elements to the display list.
* Example transient elements include bitmap placeholders, temporary
* containers required during the transition, and other elements.
* If required, a final validation pass occurs prior to the invocation
* of the play()
method.
*
* If it is determined that a standard transition can be initiated,
* meaning one that transitions the control bars separately from the views,
* the default implementation of this method constructs
* a single Parallel effect which wraps the individual effect sequences
* for the view transition, the action bar transition, and the tab bar transition.
* This method uses the methods, createActionBarEffect()
,
* createTabBarEffect()
, and createViewEffect()
.
If transitionControlsWithContent
is set to true
,
* or if it is determined that the control bars cannot be transitioned independently,
* a single effect is created to transition the navigator in its entirety.
* In this case, only createConsolidatedEffect()
is invoked.
prepareForPlay()
implementation,
* this method is responsible for creating the Spark effect
* played on the action bar when the transition starts.
* This method should be overridden by subclasses if a custom action bar
* effect is required.
* By default, this method returns a basic action bar effect.
*
* @return An IEffect instance serving as the action bar effect.
* This effect is played by the default play()
method implementation.
*
* @langversion 3.0
* @playerversion AIR 2.5
* @productversion Flex 4.5
*/
protected function createActionBarEffect():IEffect
{
var transformOffsets:TransformOffsets;
var slideDistance:Number;
var animatedProperty:String;
var actionBarSkin:UIComponent = actionBar.skin;
var slideTargets:Array = new Array();
var fadeOutTargets:Array = new Array();
var fadeInTargets:Array = new Array();
// Return if we have a noop action bar transition mode.
if (!actionBar || actionBarTransitionMode == ACTION_BAR_MODE_NONE ||
!actionBarTransitionMode)
return null;
transitionGroup = new Group();
transitionGroup.autoLayout = false;
transitionGroup.includeInLayout = false;
transitionGroup.width = actionBar.width;
transitionGroup.height = actionBar.height;
addComponentToContainer(transitionGroup, actionBarSkin);
// Construct our parallel effect.
var actionBarEffect:Parallel = new Parallel();
// Calculate the slide distance based on direction.
switch (actionBarTransitionDirection)
{
case ViewTransitionDirection.RIGHT:
animatedProperty = "x";
slideDistance = -actionBar.width / 2.5;
break;
case ViewTransitionDirection.DOWN:
animatedProperty = "y";
slideDistance = -actionBar.height / 2.5;
verticalTransition = true;
break;
case ViewTransitionDirection.UP:
animatedProperty = "y";
slideDistance = actionBar.height / 2.5;
verticalTransition = true;
break;
case ViewTransitionDirection.LEFT:
default:
animatedProperty = "x";
slideDistance = actionBar.width / 2.5;
break;
}
transitionGroup.clipAndEnableScrolling = true;
// Suppress slide if our action bar transition behavior is fade-only.
if (actionBarTransitionMode == ACTION_BAR_MODE_FADE)
slideDistance = 0;
// If the skin has title content queue new title content for fade in.
if (actionBar.titleGroup || actionBar.titleDisplay)
{
var titleComponent:UIComponent = actionBar.titleGroup;
if (!titleComponent || !titleComponent.visible)
titleComponent = actionBar.titleDisplay as UIComponent;
if (titleComponent)
{
// Initialize the transformation offests
transformOffsets = new TransformOffsets();
transformOffsets[animatedProperty] = slideDistance;
slideTargets.push(transformOffsets);
// Initialize titleGroup
titleComponent.cacheAsBitmap = true;
titleComponent.alpha = 0;
titleComponent.postLayoutTransformOffsets = transformOffsets;
fadeInTargets.push(titleComponent);
// We reparent the titleComponent into the transition group so
// that the items are properly clipped when animating vertically
if (verticalTransition)
transitionGroup.addElementAt(titleComponent, 0);
}
if (cachedTitleGroup)
addCachedElementToGroup(transitionGroup, cachedTitleGroup, cachedTitleGroupGlobalPosition);
}
// If a cache of the navigation group exists, that means the content
// changed. In this case the queue cached representation to be faded
// out.
if (cachedNavigationGroup)
addCachedElementToGroup(transitionGroup, cachedNavigationGroup, cachedNavigationGroupGlobalPosition);
// If a cache of the action group exists, that means the content
// changed. In this case the queue cached representation to be faded
// out.
if (cachedActionGroup)
addCachedElementToGroup(transitionGroup, cachedActionGroup, cachedActionGroupGlobalPosition);
// Create fade in animations for navigationContent and actionContent
// of the next view.
if (endView)
{
if (endView.navigationContent)
{
// Initialize the transformation offests
transformOffsets = new TransformOffsets();
transformOffsets[animatedProperty] = slideDistance;
slideTargets.push(transformOffsets);
actionBar.navigationGroup.postLayoutTransformOffsets = transformOffsets;
actionBar.navigationGroup.cacheAsBitmap = true;
actionBar.navigationGroup.alpha = 0;
fadeInTargets.push(actionBar.navigationGroup);
// We reparent the titleComponent into the transition group so
// that the items are properly clipped when animating vertically
if (verticalTransition)
transitionGroup.addElementAt(actionBar.navigationGroup, 0);
}
if (endView.actionContent)
{
// Initialize the transformation offests
transformOffsets = new TransformOffsets();
transformOffsets[animatedProperty] = slideDistance;
slideTargets.push(transformOffsets);
actionBar.actionGroup.postLayoutTransformOffsets = transformOffsets;
actionBar.actionGroup.cacheAsBitmap = true;
actionBar.actionGroup.alpha = 0;
fadeInTargets.push(actionBar.actionGroup);
// We reparent the titleComponent into the transition group so
// that the items are properly clipped when animating vertically
if (verticalTransition)
transitionGroup.addElementAt(actionBar.actionGroup, 0);
}
}
// Ensure bitmaps are rendered prior to invocation of our effect.
transitionGroup.validateNow();
// Setup fade out targets
if (cachedTitleGroup)
{
// Initialize the transformation offests
transformOffsets = new TransformOffsets();
slideTargets.push(transformOffsets);
cachedTitleGroup.postLayoutTransformOffsets = transformOffsets;
fadeOutTargets.push(cachedTitleGroup.displayObject);
}
if (cachedNavigationGroup)
{
// Initialize the transformation offests
transformOffsets = new TransformOffsets();
slideTargets.push(transformOffsets);
cachedNavigationGroup.postLayoutTransformOffsets = transformOffsets;
fadeOutTargets.push(cachedNavigationGroup.displayObject);
}
if (cachedActionGroup)
{
// Initialize the transformation offests
transformOffsets = new TransformOffsets();
slideTargets.push(transformOffsets);
cachedActionGroup.postLayoutTransformOffsets = transformOffsets;
fadeOutTargets.push(cachedActionGroup.displayObject);
}
// If no fade effects we aren't animating anything so return null
if (fadeInTargets.length == 0 && fadeOutTargets.length == 0)
return null;
// Create fade in effect
if (fadeInTargets.length > 0)
{
var fadeInEffect:Fade = new Fade();
fadeInEffect.targets = fadeInTargets;
fadeInEffect.duration = duration;
fadeInEffect.alphaFrom = 0;
fadeInEffect.alphaTo = 1;
actionBarEffect.addChild(fadeInEffect);
}
// Create fade out effect
if (fadeOutTargets.length > 0)
{
var fadeOutEffect:Fade = new Fade();
fadeOutEffect.targets = fadeOutTargets;
fadeOutEffect.duration = duration;
fadeOutEffect.alphaFrom = 1;
fadeOutEffect.alphaTo = 0;
actionBarEffect.addChild(fadeOutEffect);
}
// Create slide in effect
var vector:Vector.prepareForPlay()
implementation,
* this method is responsible for creating the Spark effect played
* on the tab bar when the transition starts.
* This method should be overridden by subclasses.
* By default, this returns null.
*
* @return An IEffect instance serving as the tab bar transition.
* This effect is played by the default play()
method implementation.
*
* @langversion 3.0
* @playerversion AIR 2.5
* @productversion Flex 4.5
*/
protected function createTabBarEffect():IEffect
{
return null;
}
/**
* Called by the default prepareForPlay()
implementation,
* this method is responsible for creating the Spark effect played
* on the current and next view when the transition starts.
* This method should be overridden by subclasses.
* By default, this method returns null.
*
* @return An IEffect instance serving as the view transition.
* This effect is played by the default play()
method implementation.
*
* @langversion 3.0
* @playerversion AIR 2.5
* @productversion Flex 4.5
*/
protected function createViewEffect():IEffect
{
return null;
}
/**
* Called by the default prepareForPlay()
implementation,
* this method is responsible for creating the Spark effect played to
* transition the entire navigator, inclusive of the control bar content,
* when necessary.
* This method should be overridden by subclasses.
* By default, this method returns null.
*
* @return An IEffect instance serving as the view transition.
* This effect is played by the default play()
method implementation.
*
* @langversion 3.0
* @playerversion AIR 2.5
* @productversion Flex 4.5
*/
protected function createConsolidatedEffect():IEffect
{
return null;
}
/**
* Called by the transition to indicate that the transition
* has completed.
* This method dispatches the end
event.
*
* @langversion 3.0
* @playerversion AIR 2.5
* @productversion Flex 4.5
*/
protected function transitionComplete():void
{
var stage:Stage;
if (navigator)
stage = navigator.stage;
cleanUp();
activeTransitions.splice(activeTransitions.indexOf(this), 1);
if (hasEventListener(FlexEvent.TRANSITION_END))
dispatchEvent(new FlexEvent(FlexEvent.TRANSITION_END));
if (stage && stage.hasEventListener(FlexEvent.TRANSITION_END))
stage.dispatchEvent(new FlexEvent(FlexEvent.TRANSITION_END));
}
/**
* Called after the transition completes.
* This method is responsible for releasing any references
* and temporary constructs used by the transition.
*
* @langversion 3.0
* @playerversion AIR 2.5
* @productversion Flex 4.5
*/
protected function cleanUp():void
{
if (!consolidatedTransition && transitionGroup)
{
if (cachedTitleGroup)
transitionGroup.removeElement(cachedTitleGroup);
if (cachedNavigationGroup)
transitionGroup.removeElement(cachedNavigationGroup);
if (cachedActionGroup)
{
transitionGroup.removeElement(cachedActionGroup);
actionBar.actionGroup.cacheAsBitmap = false;
}
if (actionBar)
{
// Restore title group and title content to their original state.
if (actionBar.titleGroup && actionBar.titleGroup.visible)
{
actionBar.titleGroup.postLayoutTransformOffsets = null;
actionBar.titleGroup.cacheAsBitmap = false;
}
if (actionBar.titleDisplay
&& (actionBar.titleDisplay is DisplayObject)
&& DisplayObject(actionBar.titleDisplay).visible)
{
(actionBar.titleDisplay as UIComponent).postLayoutTransformOffsets = null;
DisplayObject(actionBar.titleDisplay).cacheAsBitmap = false;
}
// Restore title group and title content to their original home.
if (verticalTransition)
{
var titleComponent:UIComponent = actionBar.titleGroup;
if (!titleComponent || !titleComponent.visible)
titleComponent = actionBar.titleDisplay as UIComponent;
if (titleComponent)
{
transitionGroup.removeElement(titleComponent);
addComponentToContainer(titleComponent, actionBar.skin);
}
}
// Restore navigation group to their proper home.
if (endView.navigationContent && verticalTransition)
{
transitionGroup.removeElement(actionBar.navigationGroup);
if (actionBar.titleDisplay)
{
var childIndex:uint = actionBar.skin.getChildIndex(actionBar.titleDisplay as DisplayObject);
addComponentToContainerAt(actionBar.navigationGroup, actionBar.skin, childIndex);
}
else
addComponentToContainer(actionBar.navigationGroup, actionBar.skin);
}
// Restore action group to their proper home.
if (endView.actionContent && verticalTransition)
{
transitionGroup.removeElement(actionBar.actionGroup);
if (actionBar.titleDisplay)
{
childIndex = actionBar.skin.getChildIndex(actionBar.titleDisplay as DisplayObject);
addComponentToContainerAt(actionBar.actionGroup, actionBar.skin, childIndex);
}
else
addComponentToContainer(actionBar.actionGroup, actionBar.skin);
}
removeComponentFromContainer(transitionGroup, actionBar.skin);
actionBar.skin.scrollRect = null;
// Force actionBar to update content group positions after
// animating positions. If the width and height change during
// the transition, we need relayout it's children because
// during the transition they are removed from layout and
// missed during the validation pass. See SDK-30142.
// TODO (jasonsj): Consider ending transitions when orientation
// changes
if ((actionBar.width != cachedActionBarWidth)
|| (actionBar.height != cachedActionBarHeight))
{
actionBar.skin.invalidateDisplayList();
}
if (actionBar.actionGroup)
actionBar.actionGroup.postLayoutTransformOffsets = null;
if (actionBar.navigationGroup)
actionBar.navigationGroup.postLayoutTransformOffsets = null;
}
verticalTransition = false;
cachedActionBarHeight = 0;
cachedActionBarWidth = 0;
transitionGroup = null;
cachedTitleGroup = null;
cachedNavigationGroup = null;
cachedActionGroup = null;
}
consolidatedTransition = false;
actionBar = null;
tabBar = null;
parentNavigator = null;
targetNavigator = null;
navigator = null;
startView = null;
endView = null;
// Re-enable layout manager if appropriate.
if (suspendBackgroundProcessing)
UIComponent.resumeBackgroundProcessing();
}
/**
* Determine if Flex can perform a transition on
* action bar or tab bar content independently of the views.
*
* Flex cannot perform a transition on the control bars independently:
*overlayControls
* property changes between views.false
if Flex determines controls bars between views are
* incompatible in some way.
*
* @langversion 3.0
* @playerversion AIR 2.5
* @productversion Flex 4.5
*/
protected function canTransitionControlBarContent():Boolean
{
// Short circuit if we've already been asked to not consider
// control bars during transition.
if (transitionControlsWithContent)
return false;
// Test for visibility or size of tab bar changing.
if (targetNavigator is TabbedViewNavigator)
{
var tabBar:ButtonBarBase = TabbedViewNavigator(targetNavigator).tabBar;
if (componentIsVisible(tabBar) != endView.tabBarVisible)
return false;
}
// Test for visibility or size of action bar changing.
if (navigator is ViewNavigator)
{
var actionBar:ActionBar = ViewNavigator(navigator).actionBar;
if (componentIsVisible(actionBar) != endView.actionBarVisible)
return false;
}
// Test for valid views.
if (!startView || !endView)
return false;
// Test for value of overlayControls changing.
if (startView.overlayControls != endView.overlayControls)
return false;
return true;
}
/**
* Used to render snap shots of screen elements in
* preparation for transitioning.
* The bitmap is returned in the form of a BitmapImage object.
*
* The BitmapImage is in target's parent coordiantes space -
* it overlaps the target precisely if paranted to the same parent.
*
* When moving to a different parent, make sure to adjust the
* transformation of the BitmapImage to correctly account for the
* change in coordinate spaces.
*
* The updated value of the globalPosition
parameter
* can be used for that.
globalPosition
* will be updated with the origin of the BitmapImage in global
* coordiantes. When moving to a different coordinate space, this
* value can be used to adjust the snapshot's position so its
* global position on screen doesn't change.
*
* @return BitmapImage object representing the target.
*
* @langversion 3.0
* @playerversion AIR 2.5
* @productversion Flex 4.5
*/
protected function getSnapshot(target:UIComponent, padding:int = 4, globalPosition:Point = null):BitmapImage
{
if (!target || !target.visible || target.width == 0 || target.height == 0)
return null;
var snapshot:BitmapImage = new BitmapImage();
// Ensure bitmap leverages its own display object for performance
// reasons.
snapshot.alwaysCreateDisplayObject = true;
// Capture image, with consideration for transform and color matrix.
// Return null if an error is thrown.
var bounds:Rectangle = new Rectangle();
try
{
snapshot.source = BitmapUtil.getSnapshotWithPadding(target, padding, true, bounds);
}
catch (e:SecurityError)
{
return null;
}
// Size and offset snapShot to match our image bounds data.
snapshot.width = bounds.width;
snapshot.height = bounds.height;
var m:Matrix = new Matrix();
m.translate(bounds.left, bounds.top);
// Apply target's inverse concatenated matrix:
var parent:DisplayObjectContainer = target.parent;
if (parent)
{
var inverted:Matrix = parent.transform.concatenatedMatrix.clone();
inverted.invert();
m.concat(inverted);
}
snapshot.setLayoutMatrix(m, false);
// Exclude from layout.
snapshot.includeInLayout = false;
if (globalPosition)
{
var pt:Point = parent ? parent.localToGlobal(new Point(snapshot.x, snapshot.y)) : new Point();
globalPosition.x = pt.x;
globalPosition.y = pt.y;
}
return snapshot;
}
//--------------------------------------------------------------------------
//
// Private Methods
//
//--------------------------------------------------------------------------
/**
* @private
* Remove listeners
*/
private function actionBarMoveEffect_effectEndedHandler(event:EffectEvent):void
{
event.target.removeEventListener(EffectEvent.EFFECT_UPDATE, actionBarMoveEffect_effectUpdateHandler);
event.target.removeEventListener(EffectEvent.EFFECT_END, actionBarMoveEffect_effectEndedHandler);
}
/**
* @private
* Since layout is disabled, we need to force validation on all of the
* participating display objects.
*/
private function actionBarMoveEffect_effectUpdateHandler(event:EffectEvent):void
{
if (!actionBar)
return;
// This code is a temporary performance fix for transitions. Since layout is
// disabled during ViewTransitions, in the past this method would call
// validateDisplayList() on the animation targets to update their internal position
// matrices. We learned that this was causing a reduction in framerate due to
// actionScript overhead. To workaround the issue we are moving the x and y positions
// of the underlying displayObjects manually. This is a temporary fix to get
// our performance numbers back up after checking in a fix for SDK-30839.
// TODO (chiedozi): Clean up this code and use propery layout methods
if (verticalTransition)
{
if (actionBar.actionGroup && actionBar.actionGroup.postLayoutTransformOffsets)
actionBar.actionGroup.$y = actionBar.actionGroup.y + actionBar.actionGroup.postLayoutTransformOffsets.y;
if (actionBar.navigationGroup && actionBar.navigationGroup.postLayoutTransformOffsets)
actionBar.navigationGroup.$y = actionBar.navigationGroup.y + actionBar.navigationGroup.postLayoutTransformOffsets.y;
if (actionBar.titleDisplay && UIComponent(actionBar.titleDisplay).postLayoutTransformOffsets)
UIComponent(actionBar.titleDisplay).$y = UIComponent(actionBar.titleDisplay).y + UIComponent(actionBar.titleDisplay).postLayoutTransformOffsets.y;
if (actionBar.titleGroup && actionBar.titleGroup.postLayoutTransformOffsets)
actionBar.titleGroup.$y = actionBar.titleGroup.y + actionBar.titleGroup.postLayoutTransformOffsets.y;
if (cachedTitleGroup && cachedTitleGroup.displayObject)
cachedTitleGroup.displayObject.y = cachedTitleGroup.y + cachedTitleGroup.postLayoutTransformOffsets.y;
if (cachedNavigationGroup && cachedNavigationGroup.displayObject)
cachedNavigationGroup.displayObject.y = cachedNavigationGroup.y + cachedNavigationGroup.postLayoutTransformOffsets.y;
if (cachedActionGroup && cachedActionGroup.displayObject)
cachedActionGroup.displayObject.y = cachedActionGroup.y + cachedActionGroup.postLayoutTransformOffsets.y;
}
else
{
if (actionBar.actionGroup && actionBar.actionGroup.postLayoutTransformOffsets)
actionBar.actionGroup.$x = actionBar.actionGroup.x + actionBar.actionGroup.postLayoutTransformOffsets.x;
if (actionBar.navigationGroup && actionBar.navigationGroup.postLayoutTransformOffsets)
actionBar.navigationGroup.$x = actionBar.navigationGroup.x + actionBar.navigationGroup.postLayoutTransformOffsets.x;
if (actionBar.titleDisplay && UIComponent(actionBar.titleDisplay).postLayoutTransformOffsets)
UIComponent(actionBar.titleDisplay).$x = UIComponent(actionBar.titleDisplay).x + UIComponent(actionBar.titleDisplay).postLayoutTransformOffsets.x;
if (actionBar.titleGroup && actionBar.titleGroup.postLayoutTransformOffsets)
actionBar.titleGroup.$x = actionBar.titleGroup.x + actionBar.titleGroup.postLayoutTransformOffsets.x;
if (cachedTitleGroup && cachedTitleGroup.displayObject)
cachedTitleGroup.displayObject.x = cachedTitleGroup.x + cachedTitleGroup.postLayoutTransformOffsets.x;
if (cachedNavigationGroup && cachedNavigationGroup.displayObject)
cachedNavigationGroup.displayObject.x = cachedNavigationGroup.x + cachedNavigationGroup.postLayoutTransformOffsets.x;
if (cachedActionGroup && cachedActionGroup.displayObject)
cachedActionGroup.displayObject.x = cachedActionGroup.x + cachedActionGroup.postLayoutTransformOffsets.x;
}
}
/**
* @private
*/
private function effectComplete(event:EffectEvent):void
{
effect.removeEventListener(EffectEvent.EFFECT_END, effectComplete);
// Validate the last frame of the actionBar animation so that it
// renders properly. We put this here because layout isn't reenabled
// until the next frame, meaning this validation won't be applied for
// two frames.
if (!consolidatedTransition)
actionBarMoveEffect_effectUpdateHandler(null);
if (renderLastFrame)
{
// We don't call transitionComplete just yet, we want to ensure
// that the last frame of animation actually gets rendered on screen
// before we clean up after ourselves. This prevents a perceived
// stutter on the very last frame.
navigator.addEventListener(Event.ENTER_FRAME, enterFrameHandler);
}
else
{
enterFrameHandler(null);
}
}
/**
* @private
*/
private function enterFrameHandler(event:Event):void
{
if (event)
navigator.removeEventListener(Event.ENTER_FRAME, enterFrameHandler);
effect = null;
transitionComplete();
}
/**
* @private
* Helper method to test whether a component is visible to user.
*/
mx_internal function componentIsVisible(component:UIComponent):Boolean
{
return component && component.visible &&
component.width && component.height && component.alpha;
}
/**
* @private
* Helper method to add a UIComponent instance to either an IVisualElementContainer
* or DisplayObjectContainer.
*/
mx_internal function addComponentToContainerAt(component:UIComponent,
container:UIComponent,
index:int):void
{
if (container is IVisualElementContainer)
IVisualElementContainer(container).addElementAt(component, index);
else
container.addChildAt(component, index);
}
/**
* @private
* Helper method to add a UIComponent instance to either an IVisualElementContainer
* or DisplayObjectContainer.
*/
mx_internal function addComponentToContainer(component:UIComponent,
container:UIComponent):void
{
if (container is IVisualElementContainer)
IVisualElementContainer(container).addElement(component);
else
container.addChild(component);
}
/**
* @private
* Helper method to remove a UIComponent instance from either an IVisualElementContainer
* or DisplayObjectContainer.
*/
mx_internal function removeComponentFromContainer(component:UIComponent,
container:UIComponent):void
{
if (container is IVisualElementContainer)
IVisualElementContainer(container).removeElement(component);
else
container.removeChild(component);
}
/**
* @private
* Helper method to set the child index of the given component.
*/
mx_internal function setComponentChildIndex(component:UIComponent,
container:UIComponent,
index:int):void
{
if (container is IVisualElementContainer)
IVisualElementContainer(container).setElementIndex(component, index);
else
container.setChildIndex(component, index);
}
/**
* @private
* Adds the element to the targetGroup and adjusts the position
* so that the global position remains the same.
*
* Note the targetGroup must be already added to the display list and
* positioned in order for this method to adjust the cachedElement's position
* correctly.
*
* @param targetGroup The Group that will parent the cached element.
* @param cachedElement The cached element - the return value of getSnapshot()
* @param cachedElementGlobalPosition The global position returned from getSnapshot()
*
* @see #getSnapshot
*/
mx_internal function addCachedElementToGroup(targetGroup:Group,
cachedElement:BitmapImage,
cachedElementGlobalPosition:Point):void
{
targetGroup.addElement(cachedElement);
// We are moving the cachedTitleGroup to the transitionGroup's coordinate space,
// adjust the position
var localOrigin:Point = targetGroup.globalToLocal(cachedElementGlobalPosition);
cachedElement.x = localOrigin.x;
cachedElement.y = localOrigin.y;
}
/**
* @private
* Helper method that returns index of the given component.
*/
mx_internal function getComponentChildIndex(component:UIComponent, container:UIComponent):int
{
if (container is IVisualElementContainer)
return IVisualElementContainer(container).getElementIndex(component);
else
return container.getChildIndex(component);
}
}
}