mirror of
https://github.com/NativeScript/NativeScript.git
synced 2025-08-15 19:26:42 +08:00
fix(android): nested fragment disappears on parent fragment removal (#6677)
This commit is contained in:
@ -1,7 +1,7 @@
|
|||||||
import { AndroidFragmentCallbacks, setFragmentCallbacks, setFragmentClass } from "./frame";
|
import { AndroidFragmentCallbacks, setFragmentCallbacks, setFragmentClass } from "./frame";
|
||||||
|
|
||||||
@JavaProxy("com.tns.FragmentClass")
|
@JavaProxy("com.tns.FragmentClass")
|
||||||
class FragmentClass extends android.support.v4.app.Fragment {
|
class FragmentClass extends org.nativescript.widgets.FragmentBase {
|
||||||
// This field is updated in the frame module upon `new` (although hacky this eases the Fragment->callbacks association a lot)
|
// This field is updated in the frame module upon `new` (although hacky this eases the Fragment->callbacks association a lot)
|
||||||
private _callbacks: AndroidFragmentCallbacks;
|
private _callbacks: AndroidFragmentCallbacks;
|
||||||
|
|
||||||
@ -15,8 +15,7 @@ class FragmentClass extends android.support.v4.app.Fragment {
|
|||||||
}
|
}
|
||||||
|
|
||||||
public onCreateAnimator(transit: number, enter: boolean, nextAnim: number): android.animation.Animator {
|
public onCreateAnimator(transit: number, enter: boolean, nextAnim: number): android.animation.Animator {
|
||||||
let result = this._callbacks.onCreateAnimator(this, transit, enter, nextAnim, super.onCreateAnimator);
|
return this._callbacks.onCreateAnimator(this, transit, enter, nextAnim, super.onCreateAnimator);
|
||||||
return result;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
public onStop(): void {
|
public onStop(): void {
|
||||||
|
@ -86,7 +86,20 @@ export function _setAndroidFragmentTransitions(
|
|||||||
name = navigationTransition.name ? navigationTransition.name.toLowerCase() : "";
|
name = navigationTransition.name ? navigationTransition.name.toLowerCase() : "";
|
||||||
}
|
}
|
||||||
|
|
||||||
let useLollipopTransition = name && (name.indexOf("slide") === 0 || name === "fade" || name === "explode") && sdkVersion() >= 21;
|
let useLollipopTransition = !!(name && (name.indexOf("slide") === 0 || name === "fade" || name === "explode") && sdkVersion() >= 21);
|
||||||
|
// [nested frames / fragments] force disable lollipop transitions in case nested fragments
|
||||||
|
// are detected as applying dummy animator to the nested fragment with the same duration as
|
||||||
|
// the exit animator of the removing parent fragment as a workaround for
|
||||||
|
// https://code.google.com/p/android/issues/detail?id=55228 works only if custom animations are
|
||||||
|
// used
|
||||||
|
// NOTE: this effectively means you cannot use Explode transition in nested frames scenarios as
|
||||||
|
// we have implementations only for slide, fade, and flip
|
||||||
|
if (currentFragment &&
|
||||||
|
currentFragment.getChildFragmentManager() &&
|
||||||
|
currentFragment.getChildFragmentManager().getFragments().toArray().length > 0) {
|
||||||
|
useLollipopTransition = false;
|
||||||
|
}
|
||||||
|
|
||||||
if (!animated) {
|
if (!animated) {
|
||||||
name = "none";
|
name = "none";
|
||||||
} else if (transition) {
|
} else if (transition) {
|
||||||
|
@ -457,8 +457,8 @@ export class FrameBase extends CustomLayoutView implements FrameDefinition {
|
|||||||
}
|
}
|
||||||
|
|
||||||
public _onRootViewReset(): void {
|
public _onRootViewReset(): void {
|
||||||
this._removeFromFrameStack();
|
|
||||||
super._onRootViewReset();
|
super._onRootViewReset();
|
||||||
|
this._removeFromFrameStack();
|
||||||
}
|
}
|
||||||
|
|
||||||
get _childrenCount(): number {
|
get _childrenCount(): number {
|
||||||
|
@ -209,8 +209,12 @@ export class Frame extends FrameBase {
|
|||||||
}
|
}
|
||||||
|
|
||||||
public _onRootViewReset(): void {
|
public _onRootViewReset(): void {
|
||||||
this.disposeCurrentFragment();
|
|
||||||
super._onRootViewReset();
|
super._onRootViewReset();
|
||||||
|
|
||||||
|
// call this AFTER the super call to ensure descendants apply their rootview-reset logic first
|
||||||
|
// i.e. in a scenario with nested frames / frame with tabview let the descendandt cleanup the inner
|
||||||
|
// fragments first, and then cleanup the parent fragments
|
||||||
|
this.disposeCurrentFragment();
|
||||||
}
|
}
|
||||||
|
|
||||||
onUnloaded() {
|
onUnloaded() {
|
||||||
@ -223,11 +227,6 @@ export class Frame extends FrameBase {
|
|||||||
}
|
}
|
||||||
|
|
||||||
private disposeCurrentFragment(): void {
|
private disposeCurrentFragment(): void {
|
||||||
// when interacting with nested fragments it seems Android is smart enough
|
|
||||||
// to automatically remove child fragments when parent fragment is removed;
|
|
||||||
// however, we must add a fragment.isAdded() guard as our logic will try to
|
|
||||||
// explicitly remove the already removed child fragment causing an
|
|
||||||
// IllegalStateException: Fragment has not been attached yet.
|
|
||||||
if (!this._currentEntry ||
|
if (!this._currentEntry ||
|
||||||
!this._currentEntry.fragment ||
|
!this._currentEntry.fragment ||
|
||||||
!this._currentEntry.fragment.isAdded()) {
|
!this._currentEntry.fragment.isAdded()) {
|
||||||
@ -742,7 +741,13 @@ class FragmentCallbacksImplementation implements AndroidFragmentCallbacks {
|
|||||||
}
|
}
|
||||||
|
|
||||||
@profile
|
@profile
|
||||||
public onCreateAnimator(fragment: android.support.v4.app.Fragment, transit: number, enter: boolean, nextAnim: number, superFunc: Function): android.animation.Animator {
|
public onCreateAnimator(fragment: org.nativescript.widgets.FragmentBase, transit: number, enter: boolean, nextAnim: number, superFunc: Function): android.animation.Animator {
|
||||||
|
// HACK: FragmentBase class MUST handle removing nested fragment scenario to workaround
|
||||||
|
// https://code.google.com/p/android/issues/detail?id=55228
|
||||||
|
if (!enter && fragment.getRemovingParentFragment()) {
|
||||||
|
return superFunc.call(fragment, transit, enter, nextAnim);
|
||||||
|
}
|
||||||
|
|
||||||
let nextAnimString: string;
|
let nextAnimString: string;
|
||||||
switch (nextAnim) {
|
switch (nextAnim) {
|
||||||
case AnimationType.enterFakeResourceId: nextAnimString = "enter"; break;
|
case AnimationType.enterFakeResourceId: nextAnimString = "enter"; break;
|
||||||
|
@ -45,7 +45,7 @@ function initializeNativeClasses() {
|
|||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
class TabFragmentImplementation extends android.support.v4.app.Fragment {
|
class TabFragmentImplementation extends org.nativescript.widgets.FragmentBase {
|
||||||
private tab: TabView;
|
private tab: TabView;
|
||||||
private index: number;
|
private index: number;
|
||||||
|
|
||||||
@ -55,7 +55,6 @@ function initializeNativeClasses() {
|
|||||||
}
|
}
|
||||||
|
|
||||||
static newInstance(tabId: number, index: number): TabFragmentImplementation {
|
static newInstance(tabId: number, index: number): TabFragmentImplementation {
|
||||||
|
|
||||||
const args = new android.os.Bundle();
|
const args = new android.os.Bundle();
|
||||||
args.putInt(TABID, tabId);
|
args.putInt(TABID, tabId);
|
||||||
args.putInt(INDEX, index);
|
args.putInt(INDEX, index);
|
||||||
@ -79,10 +78,6 @@ function initializeNativeClasses() {
|
|||||||
|
|
||||||
return tabItem.view.nativeViewProtected;
|
return tabItem.view.nativeViewProtected;
|
||||||
}
|
}
|
||||||
|
|
||||||
public onDestroyView() {
|
|
||||||
super.onDestroyView();
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
const POSITION_UNCHANGED = -1;
|
const POSITION_UNCHANGED = -1;
|
||||||
@ -560,8 +555,13 @@ export class TabView extends TabViewBase {
|
|||||||
}
|
}
|
||||||
|
|
||||||
public _onRootViewReset(): void {
|
public _onRootViewReset(): void {
|
||||||
this.disposeCurrentFragments();
|
|
||||||
super._onRootViewReset();
|
super._onRootViewReset();
|
||||||
|
|
||||||
|
// call this AFTER the super call to ensure descendants apply their rootview-reset logic first
|
||||||
|
// i.e. in a scenario with tab frames let the frames cleanup their fragments first, and then
|
||||||
|
// cleanup the tab fragments to avoid
|
||||||
|
// android.content.res.Resources$NotFoundException: Unable to find resource ID #0xfffffff6
|
||||||
|
this.disposeCurrentFragments();
|
||||||
}
|
}
|
||||||
|
|
||||||
private disposeCurrentFragments(): void {
|
private disposeCurrentFragments(): void {
|
||||||
|
@ -164,6 +164,12 @@
|
|||||||
public verticalAlignment: VerticalAlignment;
|
public verticalAlignment: VerticalAlignment;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
export class FragmentBase extends android.support.v4.app.Fragment {
|
||||||
|
constructor();
|
||||||
|
|
||||||
|
public getRemovingParentFragment(): android.support.v4.app.Fragment;
|
||||||
|
}
|
||||||
|
|
||||||
export enum Stretch {
|
export enum Stretch {
|
||||||
none,
|
none,
|
||||||
aspectFill,
|
aspectFill,
|
||||||
|
Reference in New Issue
Block a user