| FazBrowse GitHub Viewer | Trending | | Home |
| Tools: [Download Repo ZIP] [Original HTTPS Page] |
1 parent 0002624 commit effa137
4 files changed
| Original file line number | Diff line number | Diff line change | |
|---|---|---|---|
@@ -6,6 +6,7 @@ import { AnimationType } from "./fragment.transitions"; | |||
| 6 | 6 | ||
| 7 | 7 | // Types. | |
| 8 | 8 | import { Transition, AndroidTransitionType } from "../transition/transition"; | |
| 9 | + import { ExpandedEntry, ExpandedAnimator, ExpandedTransitionListener } from "./fragment.transitions"; | ||
| 9 | 10 | import { SlideTransition } from "../transition/slide-transition"; | |
| 10 | 11 | import { FadeTransition } from "../transition/fade-transition"; | |
| 11 | 12 | import { FlipTransition } from "../transition/flip-transition"; | |
@@ -19,35 +20,6 @@ interface TransitionListener { | |||
| 19 | 20 | new(entry: ExpandedEntry, transition: android.transition.Transition): ExpandedTransitionListener; | |
| 20 | 21 | } | |
| 21 | 22 | ||
| 22 | - interface ExpandedAnimator extends android.animation.Animator { | ||
| 23 | - entry: ExpandedEntry; | ||
| 24 | - transitionType?: string; | ||
| 25 | - } | ||
| 26 | - | ||
| 27 | - interface ExpandedTransitionListener extends android.transition.Transition.TransitionListener { | ||
| 28 | - entry: ExpandedEntry; | ||
| 29 | - transition: android.transition.Transition; | ||
| 30 | - } | ||
| 31 | - | ||
| 32 | - interface ExpandedEntry extends BackstackEntry { | ||
| 33 | - enterTransitionListener: ExpandedTransitionListener; | ||
| 34 | - exitTransitionListener: ExpandedTransitionListener; | ||
| 35 | - reenterTransitionListener: ExpandedTransitionListener; | ||
| 36 | - returnTransitionListener: ExpandedTransitionListener; | ||
| 37 | - | ||
| 38 | - enterAnimator: ExpandedAnimator; | ||
| 39 | - exitAnimator: ExpandedAnimator; | ||
| 40 | - popEnterAnimator: ExpandedAnimator; | ||
| 41 | - popExitAnimator: ExpandedAnimator; | ||
| 42 | - | ||
| 43 | - defaultEnterAnimator: ExpandedAnimator; | ||
| 44 | - defaultExitAnimator: ExpandedAnimator; | ||
| 45 | - | ||
| 46 | - transition: Transition; | ||
| 47 | - transitionName: string; | ||
| 48 | - frameId: number | ||
| 49 | - } | ||
| 50 | - | ||
| 51 | 23 | const sdkVersion = lazy(() => parseInt(device.sdkVersion)); | |
| 52 | 24 | const intEvaluator = lazy(() => new android.animation.IntEvaluator()); | |
| 53 | 25 | const defaultInterpolator = lazy(() => new android.view.animation.AccelerateDecelerateInterpolator()); | |
@@ -151,7 +123,8 @@ export function _setAndroidFragmentTransitions( | |||
| 151 | 123 | ||
| 152 | 124 | // Having transition means we have custom animation | |
| 153 | 125 | if (transition) { | |
| 154 | - fragmentTransaction.setCustomAnimations(AnimationType.enterFakeResourceId, AnimationType.exitFakeResourceId, AnimationType.popEnterFakeResourceId, AnimationType.popExitFakeResourceId); | ||
| 126 | + // we do not use Android backstack so setting popEnter / popExit is meaningless (3rd and 4th optional args) | ||
| 127 | + fragmentTransaction.setCustomAnimations(AnimationType.enterFakeResourceId, AnimationType.exitFakeResourceId); | ||
| 155 | 128 | setupAllAnimation(newEntry, transition); | |
| 156 | 129 | if (currentFragmentNeedsDifferentAnimation) { | |
| 157 | 130 | setupExitAndPopEnterAnimation(currentEntry, transition); | |
| Original file line number | Diff line number | Diff line change | |
|---|---|---|---|
@@ -3,6 +3,7 @@ | |||
| 3 | 3 | */ /** */ | |
| 4 | 4 | ||
| 5 | 5 | import { NavigationTransition, BackstackEntry } from "../frame"; | |
| 6 | + import { Transition } from "../transition/transition"; | ||
| 6 | 7 | ||
| 7 | 8 | //@private | |
| 8 | 9 | /** | |
@@ -15,6 +16,47 @@ export const enum AnimationType { | |||
| 15 | 16 | popExitFakeResourceId = -40 | |
| 16 | 17 | } | |
| 17 | 18 | ||
| 19 | + //@private | ||
| 20 | + /** | ||
| 21 | + * @private | ||
| 22 | + */ | ||
| 23 | + export interface ExpandedAnimator extends android.animation.Animator { | ||
| 24 | + entry: ExpandedEntry; | ||
| 25 | + transitionType?: string; | ||
| 26 | + } | ||
| 27 | + | ||
| 28 | + //@private | ||
| 29 | + /** | ||
| 30 | + * @private | ||
| 31 | + */ | ||
| 32 | + export interface ExpandedTransitionListener extends android.transition.Transition.TransitionListener { | ||
| 33 | + entry: ExpandedEntry; | ||
| 34 | + transition: android.transition.Transition; | ||
| 35 | + } | ||
| 36 | + | ||
| 37 | + //@private | ||
| 38 | + /** | ||
| 39 | + * @private | ||
| 40 | + */ | ||
| 41 | + export interface ExpandedEntry extends BackstackEntry { | ||
| 42 | + enterTransitionListener: ExpandedTransitionListener; | ||
| 43 | + exitTransitionListener: ExpandedTransitionListener; | ||
| 44 | + reenterTransitionListener: ExpandedTransitionListener; | ||
| 45 | + returnTransitionListener: ExpandedTransitionListener; | ||
| 46 | + | ||
| 47 | + enterAnimator: ExpandedAnimator; | ||
| 48 | + exitAnimator: ExpandedAnimator; | ||
| 49 | + popEnterAnimator: ExpandedAnimator; | ||
| 50 | + popExitAnimator: ExpandedAnimator; | ||
| 51 | + | ||
| 52 | + defaultEnterAnimator: ExpandedAnimator; | ||
| 53 | + defaultExitAnimator: ExpandedAnimator; | ||
| 54 | + | ||
| 55 | + transition: Transition; | ||
| 56 | + transitionName: string; | ||
| 57 | + frameId: number | ||
| 58 | + } | ||
| 59 | + | ||
| 18 | 60 | /** | |
| 19 | 61 | * @private | |
| 20 | 62 | */ | |
| Original file line number | Diff line number | Diff line change | |
|---|---|---|---|
@@ -14,7 +14,7 @@ import { | |||
| 14 | 14 | ||
| 15 | 15 | import { | |
| 16 | 16 | _setAndroidFragmentTransitions, _onFragmentCreateAnimator, _getAnimatedEntries, | |
| 17 | - _updateTransitions, _reverseTransitions, _clearEntry, _clearFragment, AnimationType | ||
| 17 | + _updateTransitions, _reverseTransitions, _clearEntry, _clearFragment, AnimationType, ExpandedEntry | ||
| 18 | 18 | } from "./fragment.transitions"; | |
| 19 | 19 | ||
| 20 | 20 | import { profile } from "../../profiling"; | |
@@ -93,6 +93,7 @@ export class Frame extends FrameBase { | |||
| 93 | 93 | private _tearDownPending = false; | |
| 94 | 94 | private _attachedToWindow = false; | |
| 95 | 95 | public _isBack: boolean = true; | |
| 96 | + private _cachedAnimationEntry: ExpandedEntry; | ||
| 96 | 97 | ||
| 97 | 98 | constructor() { | |
| 98 | 99 | super(); | |
@@ -170,6 +171,17 @@ export class Frame extends FrameBase { | |||
| 170 | 171 | const entry = this._currentEntry; | |
| 171 | 172 | if (entry && manager && !manager.findFragmentByTag(entry.fragmentTag)) { | |
| 172 | 173 | // Simulate first navigation (e.g. no animations or transitions) | |
| 174 | + // we need to cache the original animation settings so we can restore them later; otherwise as the | ||
| 175 | + // simulated first navigation is not animated (it is actually a zero duration animator) the "popExit" animation | ||
| 176 | + // is broken when transaction.setCustomAnimations(...) is used in a scenario with: | ||
| 177 | + // 1) forward navigation | ||
| 178 | + // 2) suspend / resume app | ||
| 179 | + // 3) back navigation -- the exiting fragment is erroneously animated with the exit animator from the | ||
| 180 | + // simulated navigation (NoTransition, zero duration animator) and thus the fragment immediately disappears; | ||
| 181 | + // the user only sees the animation of the entering fragment as per its specific enter animation settings. | ||
| 182 | + // NOTE: we are restoring the animation settings in Frame.setCurrent(...) as navigation completes asynchronously | ||
| 183 | + this._cachedAnimationEntry = getAnimatorState(this._currentEntry); | ||
| 184 | + | ||
| 173 | 185 | this._currentEntry = null; | |
| 174 | 186 | // NavigateCore will eventually call _processNextNavigationEntry again. | |
| 175 | 187 | this._navigateCore(entry); | |
@@ -194,8 +206,12 @@ export class Frame extends FrameBase { | |||
| 194 | 206 | } | |
| 195 | 207 | ||
| 196 | 208 | onUnloaded() { | |
| 197 | - this.disposeCurrentFragment(); | ||
| 198 | 209 | super.onUnloaded(); | |
| 210 | + | ||
| 211 | + // calling dispose fragment after super.onUnloaded() means we are not relying on the built-in Android logic | ||
| 212 | + // to automatically remove child fragments when parent fragment is removed; | ||
| 213 | + // this fixes issue with missing nested fragment on app suspend / resume; | ||
| 214 | + this.disposeCurrentFragment(); | ||
| 199 | 215 | } | |
| 200 | 216 | ||
| 201 | 217 | private disposeCurrentFragment(): void { | |
@@ -278,6 +294,14 @@ export class Frame extends FrameBase { | |||
| 278 | 294 | // Continue with next item in the queue. | |
| 279 | 295 | this._processNextNavigationEntry(); | |
| 280 | 296 | } | |
| 297 | + | ||
| 298 | + // restore cached animation settings if we just completed simulated first navigation (no animation) | ||
| 299 | + if (this._cachedAnimationEntry) { | ||
| 300 | + setAnimatorState(this._currentEntry, this._cachedAnimationEntry); | ||
| 301 | + | ||
| 302 | + this._cachedAnimationEntry = null; | ||
| 303 | + } | ||
| 304 | + | ||
| 281 | 305 | } | |
| 282 | 306 | ||
| 283 | 307 | public onBackPressed(): boolean { | |
@@ -332,7 +356,7 @@ export class Frame extends FrameBase { | |||
| 332 | 356 | const newFragmentTag = `fragment${fragmentId}[${navDepth}]`; | |
| 333 | 357 | const newFragment = this.createFragment(newEntry, newFragmentTag); | |
| 334 | 358 | const transaction = manager.beginTransaction(); | |
| 335 | - const animated = this._getIsAnimatedNavigation(newEntry.entry); | ||
| 359 | + const animated = currentEntry ? this._getIsAnimatedNavigation(newEntry.entry) : false; | ||
| 336 | 360 | // NOTE: Don't use transition for the initial navigation (same as on iOS) | |
| 337 | 361 | // On API 21+ transition won't be triggered unless there was at least one | |
| 338 | 362 | // layout pass so we will wait forever for transitionCompleted handler... | |
@@ -346,7 +370,7 @@ export class Frame extends FrameBase { | |||
| 346 | 370 | } | |
| 347 | 371 | ||
| 348 | 372 | transaction.replace(this.containerViewId, newFragment, newFragmentTag); | |
| 349 | - transaction.commit(); | ||
| 373 | + transaction.commitAllowingStateLoss(); | ||
| 350 | 374 | } | |
| 351 | 375 | ||
| 352 | 376 | public _goBackCore(backstackEntry: BackstackEntry) { | |
@@ -369,11 +393,12 @@ export class Frame extends FrameBase { | |||
| 369 | 393 | const transitionReversed = _reverseTransitions(backstackEntry, this._currentEntry); | |
| 370 | 394 | if (!transitionReversed) { | |
| 371 | 395 | // If transition were not reversed then use animations. | |
| 372 | - transaction.setCustomAnimations(AnimationType.popEnterFakeResourceId, AnimationType.popExitFakeResourceId, AnimationType.enterFakeResourceId, AnimationType.exitFakeResourceId); | ||
| 396 | + // we do not use Android backstack so setting popEnter / popExit is meaningless (3rd and 4th optional args) | ||
| 397 | + transaction.setCustomAnimations(AnimationType.popEnterFakeResourceId, AnimationType.popExitFakeResourceId); | ||
| 373 | 398 | } | |
| 374 | 399 | ||
| 375 | 400 | transaction.replace(this.containerViewId, backstackEntry.fragment, backstackEntry.fragmentTag); | |
| 376 | - transaction.commit(); | ||
| 401 | + transaction.commitAllowingStateLoss(); | ||
| 377 | 402 | } | |
| 378 | 403 | ||
| 379 | 404 | public _removeEntry(removed: BackstackEntry): void { | |
@@ -470,6 +495,27 @@ export class Frame extends FrameBase { | |||
| 470 | 495 | } | |
| 471 | 496 | } | |
| 472 | 497 | ||
| 498 | + function getAnimatorState(entry: BackstackEntry): ExpandedEntry { | ||
| 499 | + const expandedEntry = <ExpandedEntry>entry; | ||
| 500 | + const snapshot = <ExpandedEntry>{}; | ||
| 501 | + snapshot.enterAnimator = expandedEntry.enterAnimator; | ||
| 502 | + snapshot.exitAnimator = expandedEntry.exitAnimator; | ||
| 503 | + snapshot.popEnterAnimator = expandedEntry.popEnterAnimator; | ||
| 504 | + snapshot.popExitAnimator = expandedEntry.popExitAnimator; | ||
| 505 | + snapshot.transitionName = expandedEntry.transitionName; | ||
| 506 | + | ||
| 507 | + return snapshot; | ||
| 508 | + } | ||
| 509 | + | ||
| 510 | + function setAnimatorState(entry: BackstackEntry, snapshot: ExpandedEntry): void { | ||
| 511 | + const expandedEntry = <ExpandedEntry>entry; | ||
| 512 | + expandedEntry.enterAnimator = snapshot.enterAnimator; | ||
| 513 | + expandedEntry.exitAnimator = snapshot.exitAnimator; | ||
| 514 | + expandedEntry.popEnterAnimator = snapshot.popEnterAnimator; | ||
| 515 | + expandedEntry.popExitAnimator = snapshot.popExitAnimator; | ||
| 516 | + expandedEntry.transitionName = snapshot.transitionName; | ||
| 517 | + } | ||
| 518 | + | ||
| 473 | 519 | function clearEntry(entry: BackstackEntry): void { | |
| 474 | 520 | if (entry.fragment) { | |
| 475 | 521 | _clearFragment(entry); | |
@@ -786,16 +832,6 @@ class FragmentCallbacksImplementation implements AndroidFragmentCallbacks { | |||
| 786 | 832 | traceWrite(`${fragment}.onDestroyView()`, traceCategories.NativeLifecycle); | |
| 787 | 833 | } | |
| 788 | 834 | ||
| 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 | 835 | superFunc.call(fragment); | |
| 800 | 836 | } | |
| 801 | 837 | ||
| 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