| FazBrowse GitHub Viewer | Trending | | Home |
| Tools: [Download Repo ZIP] [Original HTTPS Page] |
1 parent 0002624 commit a353c1d
3 files changed
| Original file line number | Diff line number | Diff line change | |
|---|---|---|---|
@@ -19,17 +19,17 @@ interface TransitionListener { | |||
| 19 | 19 | new(entry: ExpandedEntry, transition: android.transition.Transition): ExpandedTransitionListener; | |
| 20 | 20 | } | |
| 21 | 21 | ||
| 22 | - interface ExpandedAnimator extends android.animation.Animator { | ||
| 22 | + export interface ExpandedAnimator extends android.animation.Animator { | ||
| 23 | 23 | entry: ExpandedEntry; | |
| 24 | 24 | transitionType?: string; | |
| 25 | 25 | } | |
| 26 | 26 | ||
| 27 | - interface ExpandedTransitionListener extends android.transition.Transition.TransitionListener { | ||
| 27 | + export interface ExpandedTransitionListener extends android.transition.Transition.TransitionListener { | ||
| 28 | 28 | entry: ExpandedEntry; | |
| 29 | 29 | transition: android.transition.Transition; | |
| 30 | 30 | } | |
| 31 | 31 | ||
| 32 | - interface ExpandedEntry extends BackstackEntry { | ||
| 32 | + export interface ExpandedEntry extends BackstackEntry { | ||
| 33 | 33 | enterTransitionListener: ExpandedTransitionListener; | |
| 34 | 34 | exitTransitionListener: ExpandedTransitionListener; | |
| 35 | 35 | reenterTransitionListener: ExpandedTransitionListener; | |
@@ -151,7 +151,8 @@ export function _setAndroidFragmentTransitions( | |||
| 151 | 151 | ||
| 152 | 152 | // Having transition means we have custom animation | |
| 153 | 153 | if (transition) { | |
| 154 | - fragmentTransaction.setCustomAnimations(AnimationType.enterFakeResourceId, AnimationType.exitFakeResourceId, AnimationType.popEnterFakeResourceId, AnimationType.popExitFakeResourceId); | ||
| 154 | + // we do not use Android backstack so setting popEnter / popExit is meaningless (3rd and 4th optional args) | ||
| 155 | + fragmentTransaction.setCustomAnimations(AnimationType.enterFakeResourceId, AnimationType.exitFakeResourceId); | ||
| 155 | 156 | setupAllAnimation(newEntry, transition); | |
| 156 | 157 | if (currentFragmentNeedsDifferentAnimation) { | |
| 157 | 158 | setupExitAndPopEnterAnimation(currentEntry, transition); | |
| Original file line number | Diff line number | Diff line change | |
|---|---|---|---|
@@ -24,6 +24,14 @@ import { createViewFromEntry } from "../builder"; | |||
| 24 | 24 | ||
| 25 | 25 | export * from "./frame-common"; | |
| 26 | 26 | ||
| 27 | + interface AnimatorState { | ||
| 28 | + enterAnimator: android.animation.Animator; | ||
| 29 | + exitAnimator: android.animation.Animator; | ||
| 30 | + popEnterAnimator: android.animation.Animator; | ||
| 31 | + popExitAnimator: android.animation.Animator; | ||
| 32 | + transitionName: string; | ||
| 33 | + } | ||
| 34 | + | ||
| 27 | 35 | const INTENT_EXTRA = "com.tns.activity"; | |
| 28 | 36 | const ROOT_VIEW_ID_EXTRA = "com.tns.activity.rootViewId"; | |
| 29 | 37 | const FRAMEID = "_frameId"; | |
@@ -93,6 +101,7 @@ export class Frame extends FrameBase { | |||
| 93 | 101 | private _tearDownPending = false; | |
| 94 | 102 | private _attachedToWindow = false; | |
| 95 | 103 | public _isBack: boolean = true; | |
| 104 | + private _cachedAnimatorState: AnimatorState; | ||
| 96 | 105 | ||
| 97 | 106 | constructor() { | |
| 98 | 107 | super(); | |
@@ -170,6 +179,17 @@ export class Frame extends FrameBase { | |||
| 170 | 179 | const entry = this._currentEntry; | |
| 171 | 180 | if (entry && manager && !manager.findFragmentByTag(entry.fragmentTag)) { | |
| 172 | 181 | // Simulate first navigation (e.g. no animations or transitions) | |
| 182 | + // we need to cache the original animation settings so we can restore them later; otherwise as the | ||
| 183 | + // simulated first navigation is not animated (it is actually a zero duration animator) the "popExit" animation | ||
| 184 | + // is broken when transaction.setCustomAnimations(...) is used in a scenario with: | ||
| 185 | + // 1) forward navigation | ||
| 186 | + // 2) suspend / resume app | ||
| 187 | + // 3) back navigation -- the exiting fragment is erroneously animated with the exit animator from the | ||
| 188 | + // simulated navigation (NoTransition, zero duration animator) and thus the fragment immediately disappears; | ||
| 189 | + // the user only sees the animation of the entering fragment as per its specific enter animation settings. | ||
| 190 | + // NOTE: we are restoring the animation settings in Frame.setCurrent(...) as navigation completes asynchronously | ||
| 191 | + this._cachedAnimatorState = getAnimatorState(this._currentEntry); | ||
| 192 | + | ||
| 173 | 193 | this._currentEntry = null; | |
| 174 | 194 | // NavigateCore will eventually call _processNextNavigationEntry again. | |
| 175 | 195 | this._navigateCore(entry); | |
@@ -194,8 +214,12 @@ export class Frame extends FrameBase { | |||
| 194 | 214 | } | |
| 195 | 215 | ||
| 196 | 216 | onUnloaded() { | |
| 197 | - this.disposeCurrentFragment(); | ||
| 198 | 217 | super.onUnloaded(); | |
| 218 | + | ||
| 219 | + // calling dispose fragment after super.onUnloaded() means we are not relying on the built-in Android logic | ||
| 220 | + // to automatically remove child fragments when parent fragment is removed; | ||
| 221 | + // this fixes issue with missing nested fragment on app suspend / resume; | ||
| 222 | + this.disposeCurrentFragment(); | ||
| 199 | 223 | } | |
| 200 | 224 | ||
| 201 | 225 | private disposeCurrentFragment(): void { | |
@@ -278,6 +302,14 @@ export class Frame extends FrameBase { | |||
| 278 | 302 | // Continue with next item in the queue. | |
| 279 | 303 | this._processNextNavigationEntry(); | |
| 280 | 304 | } | |
| 305 | + | ||
| 306 | + // restore cached animation settings if we just completed simulated first navigation (no animation) | ||
| 307 | + if (this._cachedAnimatorState) { | ||
| 308 | + restoreAnimatorState(this._currentEntry, this._cachedAnimatorState); | ||
| 309 | + | ||
| 310 | + this._cachedAnimatorState = null; | ||
| 311 | + } | ||
| 312 | + | ||
| 281 | 313 | } | |
| 282 | 314 | ||
| 283 | 315 | public onBackPressed(): boolean { | |
@@ -332,7 +364,7 @@ export class Frame extends FrameBase { | |||
| 332 | 364 | const newFragmentTag = `fragment${fragmentId}[${navDepth}]`; | |
| 333 | 365 | const newFragment = this.createFragment(newEntry, newFragmentTag); | |
| 334 | 366 | const transaction = manager.beginTransaction(); | |
| 335 | - const animated = this._getIsAnimatedNavigation(newEntry.entry); | ||
| 367 | + const animated = currentEntry ? this._getIsAnimatedNavigation(newEntry.entry) : false; | ||
| 336 | 368 | // NOTE: Don't use transition for the initial navigation (same as on iOS) | |
| 337 | 369 | // On API 21+ transition won't be triggered unless there was at least one | |
| 338 | 370 | // layout pass so we will wait forever for transitionCompleted handler... | |
@@ -346,7 +378,7 @@ export class Frame extends FrameBase { | |||
| 346 | 378 | } | |
| 347 | 379 | ||
| 348 | 380 | transaction.replace(this.containerViewId, newFragment, newFragmentTag); | |
| 349 | - transaction.commit(); | ||
| 381 | + transaction.commitAllowingStateLoss(); | ||
| 350 | 382 | } | |
| 351 | 383 | ||
| 352 | 384 | public _goBackCore(backstackEntry: BackstackEntry) { | |
@@ -369,11 +401,12 @@ export class Frame extends FrameBase { | |||
| 369 | 401 | const transitionReversed = _reverseTransitions(backstackEntry, this._currentEntry); | |
| 370 | 402 | if (!transitionReversed) { | |
| 371 | 403 | // If transition were not reversed then use animations. | |
| 372 | - transaction.setCustomAnimations(AnimationType.popEnterFakeResourceId, AnimationType.popExitFakeResourceId, AnimationType.enterFakeResourceId, AnimationType.exitFakeResourceId); | ||
| 404 | + // we do not use Android backstack so setting popEnter / popExit is meaningless (3rd and 4th optional args) | ||
| 405 | + transaction.setCustomAnimations(AnimationType.popEnterFakeResourceId, AnimationType.popExitFakeResourceId); | ||
| 373 | 406 | } | |
| 374 | 407 | ||
| 375 | 408 | transaction.replace(this.containerViewId, backstackEntry.fragment, backstackEntry.fragmentTag); | |
| 376 | - transaction.commit(); | ||
| 409 | + transaction.commitAllowingStateLoss(); | ||
| 377 | 410 | } | |
| 378 | 411 | ||
| 379 | 412 | public _removeEntry(removed: BackstackEntry): void { | |
@@ -470,6 +503,27 @@ export class Frame extends FrameBase { | |||
| 470 | 503 | } | |
| 471 | 504 | } | |
| 472 | 505 | ||
| 506 | + function getAnimatorState(entry: BackstackEntry): AnimatorState { | ||
| 507 | + const expandedEntry = <any>entry; | ||
| 508 | + const animatorState = <AnimatorState>{}; | ||
| 509 | + animatorState.enterAnimator = expandedEntry.enterAnimator; | ||
| 510 | + animatorState.exitAnimator = expandedEntry.exitAnimator; | ||
| 511 | + animatorState.popEnterAnimator = expandedEntry.popEnterAnimator; | ||
| 512 | + animatorState.popExitAnimator = expandedEntry.popExitAnimator; | ||
| 513 | + animatorState.transitionName = expandedEntry.transitionName; | ||
| 514 | + | ||
| 515 | + return animatorState; | ||
| 516 | + } | ||
| 517 | + | ||
| 518 | + function restoreAnimatorState(entry: BackstackEntry, snapshot: AnimatorState): void { | ||
| 519 | + const expandedEntry = <any>entry; | ||
| 520 | + expandedEntry.enterAnimator = snapshot.enterAnimator; | ||
| 521 | + expandedEntry.exitAnimator = snapshot.exitAnimator; | ||
| 522 | + expandedEntry.popEnterAnimator = snapshot.popEnterAnimator; | ||
| 523 | + expandedEntry.popExitAnimator = snapshot.popExitAnimator; | ||
| 524 | + expandedEntry.transitionName = snapshot.transitionName; | ||
| 525 | + } | ||
| 526 | + | ||
| 473 | 527 | function clearEntry(entry: BackstackEntry): void { | |
| 474 | 528 | if (entry.fragment) { | |
| 475 | 529 | _clearFragment(entry); | |
@@ -786,16 +840,6 @@ class FragmentCallbacksImplementation implements AndroidFragmentCallbacks { | |||
| 786 | 840 | traceWrite(`${fragment}.onDestroyView()`, traceCategories.NativeLifecycle); | |
| 787 | 841 | } | |
| 788 | 842 | ||
| 789 | - // fixes 'java.lang.IllegalStateException: The specified child already has a parent. You must call removeView() on the child's parent first'. | ||
| 790 | - // on app resume in nested frame scenarios with support library version greater than 26.0.0 | ||
| 791 | - const view = fragment.getView(); | ||
| 792 | - if (view != null) { | ||
| 793 | - const viewParent = view.getParent(); | ||
| 794 | - if (viewParent instanceof android.view.ViewGroup) { | ||
| 795 | - viewParent.removeView(view); | ||
| 796 | - } | ||
| 797 | - } | ||
| 798 | - | ||
| 799 | 843 | superFunc.call(fragment); | |
| 800 | 844 | } | |
| 801 | 845 | ||
| Original file line number | Diff line number | Diff line change | |
|---|---|---|---|
@@ -317,8 +317,13 @@ export class TabViewItem extends TabViewItemBase { | |||
| 317 | 317 | } | |
| 318 | 318 | } | |
| 319 | 319 | ||
| 320 | + // TODO: can happen in a modal tabview scenario when the modal dialog fragment is already removed | ||
| 320 | 321 | if (!tabFragment) { | |
| 321 | - throw new Error(`Could not get child fragment manager for tab item with index ${this.index}`); | ||
| 322 | + if (traceEnabled()) { | ||
| 323 | + traceWrite(`Could not get child fragment manager for tab item with index ${this.index}`, traceCategory); | ||
| 324 | + } | ||
| 325 | + | ||
| 326 | + return (<any>tabView)._getRootFragmentManager(); | ||
| 322 | 327 | } | |
| 323 | 328 | ||
| 324 | 329 | return tabFragment.getChildFragmentManager(); | |
| Back | FazBrowse Home | New Git URL |
0 commit comments