refactor: restore animators api usage (#6403)

This commit is contained in:
Manol Donev
2018-10-16 15:37:57 +03:00
committed by GitHub
parent 7867e7ce5b
commit c8c0be7684
12 changed files with 229 additions and 235 deletions

View File

@ -5,9 +5,8 @@ export class CustomTransition extends transition.Transition {
super(duration, curve); super(duration, curve);
} }
public createAndroidAnimation(transitionType: string): android.view.animation.Animation { public createAndroidAnimator(transitionType: string): android.animation.Animator {
const scaleValues = []; var scaleValues = Array.create("float", 2);
switch (transitionType) { switch (transitionType) {
case transition.AndroidTransitionType.enter: case transition.AndroidTransitionType.enter:
case transition.AndroidTransitionType.popEnter: case transition.AndroidTransitionType.popEnter:
@ -20,22 +19,18 @@ export class CustomTransition extends transition.Transition {
scaleValues[1] = 0; scaleValues[1] = 0;
break; break;
} }
var objectAnimators = Array.create(android.animation.Animator, 2);
objectAnimators[0] = android.animation.ObjectAnimator.ofFloat(null, "scaleX", scaleValues);
objectAnimators[1] = android.animation.ObjectAnimator.ofFloat(null, "scaleY", scaleValues);
var animatorSet = new android.animation.AnimatorSet();
animatorSet.playTogether(objectAnimators);
const animationSet = new android.view.animation.AnimationSet(false); var duration = this.getDuration();
const duration = this.getDuration();
if (duration !== undefined) { if (duration !== undefined) {
animationSet.setDuration(duration); animatorSet.setDuration(duration);
} }
animatorSet.setInterpolator(this.getCurve());
animationSet.setInterpolator(this.getCurve()); return animatorSet;
animationSet.addAnimation(
new android.view.animation.ScaleAnimation(
scaleValues[0],
scaleValues[1],
scaleValues[0],
scaleValues[1]
));
return animationSet;
} }
} }

View File

@ -14,8 +14,9 @@ class FragmentClass extends android.support.v4.app.Fragment {
this._callbacks.onHiddenChanged(this, hidden, super.onHiddenChanged); this._callbacks.onHiddenChanged(this, hidden, super.onHiddenChanged);
} }
public onCreateAnimation(transit: number, enter: boolean, nextAnim: number): android.view.animation.Animation { public onCreateAnimator(transit: number, enter: boolean, nextAnim: number): android.animation.Animator {
return this._callbacks.onCreateAnimation(this, transit, enter, nextAnim, super.onCreateAnimation); let result = this._callbacks.onCreateAnimator(this, transit, enter, nextAnim, super.onCreateAnimator);
return result;
} }
public onStop(): void { public onStop(): void {

View File

@ -19,7 +19,7 @@ interface TransitionListener {
new(entry: ExpandedEntry, transition: android.transition.Transition): ExpandedTransitionListener; new(entry: ExpandedEntry, transition: android.transition.Transition): ExpandedTransitionListener;
} }
interface ExpandedAnimation extends android.view.animation.Animation { interface ExpandedAnimator extends android.animation.Animator {
entry: ExpandedEntry; entry: ExpandedEntry;
transitionType?: string; transitionType?: string;
} }
@ -35,13 +35,13 @@ interface ExpandedEntry extends BackstackEntry {
reenterTransitionListener: ExpandedTransitionListener; reenterTransitionListener: ExpandedTransitionListener;
returnTransitionListener: ExpandedTransitionListener; returnTransitionListener: ExpandedTransitionListener;
enterAnimation: ExpandedAnimation; enterAnimator: ExpandedAnimator;
exitAnimation: ExpandedAnimation; exitAnimator: ExpandedAnimator;
popEnterAnimation: ExpandedAnimation; popEnterAnimator: ExpandedAnimator;
popExitAnimation: ExpandedAnimation; popExitAnimator: ExpandedAnimator;
defaultEnterAnimation: ExpandedAnimation; defaultEnterAnimator: ExpandedAnimator;
defaultExitAnimation: ExpandedAnimation; defaultExitAnimator: ExpandedAnimator;
transition: Transition; transition: Transition;
transitionName: string; transitionName: string;
@ -49,13 +49,14 @@ interface ExpandedEntry extends BackstackEntry {
} }
const sdkVersion = lazy(() => parseInt(device.sdkVersion)); const sdkVersion = lazy(() => parseInt(device.sdkVersion));
const intEvaluator = lazy(() => new android.animation.IntEvaluator());
const defaultInterpolator = lazy(() => new android.view.animation.AccelerateDecelerateInterpolator()); const defaultInterpolator = lazy(() => new android.view.animation.AccelerateDecelerateInterpolator());
export const waitingQueue = new Map<number, Set<ExpandedEntry>>(); export const waitingQueue = new Map<number, Set<ExpandedEntry>>();
export const completedEntries = new Map<number, ExpandedEntry>(); export const completedEntries = new Map<number, ExpandedEntry>();
let TransitionListener: TransitionListener; let TransitionListener: TransitionListener;
let AnimationListener: android.view.animation.Animation.AnimationListener; let AnimationListener: android.animation.Animator.AnimatorListener;
export function _setAndroidFragmentTransitions( export function _setAndroidFragmentTransitions(
animated: boolean, animated: boolean,
@ -170,39 +171,39 @@ export function _setAndroidFragmentTransitions(
printTransitions(newEntry); printTransitions(newEntry);
} }
export function _onFragmentCreateAnimation(entry: ExpandedEntry, fragment: android.support.v4.app.Fragment, nextAnim: number, enter: boolean): android.view.animation.Animation { export function _onFragmentCreateAnimator(entry: ExpandedEntry, fragment: android.support.v4.app.Fragment, nextAnim: number, enter: boolean): android.animation.Animator {
let animation: android.view.animation.Animation; let animator: android.animation.Animator;
switch (nextAnim) { switch (nextAnim) {
case AnimationType.enterFakeResourceId: case AnimationType.enterFakeResourceId:
animation = entry.enterAnimation; animator = entry.enterAnimator;
break; break;
case AnimationType.exitFakeResourceId: case AnimationType.exitFakeResourceId:
animation = entry.exitAnimation; animator = entry.exitAnimator;
break; break;
case AnimationType.popEnterFakeResourceId: case AnimationType.popEnterFakeResourceId:
animation = entry.popEnterAnimation; animator = entry.popEnterAnimator;
break; break;
case AnimationType.popExitFakeResourceId: case AnimationType.popExitFakeResourceId:
animation = entry.popExitAnimation; animator = entry.popExitAnimator;
break; break;
} }
if (!animation && sdkVersion() >= 21) { if (!animator && sdkVersion() >= 21) {
const view = fragment.getView(); const view = fragment.getView();
const jsParent = entry.resolvedPage.parent; const jsParent = entry.resolvedPage.parent;
const parent = view.getParent() || (jsParent && jsParent.nativeViewProtected); const parent = view.getParent() || (jsParent && jsParent.nativeViewProtected);
const animatedEntries = _getAnimatedEntries(entry.frameId); const animatedEntries = _getAnimatedEntries(entry.frameId);
if (!animatedEntries || !animatedEntries.has(entry)) { if (!animatedEntries || !animatedEntries.has(entry)) {
if (parent && !(<any>parent).isLaidOut()) { if (parent && !(<any>parent).isLaidOut()) {
animation = enter ? entry.defaultEnterAnimation : entry.defaultExitAnimation; animator = enter ? entry.defaultEnterAnimator : entry.defaultExitAnimator;
} }
} }
} }
return animation; return animator;
} }
export function _getAnimatedEntries(frameId: number): Set<BackstackEntry> { export function _getAnimatedEntries(frameId: number): Set<BackstackEntry> {
@ -312,40 +313,40 @@ function getTransitionListener(entry: ExpandedEntry, transition: android.transit
return new TransitionListener(entry, transition); return new TransitionListener(entry, transition);
} }
function getAnimationListener(): android.view.animation.Animation.AnimationListener { function getAnimationListener(): android.animation.Animator.AnimatorListener {
if (!AnimationListener) { if (!AnimationListener) {
@Interfaces([android.view.animation.Animation.AnimationListener]) @Interfaces([android.animation.Animator.AnimatorListener])
class AnimationListenerImpl extends java.lang.Object implements android.view.animation.Animation.AnimationListener { class AnimationListenerImpl extends java.lang.Object implements android.animation.Animator.AnimatorListener {
constructor() { constructor() {
super(); super();
return global.__native(this); return global.__native(this);
} }
onAnimationStart(animation: ExpandedAnimation): void { onAnimationStart(animator: ExpandedAnimator): void {
const entry = animation.entry; const entry = animator.entry;
addToWaitingQueue(entry); addToWaitingQueue(entry);
if (traceEnabled()) { if (traceEnabled()) {
traceWrite(`START ${animation.transitionType} for ${entry.fragmentTag}`, traceCategories.Transition); traceWrite(`START ${animator.transitionType} for ${entry.fragmentTag}`, traceCategories.Transition);
} }
} }
onAnimationRepeat(animation: ExpandedAnimation): void { onAnimationRepeat(animator: ExpandedAnimator): void {
if (traceEnabled()) { if (traceEnabled()) {
traceWrite(`REPEAT ${animation.transitionType} for ${animation.entry.fragmentTag}`, traceCategories.Transition); traceWrite(`REPEAT ${animator.transitionType} for ${animator.entry.fragmentTag}`, traceCategories.Transition);
} }
} }
onAnimationEnd(animation: ExpandedAnimation): void { onAnimationEnd(animator: ExpandedAnimator): void {
if (traceEnabled()) { if (traceEnabled()) {
traceWrite(`END ${animation.transitionType} for ${animation.entry.fragmentTag}`, traceCategories.Transition); traceWrite(`END ${animator.transitionType} for ${animator.entry.fragmentTag}`, traceCategories.Transition);
} }
transitionOrAnimationCompleted(animation.entry); transitionOrAnimationCompleted(animator.entry);
} }
onAnimationCancel(animation: ExpandedAnimation): void { onAnimationCancel(animator: ExpandedAnimator): void {
if (traceEnabled()) { if (traceEnabled()) {
traceWrite(`CANCEL ${animation.transitionType} for ${animation.entry.fragmentTag}`, traceCategories.Transition); traceWrite(`CANCEL ${animator.transitionType} for ${animator.entry.fragmentTag}`, traceCategories.Transition);
} }
} }
} }
@ -367,18 +368,19 @@ function addToWaitingQueue(entry: ExpandedEntry): void {
entries.add(entry); entries.add(entry);
} }
function clearAnimationListener(animation: ExpandedAnimation): void { function clearAnimationListener(animator: ExpandedAnimator, listener: android.animation.Animator.AnimatorListener): void {
if (!animation) { if (!animator) {
return; return;
} }
animator.removeListener(listener);
if (traceEnabled()) { if (traceEnabled()) {
const entry = animation.entry; const entry = animator.entry;
traceWrite(`Clear ${animation.transitionType} - ${entry.transition} for ${entry.fragmentTag}`, traceCategories.Transition); traceWrite(`Clear ${animator.transitionType} - ${entry.transition} for ${entry.fragmentTag}`, traceCategories.Transition);
} }
animation.setAnimationListener(null); animator.entry = null;
animation.entry = null;
} }
function clearExitAndReenterTransitions(entry: ExpandedEntry, removeListener: boolean): void { function clearExitAndReenterTransitions(entry: ExpandedEntry, removeListener: boolean): void {
@ -477,10 +479,11 @@ function clearEntry(entry: ExpandedEntry, removeListener: boolean): void {
} }
if (removeListener) { if (removeListener) {
clearAnimationListener(entry.enterAnimation); const listener = getAnimationListener();
clearAnimationListener(entry.exitAnimation); clearAnimationListener(entry.enterAnimator, listener);
clearAnimationListener(entry.popEnterAnimation); clearAnimationListener(entry.exitAnimator, listener);
clearAnimationListener(entry.popExitAnimation); clearAnimationListener(entry.popEnterAnimator, listener);
clearAnimationListener(entry.popExitAnimator, listener);
} }
} }
@ -617,21 +620,21 @@ function setupNewFragmentExplodeTransition(navTransition: NavigationTransition,
function setupExitAndPopEnterAnimation(entry: ExpandedEntry, transition: Transition): void { function setupExitAndPopEnterAnimation(entry: ExpandedEntry, transition: Transition): void {
const listener = getAnimationListener(); const listener = getAnimationListener();
// remove previous listener if we are changing the animation. // remove previous listener if we are changing the animator.
clearAnimationListener(entry.exitAnimation); clearAnimationListener(entry.exitAnimator, listener);
clearAnimationListener(entry.popEnterAnimation); clearAnimationListener(entry.popEnterAnimator, listener);
const exitAnimation = <ExpandedAnimation>transition.createAndroidAnimation(AndroidTransitionType.exit); const exitAnimator = <ExpandedAnimator>transition.createAndroidAnimator(AndroidTransitionType.exit);
exitAnimation.transitionType = AndroidTransitionType.exit; exitAnimator.transitionType = AndroidTransitionType.exit;
exitAnimation.entry = entry; exitAnimator.entry = entry;
exitAnimation.setAnimationListener(listener); exitAnimator.addListener(listener);
entry.exitAnimation = exitAnimation; entry.exitAnimator = exitAnimator;
const popEnterAnimation = <ExpandedAnimation>transition.createAndroidAnimation(AndroidTransitionType.popEnter); const popEnterAnimator = <ExpandedAnimator>transition.createAndroidAnimator(AndroidTransitionType.popEnter);
popEnterAnimation.transitionType = AndroidTransitionType.popEnter; popEnterAnimator.transitionType = AndroidTransitionType.popEnter;
popEnterAnimation.entry = entry; popEnterAnimator.entry = entry;
popEnterAnimation.setAnimationListener(listener); popEnterAnimator.addListener(listener);
entry.popEnterAnimation = popEnterAnimation; entry.popEnterAnimator = popEnterAnimator;
} }
function setupAllAnimation(entry: ExpandedEntry, transition: Transition): void { function setupAllAnimation(entry: ExpandedEntry, transition: Transition): void {
@ -640,33 +643,33 @@ function setupAllAnimation(entry: ExpandedEntry, transition: Transition): void {
// setupAllAnimation is called only for new fragments so we don't // setupAllAnimation is called only for new fragments so we don't
// need to clearAnimationListener for enter & popExit animators. // need to clearAnimationListener for enter & popExit animators.
const enterAnimation = <ExpandedAnimation>transition.createAndroidAnimation(AndroidTransitionType.enter); const enterAnimator = <ExpandedAnimator>transition.createAndroidAnimator(AndroidTransitionType.enter);
enterAnimation.transitionType = AndroidTransitionType.enter; enterAnimator.transitionType = AndroidTransitionType.enter;
enterAnimation.entry = entry; enterAnimator.entry = entry;
enterAnimation.setAnimationListener(listener); enterAnimator.addListener(listener);
entry.enterAnimation = enterAnimation; entry.enterAnimator = enterAnimator;
const popExitAnimation = <ExpandedAnimation>transition.createAndroidAnimation(AndroidTransitionType.popExit); const popExitAnimator = <ExpandedAnimator>transition.createAndroidAnimator(AndroidTransitionType.popExit);
popExitAnimation.transitionType = AndroidTransitionType.popExit; popExitAnimator.transitionType = AndroidTransitionType.popExit;
popExitAnimation.entry = entry; popExitAnimator.entry = entry;
popExitAnimation.setAnimationListener(listener); popExitAnimator.addListener(listener);
entry.popExitAnimation = popExitAnimation; entry.popExitAnimator = popExitAnimator;
} }
function setupDefaultAnimations(entry: ExpandedEntry, transition: Transition): void { function setupDefaultAnimations(entry: ExpandedEntry, transition: Transition): void {
const listener = getAnimationListener(); const listener = getAnimationListener();
const enterAnimation = <ExpandedAnimation>transition.createAndroidAnimation(AndroidTransitionType.enter); const enterAnimator = <ExpandedAnimator>transition.createAndroidAnimator(AndroidTransitionType.enter);
enterAnimation.transitionType = AndroidTransitionType.enter; enterAnimator.transitionType = AndroidTransitionType.enter;
enterAnimation.entry = entry; enterAnimator.entry = entry;
enterAnimation.setAnimationListener(listener); enterAnimator.addListener(listener);
entry.defaultEnterAnimation = enterAnimation; entry.defaultEnterAnimator = enterAnimator;
const exitAnimation = <ExpandedAnimation>transition.createAndroidAnimation(AndroidTransitionType.exit); const exitAnimator = <ExpandedAnimator>transition.createAndroidAnimator(AndroidTransitionType.exit);
exitAnimation.transitionType = AndroidTransitionType.exit; exitAnimator.transitionType = AndroidTransitionType.exit;
exitAnimation.entry = entry; exitAnimator.entry = entry;
exitAnimation.setAnimationListener(listener); exitAnimator.addListener(listener);
entry.defaultExitAnimation = exitAnimation; entry.defaultExitAnimator = exitAnimator;
} }
function setUpNativeTransition(navigationTransition: NavigationTransition, nativeTransition: android.transition.Transition) { function setUpNativeTransition(navigationTransition: NavigationTransition, nativeTransition: android.transition.Transition) {
@ -729,10 +732,10 @@ function printTransitions(entry: ExpandedEntry) {
} }
if (entry.transition) { if (entry.transition) {
result += `enterAnimator=${entry.enterAnimation}, `; result += `enterAnimator=${entry.enterAnimator}, `;
result += `exitAnimator=${entry.exitAnimation}, `; result += `exitAnimator=${entry.exitAnimator}, `;
result += `popEnterAnimator=${entry.popEnterAnimation}, `; result += `popEnterAnimator=${entry.popEnterAnimator}, `;
result += `popExitAnimator=${entry.popExitAnimation}, `; result += `popExitAnimator=${entry.popExitAnimator}, `;
} }
if (sdkVersion() >= 21) { if (sdkVersion() >= 21) {
const fragment = entry.fragment; const fragment = entry.fragment;
@ -745,16 +748,20 @@ function printTransitions(entry: ExpandedEntry) {
} }
} }
class NoTransition extends Transition { function javaObjectArray(...params: java.lang.Object[]) {
public createAndroidAnimation(transitionType: string): android.view.animation.Animation { const nativeArray = Array.create(java.lang.Object, params.length);
const animation = new android.view.animation.AlphaAnimation(1, 1); params.forEach((value, i) => nativeArray[i] = value);
// NOTE: this should not be necessary when we revert to Animators API return nativeArray;
// HACK: Android view animation with zero duration seems to be buggy and raises animation listener events in illogical (wrong?) order: }
// "enter" start -> "enter" end -> "exit" start -> "exit" end;
// we would expect events to overlap "exit" start -> "enter" start -> "exit" end -> "enter" end, or at least
// "exit" start / end to be raised before "enter" start / end
animation.setDuration(1);
return animation; function createDummyZeroDurationAnimator(): android.animation.Animator {
const animator = android.animation.ValueAnimator.ofObject(intEvaluator(), javaObjectArray(java.lang.Integer.valueOf(0), java.lang.Integer.valueOf(1)));
animator.setDuration(0);
return animator;
}
class NoTransition extends Transition {
public createAndroidAnimator(transitionType: string): android.animation.Animator {
return createDummyZeroDurationAnimator();
} }
} }

View File

@ -28,7 +28,7 @@ export function _setAndroidFragmentTransitions(
/** /**
* @private * @private
*/ */
export function _onFragmentCreateAnimation(entry: BackstackEntry, fragment: any, nextAnim: number, enter: boolean): any; export function _onFragmentCreateAnimator(entry: BackstackEntry, fragment: any, nextAnim: number, enter: boolean): any;
/** /**
* @private * @private
*/ */

View File

@ -13,7 +13,7 @@ import {
} from "./frame-common"; } from "./frame-common";
import { import {
_setAndroidFragmentTransitions, _onFragmentCreateAnimation, _getAnimatedEntries, _setAndroidFragmentTransitions, _onFragmentCreateAnimator, _getAnimatedEntries,
_updateTransitions, _reverseTransitions, _clearEntry, _clearFragment, AnimationType _updateTransitions, _reverseTransitions, _clearEntry, _clearFragment, AnimationType
} from "./fragment.transitions"; } from "./fragment.transitions";
@ -667,7 +667,7 @@ class FragmentCallbacksImplementation implements AndroidFragmentCallbacks {
} }
@profile @profile
public onCreateAnimation(fragment: android.support.v4.app.Fragment, transit: number, enter: boolean, nextAnim: number, superFunc: Function): android.view.animation.Animation { public onCreateAnimator(fragment: android.support.v4.app.Fragment, transit: number, enter: boolean, nextAnim: number, superFunc: Function): android.animation.Animator {
let nextAnimString: string; let nextAnimString: string;
switch (nextAnim) { switch (nextAnim) {
case AnimationType.enterFakeResourceId: nextAnimString = "enter"; break; case AnimationType.enterFakeResourceId: nextAnimString = "enter"; break;
@ -676,16 +676,16 @@ class FragmentCallbacksImplementation implements AndroidFragmentCallbacks {
case AnimationType.popExitFakeResourceId: nextAnimString = "popExit"; break; case AnimationType.popExitFakeResourceId: nextAnimString = "popExit"; break;
} }
let animation = _onFragmentCreateAnimation(this.entry, fragment, nextAnim, enter); let animator = _onFragmentCreateAnimator(this.entry, fragment, nextAnim, enter);
if (!animation) { if (!animator) {
animation = superFunc.call(fragment, transit, enter, nextAnim); animator = superFunc.call(fragment, transit, enter, nextAnim);
} }
if (traceEnabled()) { if (traceEnabled()) {
traceWrite(`${fragment}.onCreateAnimation(${transit}, ${enter ? "enter" : "exit"}, ${nextAnimString}): ${animation ? "animation" : "no animation"}`, traceCategories.NativeLifecycle); traceWrite(`${fragment}.onCreateAnimator(${transit}, ${enter ? "enter" : "exit"}, ${nextAnimString}): ${animator ? "animator" : "no animator"}`, traceCategories.NativeLifecycle);
} }
return animation; return animator;
} }
@profile @profile

View File

@ -414,7 +414,7 @@ export interface AndroidActivityCallbacks {
export interface AndroidFragmentCallbacks { export interface AndroidFragmentCallbacks {
onHiddenChanged(fragment: any, hidden: boolean, superFunc: Function): void; onHiddenChanged(fragment: any, hidden: boolean, superFunc: Function): void;
onCreateAnimation(fragment: any, transit: number, enter: boolean, nextAnim: number, superFunc: Function): any; onCreateAnimator(fragment: any, transit: number, enter: boolean, nextAnim: number, superFunc: Function): any;
onCreate(fragment: any, savedInstanceState: any, superFunc: Function): void; onCreate(fragment: any, savedInstanceState: any, superFunc: Function): void;
onCreateView(fragment: any, inflater: any, container: any, savedInstanceState: any, superFunc: Function): any; onCreateView(fragment: any, inflater: any, container: any, savedInstanceState: any, superFunc: Function): any;
onSaveInstanceState(fragment: any, outState: any, superFunc: Function): void; onSaveInstanceState(fragment: any, outState: any, superFunc: Function): void;

View File

@ -1,29 +1,28 @@
import { Transition, AndroidTransitionType } from "./transition"; import { Transition, AndroidTransitionType } from "./transition";
export class FadeTransition extends Transition { export class FadeTransition extends Transition {
public createAndroidAnimation(transitionType: string): android.view.animation.Animation { public createAndroidAnimator(transitionType: string): android.animation.Animator {
const alphaValues = []; const alphaValues = Array.create("float", 2);
switch (transitionType) { switch (transitionType) {
case AndroidTransitionType.enter: case AndroidTransitionType.enter:
case AndroidTransitionType.popEnter: case AndroidTransitionType.popEnter:
alphaValues[0] = 0.0; alphaValues[0] = 0;
alphaValues[1] = 1.0; alphaValues[1] = 1;
break; break;
case AndroidTransitionType.exit: case AndroidTransitionType.exit:
case AndroidTransitionType.popExit: case AndroidTransitionType.popExit:
alphaValues[0] = 1.0; alphaValues[0] = 1;
alphaValues[1] = 0.0; alphaValues[1] = 0;
break; break;
} }
const animation = new android.view.animation.AlphaAnimation(alphaValues[0], alphaValues[1]); const animator = android.animation.ObjectAnimator.ofFloat(null, "alpha", alphaValues);
const duration = this.getDuration(); const duration = this.getDuration();
if (duration !== undefined) { if (duration !== undefined) {
animation.setDuration(duration); animator.setDuration(duration);
} }
animation.setInterpolator(this.getCurve()); animator.setInterpolator(this.getCurve());
return animator;
return animation;
} }
} }

View File

@ -9,117 +9,109 @@ export class FlipTransition extends Transition {
this._direction = direction; this._direction = direction;
} }
public createAndroidAnimation(transitionType: string): android.view.animation.Animation { public createAndroidAnimator(transitionType: string): android.animation.Animator {
ensureRotate3dAnimationClass(); let objectAnimators;
let values;
let animation: android.view.animation.Animation; let animator: android.animation.ObjectAnimator;
let animationSet: android.view.animation.AnimationSet const animatorSet = new android.animation.AnimatorSet();
let rotateAnimation: android.view.animation.Animation;
let alphaAnimation: android.view.animation.Animation;
const fullDuration = this.getDuration() || 300; const fullDuration = this.getDuration() || 300;
const interpolator = this.getCurve(); const interpolator = this.getCurve();
const rotationY = this._direction === "right" ? 180 : -180; const rotationY = this._direction === "right" ? 180 : -180;
switch (transitionType) { switch (transitionType) {
case AndroidTransitionType.enter: // card_flip_right_in case AndroidTransitionType.enter: // card_flip_right_in
animation = new Rotate3dAnimationClass(rotationY, 0.0, 0.5, 0.5); objectAnimators = Array.create(android.animation.Animator, 3);
animation.setInterpolator(interpolator);
animation.setDuration(fullDuration); values = Array.create("float", 2);
values[0] = 1.0;
values[1] = 0.0;
animator = android.animation.ObjectAnimator.ofFloat(null, "alpha", values);
animator.setDuration(0);
objectAnimators[0] = animator;
values = Array.create("float", 2);
values[0] = rotationY;
values[1] = 0.0;
animator = android.animation.ObjectAnimator.ofFloat(null, "rotationY", values);
animator.setInterpolator(interpolator);
animator.setDuration(fullDuration);
objectAnimators[1] = animator;
values = Array.create("float", 2);
values[0] = 0.0;
values[1] = 1.0;
animator = android.animation.ObjectAnimator.ofFloat(null, "alpha", values);
animator.setStartDelay(fullDuration / 2);
animator.setDuration(1);
objectAnimators[2] = animator;
break; break;
case AndroidTransitionType.exit: // card_flip_right_out case AndroidTransitionType.exit: // card_flip_right_out
animation = animationSet = new android.view.animation.AnimationSet(false /* shareInterpolator */); objectAnimators = Array.create(android.animation.Animator, 2);
rotateAnimation = new Rotate3dAnimationClass(0.0, -rotationY, 0.5, 0.5); values = Array.create("float", 2);
rotateAnimation.setInterpolator(interpolator); values[0] = 0.0;
rotateAnimation.setDuration(fullDuration); values[1] = -rotationY;
animationSet.addAnimation(rotateAnimation); animator = android.animation.ObjectAnimator.ofFloat(null, "rotationY", values);
animator.setInterpolator(interpolator);
animator.setDuration(fullDuration);
objectAnimators[0] = animator;
alphaAnimation = new android.view.animation.AlphaAnimation(1.0, 0.0); values = Array.create("float", 2);
alphaAnimation.setStartOffset(fullDuration / 2); values[0] = 1.0;
alphaAnimation.setDuration(1); values[1] = 0.0;
animationSet.addAnimation(alphaAnimation); animator = android.animation.ObjectAnimator.ofFloat(null, "alpha", values);
animator.setStartDelay(fullDuration / 2);
animator.setDuration(1);
objectAnimators[1] = animator;
break; break;
case AndroidTransitionType.popEnter: // card_flip_left_in case AndroidTransitionType.popEnter: // card_flip_left_in
animation = new Rotate3dAnimationClass(-rotationY, 0.0, 0.5, 0.5); objectAnimators = Array.create(android.animation.Animator, 3);
animation.setInterpolator(interpolator);
animation.setDuration(fullDuration); values = Array.create("float", 2);
values[0] = 1.0;
values[1] = 0.0;
animator = android.animation.ObjectAnimator.ofFloat(null, "alpha", values);
animator.setDuration(0);
objectAnimators[0] = animator;
values = Array.create("float", 2);
values[0] = -rotationY;
values[1] = 0.0;
animator = android.animation.ObjectAnimator.ofFloat(null, "rotationY", values);
animator.setInterpolator(interpolator);
animator.setDuration(fullDuration);
objectAnimators[1] = animator;
values = Array.create("float", 2);
values[0] = 0.0;
values[1] = 1.0;
animator = android.animation.ObjectAnimator.ofFloat(null, "alpha", values);
animator.setStartDelay(fullDuration / 2);
animator.setDuration(1);
objectAnimators[2] = animator;
break; break;
case AndroidTransitionType.popExit: // card_flip_left_out case AndroidTransitionType.popExit: // card_flip_left_out
animation = animationSet = new android.view.animation.AnimationSet(false /* shareInterpolator */); objectAnimators = Array.create(android.animation.Animator, 2);
rotateAnimation = new Rotate3dAnimationClass(0.0, rotationY, 0.5, 0.5); values = Array.create("float", 2);
rotateAnimation.setInterpolator(interpolator); values[0] = 0.0;
rotateAnimation.setDuration(fullDuration); values[1] = rotationY;
animationSet.addAnimation(rotateAnimation); animator = android.animation.ObjectAnimator.ofFloat(null, "rotationY", values);
animator.setInterpolator(interpolator);
animator.setDuration(fullDuration);
objectAnimators[0] = animator;
alphaAnimation = new android.view.animation.AlphaAnimation(1.0, 0.0); values = Array.create("float", 2);
alphaAnimation.setStartOffset(fullDuration / 2); values[0] = 1.0;
alphaAnimation.setDuration(1); values[1] = 0.0;
animationSet.addAnimation(alphaAnimation); animator = android.animation.ObjectAnimator.ofFloat(null, "alpha", values);
animator.setStartDelay(fullDuration / 2);
animator.setDuration(1);
objectAnimators[1] = animator;
break; break;
} }
return animation; animatorSet.playTogether(objectAnimators);
return animatorSet;
} }
} }
let Rotate3dAnimationClass;
function ensureRotate3dAnimationClass() {
if (Rotate3dAnimationClass) {
return;
}
/**
* Creates a new 3D rotation on the Y axis. The rotation is defined by its
* start angle and its end angle. Both angles are in degrees. The rotation
* is performed around a center point on the 2D space, definied by a pair
* of X and Y coordinates, called centerX and centerY.
*
* @param fromDegrees the start angle of the 3D rotation
* @param toDegrees the end angle of the 3D rotation
* @param centerX the X center of the 3D rotation (relative to self)
* @param centerY the Y center of the 3D rotation (relative to self)
*/
class Rotate3dAnimation extends android.view.animation.Animation {
private _camera: android.graphics.Camera;
private _pivotX: number;
private _pivotY: number;
constructor(
private _fromDegrees: number,
private _toDegrees: number,
private _centerX: number,
private _centerY: number) {
super();
return global.__native(this);
}
public initialize(width: number, height: number, parentWidth: number, parentHeight: number) {
super.initialize(width, height, parentWidth, parentHeight);
this._pivotX = this.resolveSize(android.view.animation.Animation.RELATIVE_TO_SELF, this._centerX, width, parentWidth);
this._pivotY = this.resolveSize(android.view.animation.Animation.RELATIVE_TO_SELF, this._centerY, height, parentHeight);
this._camera = new android.graphics.Camera();
}
public applyTransformation(interpolatedTime: number, t: android.view.animation.Transformation) {
const fromDegrees = this._fromDegrees;
const degrees = fromDegrees + ((this._toDegrees - fromDegrees) * interpolatedTime);
const pivotX = this._pivotX;
const pivotY = this._pivotY;
const camera = this._camera;
const matrix: android.graphics.Matrix = t.getMatrix();
camera.save();
camera.rotateY(degrees);
camera.getMatrix(matrix);
camera.restore();
matrix.preTranslate(-pivotX, -pivotY);
matrix.postTranslate(pivotX, pivotY);
}
}
Rotate3dAnimationClass = Rotate3dAnimation;
}

View File

@ -13,8 +13,8 @@ export class SlideTransition extends transition.Transition {
this._direction = direction; this._direction = direction;
} }
public createAndroidAnimation(transitionType: string): android.view.animation.Animation { public createAndroidAnimator(transitionType: string): android.animation.Animator {
const translationValues = []; const translationValues = Array.create("float", 2);
switch (this._direction) { switch (this._direction) {
case "left": case "left":
switch (transitionType) { switch (transitionType) {
@ -98,22 +98,22 @@ export class SlideTransition extends transition.Transition {
break; break;
} }
let animation; let prop;
if (this._direction === "left" || this._direction === "right") { if (this._direction === "left" || this._direction === "right") {
animation = new android.view.animation.TranslateAnimation(translationValues[0], translationValues[1], 0, 0); prop = "translationX";
} }
else { else {
animation = new android.view.animation.TranslateAnimation(0, 0, translationValues[0], translationValues[1]); prop = "translationY";
} }
const animator = android.animation.ObjectAnimator.ofFloat(null, prop, translationValues);
const duration = this.getDuration(); const duration = this.getDuration();
if (duration !== undefined) { if (duration !== undefined) {
animation.setDuration(duration); animator.setDuration(duration);
} }
animator.setInterpolator(this.getCurve());
animation.setInterpolator(this.getCurve()); return animator;
return animation;
} }
public toString(): string { public toString(): string {

View File

@ -38,7 +38,7 @@ export class Transition implements TransitionDefinition {
throw new Error("Abstract method call"); throw new Error("Abstract method call");
} }
public createAndroidAnimation(transitionType: string): android.view.animation.Animation { public createAndroidAnimator(transitionType: string): android.animation.Animator {
throw new Error("Abstract method call"); throw new Error("Abstract method call");
} }

View File

@ -14,6 +14,6 @@ export class Transition {
public getDuration(): number; public getDuration(): number;
public getCurve(): any; public getCurve(): any;
public animateIOSTransition(containerView: any, fromView: any, toView: any, operation: any, completion: (finished: boolean) => void): void; public animateIOSTransition(containerView: any, fromView: any, toView: any, operation: any, completion: (finished: boolean) => void): void;
public createAndroidAnimation(transitionType: string): any; public createAndroidAnimator(transitionType: string): any;
public toString(): string; public toString(): string;
} }

View File

@ -24,7 +24,7 @@ export class Transition implements TransitionDefinition {
throw new Error("Abstract method call"); throw new Error("Abstract method call");
} }
public createAndroidAnimation(transitionType: string): any { public createAndroidAnimator(transitionType: string): any {
throw new Error("Abstract method call"); throw new Error("Abstract method call");
} }