mirror of
https://github.com/NativeScript/NativeScript.git
synced 2025-08-20 15:34:26 +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);
|
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 {
|
onStop(): void {
|
||||||
this._callbacks.onStop(this, superProto.onStop);
|
this._callbacks.onStop(this, superProto.onStop);
|
||||||
},
|
},
|
||||||
|
@ -8,6 +8,8 @@ import { FlipTransition } from '../transition/flip-transition';
|
|||||||
import { _resolveAnimationCurve } from '../animation';
|
import { _resolveAnimationCurve } from '../animation';
|
||||||
import lazy from '../../utils/lazy';
|
import lazy from '../../utils/lazy';
|
||||||
import { Trace } from '../../trace';
|
import { Trace } from '../../trace';
|
||||||
|
import { FadeTransition } from 'ui/transition/fade-transition.android';
|
||||||
|
import { SlideTransition } from 'ui/transition/slide-transition.android';
|
||||||
|
|
||||||
interface TransitionListener {
|
interface TransitionListener {
|
||||||
new (entry: ExpandedEntry, transition: androidx.transition.Transition): ExpandedTransitionListener;
|
new (entry: ExpandedEntry, transition: androidx.transition.Transition): ExpandedTransitionListener;
|
||||||
@ -66,43 +68,39 @@ export function _setAndroidFragmentTransitions(animated: boolean, navigationTran
|
|||||||
allowTransitionOverlap(newFragment);
|
allowTransitionOverlap(newFragment);
|
||||||
|
|
||||||
let name = '';
|
let name = '';
|
||||||
let transition: Transition;
|
let customTransition: Transition;
|
||||||
|
|
||||||
if (navigationTransition) {
|
if (navigationTransition) {
|
||||||
transition = navigationTransition.instance;
|
customTransition = navigationTransition.instance;
|
||||||
name = navigationTransition.name ? navigationTransition.name.toLowerCase() : '';
|
name = navigationTransition.name ? navigationTransition.name.toLowerCase() : '';
|
||||||
}
|
}
|
||||||
|
|
||||||
if (!animated) {
|
if (!animated) {
|
||||||
name = 'none';
|
name = 'none';
|
||||||
} else if (transition) {
|
} else if (customTransition) {
|
||||||
name = 'custom';
|
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.
|
// If we are given name that doesn't match any of ours - fallback to default.
|
||||||
name = 'default';
|
name = 'default';
|
||||||
}
|
}
|
||||||
|
|
||||||
let currentFragmentNeedsDifferentAnimation = false;
|
let currentFragmentNeedsDifferentAnimation = false;
|
||||||
if (currentEntry) {
|
if (currentEntry) {
|
||||||
_updateTransitions(currentEntry);
|
_updateTransitions(currentEntry);
|
||||||
if (currentEntry.transitionName !== name || currentEntry.transition !== transition || isNestedDefaultTransition) {
|
if (currentEntry.transitionName !== name || currentEntry.transition !== customTransition || isNestedDefaultTransition) {
|
||||||
clearExitAndReenterTransitions(currentEntry, true);
|
clearExitAndReenterTransitions(currentEntry, true);
|
||||||
currentFragmentNeedsDifferentAnimation = true;
|
currentFragmentNeedsDifferentAnimation = true;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
let transition: Transition;
|
||||||
if (name === 'none') {
|
if (name === 'none') {
|
||||||
const noTransition = new NoTransition(0, null);
|
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) {
|
if (isNestedDefaultTransition) {
|
||||||
fragmentTransaction.setCustomAnimations(animFadeIn, animFadeOut);
|
// with androidx.fragment 1.3.0 the first fragment animation is not triggered
|
||||||
setupAllAnimation(newEntry, noTransition);
|
// so let's simulate a transition end
|
||||||
setupNewFragmentCustomTransition({ duration: 0, curve: null }, newEntry, noTransition);
|
setTimeout(() => {
|
||||||
} else {
|
addToWaitingQueue(newEntry);
|
||||||
setupNewFragmentCustomTransition({ duration: 0, curve: null }, newEntry, noTransition);
|
transitionOrAnimationCompleted(newEntry, null);
|
||||||
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
newEntry.isNestedDefaultTransition = isNestedDefaultTransition;
|
newEntry.isNestedDefaultTransition = isNestedDefaultTransition;
|
||||||
@ -110,52 +108,40 @@ export function _setAndroidFragmentTransitions(animated: boolean, navigationTran
|
|||||||
if (currentFragmentNeedsDifferentAnimation) {
|
if (currentFragmentNeedsDifferentAnimation) {
|
||||||
setupCurrentFragmentCustomTransition({ duration: 0, curve: null }, currentEntry, noTransition);
|
setupCurrentFragmentCustomTransition({ duration: 0, curve: null }, currentEntry, noTransition);
|
||||||
}
|
}
|
||||||
} else if (name === 'custom') {
|
} else if (customTransition) {
|
||||||
setupNewFragmentCustomTransition(
|
setupNewFragmentCustomTransition(
|
||||||
{
|
{
|
||||||
duration: transition.getDuration(),
|
duration: customTransition.getDuration(),
|
||||||
curve: transition.getCurve(),
|
curve: customTransition.getCurve(),
|
||||||
},
|
},
|
||||||
newEntry,
|
newEntry,
|
||||||
transition
|
customTransition
|
||||||
);
|
);
|
||||||
if (currentFragmentNeedsDifferentAnimation) {
|
if (currentFragmentNeedsDifferentAnimation) {
|
||||||
setupCurrentFragmentCustomTransition(
|
setupCurrentFragmentCustomTransition(
|
||||||
{
|
{
|
||||||
duration: transition.getDuration(),
|
duration: customTransition.getDuration(),
|
||||||
curve: transition.getCurve(),
|
curve: customTransition.getCurve(),
|
||||||
},
|
},
|
||||||
currentEntry,
|
currentEntry,
|
||||||
transition
|
customTransition
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
} else if (name === 'default') {
|
} else if (name === 'default') {
|
||||||
setupNewFragmentFadeTransition({ duration: 150, curve: null }, newEntry);
|
transition = new FadeTransition(150, null);
|
||||||
if (currentFragmentNeedsDifferentAnimation) {
|
|
||||||
setupCurrentFragmentFadeTransition({ duration: 150, curve: null }, currentEntry);
|
|
||||||
}
|
|
||||||
} else if (name.indexOf('slide') === 0) {
|
} else if (name.indexOf('slide') === 0) {
|
||||||
setupNewFragmentSlideTransition(navigationTransition, newEntry, name);
|
const direction = name.substr('slide'.length) || 'left'; //Extract the direction from the string
|
||||||
if (currentFragmentNeedsDifferentAnimation) {
|
transition = new SlideTransition(direction, navigationTransition.duration, navigationTransition.curve);
|
||||||
setupCurrentFragmentSlideTransition(navigationTransition, currentEntry, name);
|
|
||||||
}
|
|
||||||
} else if (name === 'fade') {
|
} else if (name === 'fade') {
|
||||||
setupNewFragmentFadeTransition(navigationTransition, newEntry);
|
transition = new FadeTransition(navigationTransition.duration, navigationTransition.curve);
|
||||||
if (currentFragmentNeedsDifferentAnimation) {
|
|
||||||
setupCurrentFragmentFadeTransition(navigationTransition, currentEntry);
|
|
||||||
}
|
|
||||||
} else if (name === 'explode') {
|
|
||||||
setupNewFragmentExplodeTransition(navigationTransition, newEntry);
|
|
||||||
if (currentFragmentNeedsDifferentAnimation) {
|
|
||||||
setupCurrentFragmentExplodeTransition(navigationTransition, currentEntry);
|
|
||||||
}
|
|
||||||
} else if (name.indexOf('flip') === 0) {
|
} else if (name.indexOf('flip') === 0) {
|
||||||
const direction = name.substr('flip'.length) || 'right'; //Extract the direction from the string
|
const direction = name.substr('flip'.length) || 'right'; //Extract the direction from the string
|
||||||
const flipTransition = new FlipTransition(direction, navigationTransition.duration, navigationTransition.curve);
|
transition = new FlipTransition(direction, navigationTransition.duration, navigationTransition.curve);
|
||||||
|
}
|
||||||
setupNewFragmentCustomTransition(navigationTransition, newEntry, flipTransition);
|
if (transition) {
|
||||||
|
setupNewFragmentCustomTransition(navigationTransition, newEntry, transition);
|
||||||
if (currentFragmentNeedsDifferentAnimation) {
|
if (currentFragmentNeedsDifferentAnimation) {
|
||||||
setupCurrentFragmentCustomTransition(navigationTransition, currentEntry, flipTransition);
|
setupCurrentFragmentCustomTransition(navigationTransition, currentEntry, transition);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -88,6 +88,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;
|
||||||
onCreateAnimator(fragment: any, transit: number, enter: boolean, nextAnim: number, superFunc: Function): any;
|
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;
|
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;
|
||||||
|
@ -476,10 +476,10 @@ export class Frame extends FrameBase {
|
|||||||
|
|
||||||
_reverseTransitions(backstackEntry, this._currentEntry);
|
_reverseTransitions(backstackEntry, this._currentEntry);
|
||||||
|
|
||||||
const currentIndex =this.backStack.length;
|
const currentIndex = this.backStack.length;
|
||||||
const goBackToIndex = this.backStack.indexOf(backstackEntry);
|
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
|
// the one from the current entry we are going back from
|
||||||
if (this._currentEntry !== backstackEntry) {
|
if (this._currentEntry !== backstackEntry) {
|
||||||
const entry = this._currentEntry as ExpandedEntry;
|
const entry = this._currentEntry as ExpandedEntry;
|
||||||
@ -489,8 +489,9 @@ export class Frame extends FrameBase {
|
|||||||
if (entry.returnTransitionListener) {
|
if (entry.returnTransitionListener) {
|
||||||
entry.returnTransitionListener.backEntry = backstackEntry;
|
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++) {
|
for (let index = goBackToIndex + 1; index < currentIndex; index++) {
|
||||||
transaction.remove(this.backStack[index].fragment);
|
transaction.remove(this.backStack[index].fragment);
|
||||||
@ -501,8 +502,13 @@ export class Frame extends FrameBase {
|
|||||||
|
|
||||||
public _removeEntry(removed: BackstackEntry): void {
|
public _removeEntry(removed: BackstackEntry): void {
|
||||||
super._removeEntry(removed);
|
super._removeEntry(removed);
|
||||||
|
|
||||||
if (removed.fragment) {
|
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);
|
_clearEntry(removed);
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -788,11 +794,11 @@ function findPageForFragment(fragment: androidx.fragment.app.Fragment, frame: Fr
|
|||||||
} else if (executingContext && executingContext.entry && executingContext.entry.fragmentTag === fragmentTag) {
|
} else if (executingContext && executingContext.entry && executingContext.entry.fragmentTag === fragmentTag) {
|
||||||
entry = executingContext.entry;
|
entry = executingContext.entry;
|
||||||
} else {
|
} else {
|
||||||
frame.backStack.forEach(e=>{
|
frame.backStack.forEach((e) => {
|
||||||
if (e && e.fragmentTag === fragmentTag) {
|
if (e && e.fragmentTag === fragmentTag) {
|
||||||
entry = e;
|
entry = e;
|
||||||
}
|
}
|
||||||
})
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
let page: Page;
|
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 {
|
public onCreateAnimator(fragment: androidx.fragment.app.Fragment, transit: number, enter: boolean, nextAnim: number, superFunc: Function): android.animation.Animator {
|
||||||
let animator = null;
|
let animator = null;
|
||||||
const entry = <any>this.entry;
|
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.
|
// Return enterAnimator only when new (no current entry) nested transition.
|
||||||
if (enter && entry.isNestedDefaultTransition) {
|
if (enter && entry.isNestedDefaultTransition) {
|
||||||
animator = entry.enterAnimator;
|
animator = entry.enterAnimator;
|
||||||
entry.isNestedDefaultTransition = false;
|
entry.isNestedDefaultTransition = false;
|
||||||
}
|
}
|
||||||
|
|
||||||
return animator || superFunc.call(fragment, transit, enter, nextAnim);
|
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
|
@profile
|
||||||
public onCreate(fragment: androidx.fragment.app.Fragment, savedInstanceState: android.os.Bundle, superFunc: Function): void {
|
public onCreate(fragment: androidx.fragment.app.Fragment, savedInstanceState: android.os.Bundle, superFunc: Function): void {
|
||||||
@ -988,11 +1003,11 @@ class FragmentCallbacksImplementation implements AndroidFragmentCallbacks {
|
|||||||
|
|
||||||
@profile
|
@profile
|
||||||
public onDestroyView(fragment: org.nativescript.widgets.FragmentBase, superFunc: Function): void {
|
public onDestroyView(fragment: org.nativescript.widgets.FragmentBase, superFunc: Function): void {
|
||||||
if (Trace.isEnabled()) {
|
if (Trace.isEnabled()) {
|
||||||
Trace.write(`${fragment}.onDestroyView()`, Trace.categories.NativeLifecycle);
|
Trace.write(`${fragment}.onDestroyView()`, Trace.categories.NativeLifecycle);
|
||||||
}
|
|
||||||
superFunc.call(fragment);
|
|
||||||
}
|
}
|
||||||
|
superFunc.call(fragment);
|
||||||
|
}
|
||||||
|
|
||||||
@profile
|
@profile
|
||||||
public onDestroy(fragment: androidx.fragment.app.Fragment, superFunc: Function): void {
|
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"
|
// "IllegalStateException: Failure saving state: active fragment has cleared index: -1"
|
||||||
// in a specific mixed parent / nested frame navigation scenario
|
// in a specific mixed parent / nested frame navigation scenario
|
||||||
entry.fragment = null;
|
entry.fragment = null;
|
||||||
|
|
||||||
const page = entry.resolvedPage;
|
|
||||||
if (!page) {
|
|
||||||
Trace.error(`${fragment}.onDestroy: entry has no resolvedPage`);
|
|
||||||
|
|
||||||
return null;
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
@profile
|
@profile
|
||||||
public onPause(fragment: org.nativescript.widgets.FragmentBase, superFunc: Function): void {
|
public onPause(fragment: org.nativescript.widgets.FragmentBase, superFunc: Function): void {
|
||||||
superFunc.call(fragment);
|
superFunc.call(fragment);
|
||||||
}
|
}
|
||||||
|
|
||||||
@profile
|
@profile
|
||||||
|
Reference in New Issue
Block a user