Skip to content

Commit e17e469

Browse files
farfromrefugNathanWalker
authored andcommitted
feat(android): fragment transactions to use 'add' instead of 'replace' on fwd navigation (NativeScript#8791)
Changes the behavior of android fragment transactions to use `add` instead of `replace` on forward navigation. BREAKING CHANGE: Changes the internal behavior of Android navigation: * while navigating forward, the page navigated from is not unloaded anymore * events order is changed in the sense that now `unloaded` happens after `navigatedFrom` instead of before There are multiple plus sides to this: * no more black views on navigation when using opengl (maps, ...) * navigation is faster, especially the navigation back! No longer need to recreate the page anymore. Navigation forward also gets faster as we no longer unload the previous page * navigatedFrom event happens faster * this the default behavior used by most of the android native apps
1 parent 1e3bc48 commit e17e469

4 files changed

Lines changed: 52 additions & 14 deletions

File tree

apps/automated/src/ui/page/page-tests-common.ts

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -181,7 +181,7 @@ function _test_PageNavigation_EventSequence(withTransition: boolean) {
181181
helper.navigateWithEntry(navigationEntry);
182182
helper.goBack();
183183

184-
const expectedEventSequence = ['navigatingTo', 'loaded', 'navigatedTo', 'navigatingFrom', 'unloaded', 'navigatedFrom'];
184+
const expectedEventSequence = ['navigatingTo', 'loaded', 'navigatedTo', 'navigatingFrom', 'navigatedFrom', 'unloaded'];
185185
TKUnit.arrayAssert(eventSequence, expectedEventSequence, 'Actual event sequence is not equal to expected. Actual: ' + eventSequence + '; Expected: ' + expectedEventSequence);
186186
}
187187

packages/core/ui/frame/fragment.transitions.android.ts

Lines changed: 13 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -26,6 +26,7 @@ let AnimationListener: android.animation.Animator.AnimatorListener;
2626

2727
interface ExpandedTransitionListener extends androidx.transition.Transition.TransitionListener {
2828
entry: ExpandedEntry;
29+
backEntry?: BackstackEntry;
2930
transition: androidx.transition.Transition;
3031
}
3132

@@ -143,7 +144,7 @@ export function _setAndroidFragmentTransitions(animated: boolean, navigationTran
143144
if (currentFragmentNeedsDifferentAnimation) {
144145
setupCurrentFragmentFadeTransition(navigationTransition, currentEntry);
145146
}
146-
} else if (name === 'explode') {
147+
} else if (name === 'explode') {
147148
setupNewFragmentExplodeTransition(navigationTransition, newEntry);
148149
if (currentFragmentNeedsDifferentAnimation) {
149150
setupCurrentFragmentExplodeTransition(navigationTransition, currentEntry);
@@ -223,6 +224,7 @@ function getAnimationListener(): android.animation.Animator.AnimatorListener {
223224

224225
onAnimationStart(animator: ExpandedAnimator): void {
225226
const entry = animator.entry;
227+
const backEntry = animator.backEntry;
226228
addToWaitingQueue(entry);
227229
if (Trace.isEnabled()) {
228230
Trace.write(`START ${animator.transitionType} for ${entry.fragmentTag}`, Trace.categories.Transition);
@@ -236,10 +238,13 @@ function getAnimationListener(): android.animation.Animator.AnimatorListener {
236238
}
237239

238240
onAnimationEnd(animator: ExpandedAnimator): void {
241+
const entry = animator.entry;
242+
const backEntry = animator.backEntry;
239243
if (Trace.isEnabled()) {
240-
Trace.write(`END ${animator.transitionType} for ${animator.entry.fragmentTag}`, Trace.categories.Transition);
244+
Trace.write(`END ${animator.transitionType} for ${entry.fragmentTag} backEntry:${backEntry ? backEntry.fragmentTag : 'none'}`, Trace.categories.Transition);
241245
}
242-
transitionOrAnimationCompleted(animator.entry, animator.backEntry);
246+
transitionOrAnimationCompleted(entry, backEntry);
247+
animator.backEntry = null;
243248
}
244249

245250
onAnimationCancel(animator: ExpandedAnimator): void {
@@ -345,10 +350,12 @@ function getTransitionListener(entry: ExpandedEntry, transition: androidx.transi
345350

346351
onTransitionEnd(transition: androidx.transition.Transition): void {
347352
const entry = this.entry;
353+
const backEntry = this.backEntry;
348354
if (Trace.isEnabled()) {
349-
Trace.write(`END ${toShortString(transition)} transition for ${entry.fragmentTag}`, Trace.categories.Transition);
355+
Trace.write(`END ${toShortString(transition)} transition for ${entry.fragmentTag} backEntry:${backEntry ? backEntry.fragmentTag : 'none'}`, Trace.categories.Transition);
350356
}
351-
transitionOrAnimationCompleted(entry, this.backEntry);
357+
transitionOrAnimationCompleted(entry, backEntry);
358+
this.backEntry = null;
352359
}
353360

354361
onTransitionResume(transition: androidx.transition.Transition): void {
@@ -660,6 +667,7 @@ function transitionOrAnimationCompleted(entry: ExpandedEntry, backEntry: Backsta
660667
if (!entries) {
661668
return;
662669
}
670+
console.log('transitionOrAnimationCompleted', frameId, backEntry && backEntry.fragmentTag, waitingQueue.size, entries.size, completedEntries.size );
663671

664672
entries.delete(entry);
665673
if (entries.size === 0) {

packages/core/ui/frame/frame-common.ts

Lines changed: 1 addition & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -266,8 +266,8 @@ export class FrameBase extends CustomLayoutView {
266266
public _updateBackstack(entry: BackstackEntry, navigationType: NavigationType): void {
267267
const isBack = navigationType === NavigationType.back;
268268
const isReplace = navigationType === NavigationType.replace;
269-
this.raiseCurrentPageNavigatedEvents(isBack);
270269
const current = this._currentEntry;
270+
this.raiseCurrentPageNavigatedEvents(isBack);
271271

272272
// Do nothing for Hot Module Replacement
273273
if (isBack) {
@@ -303,10 +303,6 @@ export class FrameBase extends CustomLayoutView {
303303
private raiseCurrentPageNavigatedEvents(isBack: boolean) {
304304
const page = this.currentPage;
305305
if (page) {
306-
if (page.isLoaded) {
307-
// Forward navigation does not remove page from frame so we raise unloaded manually.
308-
page.callUnloaded();
309-
}
310306
page.onNavigatedFrom(isBack);
311307
}
312308
}

packages/core/ui/frame/index.android.ts

Lines changed: 37 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -19,6 +19,7 @@ import { Builder } from '../builder';
1919
import { CSSUtils } from '../../css/system-classes';
2020
import { Device } from '../../platform';
2121
import { profile } from '../../profiling';
22+
import { ExpandedEntry } from './fragment.transitions.android';
2223

2324
export * from './frame-common';
2425

@@ -325,6 +326,8 @@ export class Frame extends FrameBase {
325326

326327
// If we had real navigation process queue.
327328
this._processNavigationQueue(entry.resolvedPage);
329+
330+
328331
} else {
329332
// Otherwise currentPage was recreated so this wasn't real navigation.
330333
// Continue with next item in the queue.
@@ -437,7 +440,14 @@ export class Frame extends FrameBase {
437440
//transaction.setTransition(androidx.fragment.app.FragmentTransaction.TRANSIT_FRAGMENT_OPEN);
438441
}
439442

440-
transaction.replace(this.containerViewId, newFragment, newFragmentTag);
443+
if (clearHistory || isReplace) {
444+
transaction.replace(this.containerViewId, newFragment, newFragmentTag);
445+
} else {
446+
transaction.add(this.containerViewId, newFragment, newFragmentTag);
447+
}
448+
if (this._currentEntry && this._currentEntry.entry.backstackVisible === false) {
449+
transaction.remove(this._currentEntry.fragment);
450+
}
441451
transaction.commitAllowingStateLoss();
442452
}
443453

@@ -459,8 +469,26 @@ export class Frame extends FrameBase {
459469

460470
_reverseTransitions(backstackEntry, this._currentEntry);
461471

462-
transaction.replace(this.containerViewId, backstackEntry.fragment, backstackEntry.fragmentTag);
463-
472+
const currentIndex =this.backStack.length;
473+
const goBackToIndex = this.backStack.indexOf(backstackEntry);
474+
475+
// the order is important so that the transition listener called be
476+
// the one from the current entry we are going back from
477+
if (this._currentEntry !== backstackEntry) {
478+
const entry = this._currentEntry as ExpandedEntry;
479+
// if we are going back we need to store where we are backing to
480+
// so that we can set the current entry
481+
// it only needs to be done on the return transition
482+
if (entry.returnTransitionListener) {
483+
entry.returnTransitionListener.backEntry = backstackEntry;
484+
}
485+
486+
transaction.remove((this._currentEntry).fragment);
487+
}
488+
for (let index = goBackToIndex + 1; index < currentIndex; index++) {
489+
transaction.remove(this.backStack[index].fragment);
490+
}
491+
464492
transaction.commitAllowingStateLoss();
465493
}
466494

@@ -752,6 +780,12 @@ function findPageForFragment(fragment: androidx.fragment.app.Fragment, frame: Fr
752780
entry = current;
753781
} else if (executingContext && executingContext.entry && executingContext.entry.fragmentTag === fragmentTag) {
754782
entry = executingContext.entry;
783+
} else {
784+
frame.backStack.forEach(e=>{
785+
if (e && e.fragmentTag === fragmentTag) {
786+
entry = e;
787+
}
788+
})
755789
}
756790

757791
let page: Page;

0 commit comments

Comments
 (0)