mirror of
https://github.com/NativeScript/NativeScript.git
synced 2025-08-18 13:22:19 +08:00
fix(android): rewrote the transition system
to allow it work correctly with GLSurfaceView and GLTextureView
This commit is contained in:
@ -11,6 +11,10 @@ const FragmentClass = (<any>org.nativescript.widgets.FragmentBase).extend('com.t
|
||||
return this._callbacks.onCreateAnimator(this, transit, enter, nextAnim, superProto.onCreateAnimator);
|
||||
},
|
||||
|
||||
onCreateAnimation(transit: number, enter: boolean, nextAnim: number): android.view.animation.Animation {
|
||||
return this._callbacks.onCreateAnimation(this, transit, enter, nextAnim, superProto.onCreateAnimation);
|
||||
},
|
||||
|
||||
onStop(): void {
|
||||
this._callbacks.onStop(this, superProto.onStop);
|
||||
},
|
||||
|
@ -8,6 +8,8 @@ import { FlipTransition } from '../transition/flip-transition';
|
||||
import { _resolveAnimationCurve } from '../animation';
|
||||
import lazy from '../../utils/lazy';
|
||||
import { Trace } from '../../trace';
|
||||
import { FadeTransition } from 'ui/transition/fade-transition.android';
|
||||
import { SlideTransition } from 'ui/transition/slide-transition.android';
|
||||
|
||||
interface TransitionListener {
|
||||
new (entry: ExpandedEntry, transition: androidx.transition.Transition): ExpandedTransitionListener;
|
||||
@ -66,43 +68,39 @@ export function _setAndroidFragmentTransitions(animated: boolean, navigationTran
|
||||
allowTransitionOverlap(newFragment);
|
||||
|
||||
let name = '';
|
||||
let transition: Transition;
|
||||
let customTransition: Transition;
|
||||
|
||||
if (navigationTransition) {
|
||||
transition = navigationTransition.instance;
|
||||
customTransition = navigationTransition.instance;
|
||||
name = navigationTransition.name ? navigationTransition.name.toLowerCase() : '';
|
||||
}
|
||||
|
||||
if (!animated) {
|
||||
name = 'none';
|
||||
} else if (transition) {
|
||||
} else if (customTransition) {
|
||||
name = 'custom';
|
||||
} else if (name.indexOf('slide') !== 0 && name !== 'fade' && name.indexOf('flip') !== 0 && name.indexOf('explode') !== 0) {
|
||||
} else if (name.indexOf('slide') !== 0 && name !== 'fade' && name.indexOf('flip') !== 0) {
|
||||
// If we are given name that doesn't match any of ours - fallback to default.
|
||||
name = 'default';
|
||||
}
|
||||
|
||||
let currentFragmentNeedsDifferentAnimation = false;
|
||||
if (currentEntry) {
|
||||
_updateTransitions(currentEntry);
|
||||
if (currentEntry.transitionName !== name || currentEntry.transition !== transition || isNestedDefaultTransition) {
|
||||
if (currentEntry.transitionName !== name || currentEntry.transition !== customTransition || isNestedDefaultTransition) {
|
||||
clearExitAndReenterTransitions(currentEntry, true);
|
||||
currentFragmentNeedsDifferentAnimation = true;
|
||||
}
|
||||
}
|
||||
|
||||
let transition: Transition;
|
||||
if (name === 'none') {
|
||||
const noTransition = new NoTransition(0, null);
|
||||
|
||||
// Setup empty/immediate animator when transitioning to nested frame for first time.
|
||||
// Also setup empty/immediate transition to be executed when navigating back to this page.
|
||||
// TODO: Consider removing empty/immediate animator when migrating to official androidx.fragment.app.Fragment:1.2.
|
||||
if (isNestedDefaultTransition) {
|
||||
fragmentTransaction.setCustomAnimations(animFadeIn, animFadeOut);
|
||||
setupAllAnimation(newEntry, noTransition);
|
||||
setupNewFragmentCustomTransition({ duration: 0, curve: null }, newEntry, noTransition);
|
||||
} else {
|
||||
setupNewFragmentCustomTransition({ duration: 0, curve: null }, newEntry, noTransition);
|
||||
// with androidx.fragment 1.3.0 the first fragment animation is not triggered
|
||||
// so let's simulate a transition end
|
||||
setTimeout(() => {
|
||||
addToWaitingQueue(newEntry);
|
||||
transitionOrAnimationCompleted(newEntry, null);
|
||||
});
|
||||
}
|
||||
|
||||
newEntry.isNestedDefaultTransition = isNestedDefaultTransition;
|
||||
@ -110,52 +108,40 @@ export function _setAndroidFragmentTransitions(animated: boolean, navigationTran
|
||||
if (currentFragmentNeedsDifferentAnimation) {
|
||||
setupCurrentFragmentCustomTransition({ duration: 0, curve: null }, currentEntry, noTransition);
|
||||
}
|
||||
} else if (name === 'custom') {
|
||||
} else if (customTransition) {
|
||||
setupNewFragmentCustomTransition(
|
||||
{
|
||||
duration: transition.getDuration(),
|
||||
curve: transition.getCurve(),
|
||||
duration: customTransition.getDuration(),
|
||||
curve: customTransition.getCurve(),
|
||||
},
|
||||
newEntry,
|
||||
transition
|
||||
customTransition
|
||||
);
|
||||
if (currentFragmentNeedsDifferentAnimation) {
|
||||
setupCurrentFragmentCustomTransition(
|
||||
{
|
||||
duration: transition.getDuration(),
|
||||
curve: transition.getCurve(),
|
||||
duration: customTransition.getDuration(),
|
||||
curve: customTransition.getCurve(),
|
||||
},
|
||||
currentEntry,
|
||||
transition
|
||||
customTransition
|
||||
);
|
||||
}
|
||||
} else if (name === 'default') {
|
||||
setupNewFragmentFadeTransition({ duration: 150, curve: null }, newEntry);
|
||||
if (currentFragmentNeedsDifferentAnimation) {
|
||||
setupCurrentFragmentFadeTransition({ duration: 150, curve: null }, currentEntry);
|
||||
}
|
||||
transition = new FadeTransition(150, null);
|
||||
} else if (name.indexOf('slide') === 0) {
|
||||
setupNewFragmentSlideTransition(navigationTransition, newEntry, name);
|
||||
if (currentFragmentNeedsDifferentAnimation) {
|
||||
setupCurrentFragmentSlideTransition(navigationTransition, currentEntry, name);
|
||||
}
|
||||
const direction = name.substr('slide'.length) || 'left'; //Extract the direction from the string
|
||||
transition = new SlideTransition(direction, navigationTransition.duration, navigationTransition.curve);
|
||||
} else if (name === 'fade') {
|
||||
setupNewFragmentFadeTransition(navigationTransition, newEntry);
|
||||
if (currentFragmentNeedsDifferentAnimation) {
|
||||
setupCurrentFragmentFadeTransition(navigationTransition, currentEntry);
|
||||
}
|
||||
} else if (name === 'explode') {
|
||||
setupNewFragmentExplodeTransition(navigationTransition, newEntry);
|
||||
if (currentFragmentNeedsDifferentAnimation) {
|
||||
setupCurrentFragmentExplodeTransition(navigationTransition, currentEntry);
|
||||
}
|
||||
transition = new FadeTransition(navigationTransition.duration, navigationTransition.curve);
|
||||
} else if (name.indexOf('flip') === 0) {
|
||||
const direction = name.substr('flip'.length) || 'right'; //Extract the direction from the string
|
||||
const flipTransition = new FlipTransition(direction, navigationTransition.duration, navigationTransition.curve);
|
||||
|
||||
setupNewFragmentCustomTransition(navigationTransition, newEntry, flipTransition);
|
||||
transition = new FlipTransition(direction, navigationTransition.duration, navigationTransition.curve);
|
||||
}
|
||||
if (transition) {
|
||||
setupNewFragmentCustomTransition(navigationTransition, newEntry, transition);
|
||||
if (currentFragmentNeedsDifferentAnimation) {
|
||||
setupCurrentFragmentCustomTransition(navigationTransition, currentEntry, flipTransition);
|
||||
setupCurrentFragmentCustomTransition(navigationTransition, currentEntry, transition);
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -88,6 +88,7 @@ export interface AndroidActivityCallbacks {
|
||||
export interface AndroidFragmentCallbacks {
|
||||
onHiddenChanged(fragment: any, hidden: boolean, superFunc: Function): void;
|
||||
onCreateAnimator(fragment: any, transit: number, enter: boolean, nextAnim: number, superFunc: Function): any;
|
||||
onCreateAnimation(fragment: any, transit: number, enter: boolean, nextAnim: number): any;
|
||||
onCreate(fragment: any, savedInstanceState: any, superFunc: Function): void;
|
||||
onCreateView(fragment: any, inflater: any, container: any, savedInstanceState: any, superFunc: Function): any;
|
||||
onSaveInstanceState(fragment: any, outState: any, superFunc: Function): void;
|
||||
|
@ -476,10 +476,10 @@ export class Frame extends FrameBase {
|
||||
|
||||
_reverseTransitions(backstackEntry, this._currentEntry);
|
||||
|
||||
const currentIndex =this.backStack.length;
|
||||
const currentIndex = this.backStack.length;
|
||||
const goBackToIndex = this.backStack.indexOf(backstackEntry);
|
||||
|
||||
// the order is important so that the transition listener called be
|
||||
|
||||
// the order is important so that the transition listener called be
|
||||
// the one from the current entry we are going back from
|
||||
if (this._currentEntry !== backstackEntry) {
|
||||
const entry = this._currentEntry as ExpandedEntry;
|
||||
@ -489,8 +489,9 @@ export class Frame extends FrameBase {
|
||||
if (entry.returnTransitionListener) {
|
||||
entry.returnTransitionListener.backEntry = backstackEntry;
|
||||
}
|
||||
|
||||
transaction.remove((this._currentEntry).fragment);
|
||||
|
||||
// we only did hide the fragment to fix some black blick issues with GLSurfaceView and GLTextureView
|
||||
transaction.hide(this._currentEntry.fragment);
|
||||
}
|
||||
for (let index = goBackToIndex + 1; index < currentIndex; index++) {
|
||||
transaction.remove(this.backStack[index].fragment);
|
||||
@ -501,8 +502,13 @@ export class Frame extends FrameBase {
|
||||
|
||||
public _removeEntry(removed: BackstackEntry): void {
|
||||
super._removeEntry(removed);
|
||||
|
||||
if (removed.fragment) {
|
||||
// we only did hide the fragment to fix some black blick issues with GLSurfaceView and GLTextureView
|
||||
// so lets remove it
|
||||
const manager: androidx.fragment.app.FragmentManager = this._getFragmentManager();
|
||||
const transaction = manager.beginTransaction();
|
||||
transaction.remove(removed.fragment);
|
||||
transaction.commitAllowingStateLoss();
|
||||
_clearEntry(removed);
|
||||
}
|
||||
|
||||
@ -788,11 +794,11 @@ function findPageForFragment(fragment: androidx.fragment.app.Fragment, frame: Fr
|
||||
} else if (executingContext && executingContext.entry && executingContext.entry.fragmentTag === fragmentTag) {
|
||||
entry = executingContext.entry;
|
||||
} else {
|
||||
frame.backStack.forEach(e=>{
|
||||
frame.backStack.forEach((e) => {
|
||||
if (e && e.fragmentTag === fragmentTag) {
|
||||
entry = e;
|
||||
}
|
||||
})
|
||||
});
|
||||
}
|
||||
|
||||
let page: Page;
|
||||
@ -873,15 +879,24 @@ class FragmentCallbacksImplementation implements AndroidFragmentCallbacks {
|
||||
public onCreateAnimator(fragment: androidx.fragment.app.Fragment, transit: number, enter: boolean, nextAnim: number, superFunc: Function): android.animation.Animator {
|
||||
let animator = null;
|
||||
const entry = <any>this.entry;
|
||||
if (Trace.isEnabled()) {
|
||||
Trace.write(`${fragment}.onCreateAnimator(${transit},${enter}, ${nextAnim})`, Trace.categories.Animation);
|
||||
}
|
||||
|
||||
// Return enterAnimator only when new (no current entry) nested transition.
|
||||
if (enter && entry.isNestedDefaultTransition) {
|
||||
animator = entry.enterAnimator;
|
||||
entry.isNestedDefaultTransition = false;
|
||||
}
|
||||
|
||||
return animator || superFunc.call(fragment, transit, enter, nextAnim);
|
||||
}
|
||||
@profile
|
||||
public onCreateAnimation(fragment: androidx.fragment.app.Fragment, transit: number, enter: boolean, nextAnim: number, superFunc: Function): globalAndroid.view.animation.Animation {
|
||||
if (Trace.isEnabled()) {
|
||||
Trace.write(`${fragment}.onCreateAnimation(${transit},${enter}, ${nextAnim})`, Trace.categories.Animation);
|
||||
}
|
||||
return superFunc.call(fragment, transit, enter, nextAnim);
|
||||
}
|
||||
|
||||
@profile
|
||||
public onCreate(fragment: androidx.fragment.app.Fragment, savedInstanceState: android.os.Bundle, superFunc: Function): void {
|
||||
@ -988,11 +1003,11 @@ class FragmentCallbacksImplementation implements AndroidFragmentCallbacks {
|
||||
|
||||
@profile
|
||||
public onDestroyView(fragment: org.nativescript.widgets.FragmentBase, superFunc: Function): void {
|
||||
if (Trace.isEnabled()) {
|
||||
Trace.write(`${fragment}.onDestroyView()`, Trace.categories.NativeLifecycle);
|
||||
}
|
||||
superFunc.call(fragment);
|
||||
if (Trace.isEnabled()) {
|
||||
Trace.write(`${fragment}.onDestroyView()`, Trace.categories.NativeLifecycle);
|
||||
}
|
||||
superFunc.call(fragment);
|
||||
}
|
||||
|
||||
@profile
|
||||
public onDestroy(fragment: androidx.fragment.app.Fragment, superFunc: Function): void {
|
||||
@ -1014,18 +1029,11 @@ class FragmentCallbacksImplementation implements AndroidFragmentCallbacks {
|
||||
// "IllegalStateException: Failure saving state: active fragment has cleared index: -1"
|
||||
// in a specific mixed parent / nested frame navigation scenario
|
||||
entry.fragment = null;
|
||||
|
||||
const page = entry.resolvedPage;
|
||||
if (!page) {
|
||||
Trace.error(`${fragment}.onDestroy: entry has no resolvedPage`);
|
||||
|
||||
return null;
|
||||
}
|
||||
}
|
||||
|
||||
@profile
|
||||
public onPause(fragment: org.nativescript.widgets.FragmentBase, superFunc: Function): void {
|
||||
superFunc.call(fragment);
|
||||
superFunc.call(fragment);
|
||||
}
|
||||
|
||||
@profile
|
||||
|
Reference in New Issue
Block a user