fix: android cleanup after new fragment handling

This commit is contained in:
Martin Guillon
2020-10-27 21:00:10 +01:00
parent 893431a66d
commit e6fc8784e1
3 changed files with 27 additions and 143 deletions

View File

@ -91,57 +91,6 @@ function initializeNativeClasses() {
return tabItem.nativeViewProtected; return tabItem.nativeViewProtected;
} }
public onDestroyView() {
const hasRemovingParent = this.getRemovingParentFragment();
// Get view as bitmap and set it as background. This is workaround for the disapearing nested fragments.
// TODO: Consider removing it when update to androidx.fragment:1.2.0
if (hasRemovingParent && this.owner.selectedIndex === this.index) {
const bitmapDrawable = new android.graphics.drawable.BitmapDrawable(appResources, this.backgroundBitmap);
this.owner._originalBackground = this.owner.backgroundColor || new Color('White');
this.owner.nativeViewProtected.setBackgroundDrawable(bitmapDrawable);
this.backgroundBitmap = null;
let thisView = this.getView();
if (thisView) {
let thisViewParent = thisView.getParent();
if (thisViewParent && thisViewParent instanceof android.view.ViewGroup) {
thisViewParent.removeView(thisView);
}
}
}
super.onDestroyView();
}
public onPause(): void {
const hasRemovingParent = this.getRemovingParentFragment();
// Get view as bitmap and set it as background. This is workaround for the disapearing nested fragments.
// TODO: Consider removing it when update to androidx.fragment:1.2.0
if (hasRemovingParent && this.owner.selectedIndex === this.index) {
this.backgroundBitmap = this.loadBitmapFromView(this.owner.nativeViewProtected);
}
super.onPause();
}
private loadBitmapFromView(view: android.view.View): android.graphics.Bitmap {
// Another way to get view bitmap. Test performance vs setDrawingCacheEnabled
// const width = view.getWidth();
// const height = view.getHeight();
// const bitmap = android.graphics.Bitmap.createBitmap(width, height, android.graphics.Bitmap.Config.ARGB_8888);
// const canvas = new android.graphics.Canvas(bitmap);
// view.layout(0, 0, width, height);
// view.draw(canvas);
view.setDrawingCacheEnabled(true);
const bitmap = android.graphics.Bitmap.createBitmap(view.getDrawingCache());
view.setDrawingCacheEnabled(false);
return bitmap;
}
} }
@NativeClass @NativeClass
@ -250,7 +199,7 @@ function iterateIndexRange(index: number, eps: number, lastIndex: number, callba
@CSSType('BottomNavigation') @CSSType('BottomNavigation')
export class BottomNavigation extends TabNavigationBase { export class BottomNavigation extends TabNavigationBase {
private _contentView: org.nativescript.widgets.ContentLayout; private _contentView: org.nativescript.widgets.ContentLayout;
private _contentViewId: number = -1; private _contentViewId = -1;
private _bottomNavigationBar: org.nativescript.widgets.BottomNavigationBar; private _bottomNavigationBar: org.nativescript.widgets.BottomNavigationBar;
private _currentFragment: androidx.fragment.app.Fragment; private _currentFragment: androidx.fragment.app.Fragment;
private _currentTransaction: androidx.fragment.app.FragmentTransaction; private _currentTransaction: androidx.fragment.app.FragmentTransaction;
@ -347,8 +296,8 @@ export class BottomNavigation extends TabNavigationBase {
const lastIndex = this.items.length - 1; const lastIndex = this.items.length - 1;
const offsideItems = 0; const offsideItems = 0;
let toUnload = []; const toUnload = [];
let toLoad = []; const toLoad = [];
iterateIndexRange(newIndex, offsideItems, lastIndex, (i) => toLoad.push(i)); iterateIndexRange(newIndex, offsideItems, lastIndex, (i) => toLoad.push(i));
@ -610,7 +559,7 @@ export class BottomNavigation extends TabNavigationBase {
tabItemSpec.backgroundColor = backgroundColor ? backgroundColor.android : this.getTabBarBackgroundArgbColor(); tabItemSpec.backgroundColor = backgroundColor ? backgroundColor.android : this.getTabBarBackgroundArgbColor();
// COLOR // COLOR
let itemColor = this.selectedIndex === tabStripItem._index ? this._selectedItemColor : this._unSelectedItemColor; const itemColor = this.selectedIndex === tabStripItem._index ? this._selectedItemColor : this._unSelectedItemColor;
const color = itemColor || titleLabel.style.color; const color = itemColor || titleLabel.style.color;
tabItemSpec.color = color && color.android; tabItemSpec.color = color && color.android;
@ -669,7 +618,7 @@ export class BottomNavigation extends TabNavigationBase {
image = this.getFixedSizeIcon(image); image = this.getFixedSizeIcon(image);
} }
let imageDrawable = new android.graphics.drawable.BitmapDrawable(application.android.context.getResources(), image); const imageDrawable = new android.graphics.drawable.BitmapDrawable(application.android.context.getResources(), image);
return { return {
drawable: imageDrawable, drawable: imageDrawable,
@ -681,7 +630,7 @@ export class BottomNavigation extends TabNavigationBase {
} }
private getIconInfo(tabStripItem: TabStripItem, color?: Color): IconInfo { private getIconInfo(tabStripItem: TabStripItem, color?: Color): IconInfo {
let originalIcon = this.getOriginalIcon(tabStripItem, color); const originalIcon = this.getOriginalIcon(tabStripItem, color);
return this.getDrawableInfo(originalIcon); return this.getDrawableInfo(originalIcon);
} }
@ -841,7 +790,7 @@ export class BottomNavigation extends TabNavigationBase {
} }
public setTabBarTextTransform(value: TextTransform): void { public setTabBarTextTransform(value: TextTransform): void {
let items = this.tabStrip && this.tabStrip.items; const items = this.tabStrip && this.tabStrip.items;
if (items) { if (items) {
items.forEach((tabStripItem) => { items.forEach((tabStripItem) => {
if (tabStripItem.label && tabStripItem.nativeViewProtected) { if (tabStripItem.label && tabStripItem.nativeViewProtected) {

View File

@ -81,7 +81,7 @@ function getAttachListener(): android.view.View.OnAttachStateChangeListener {
export class Frame extends FrameBase { export class Frame extends FrameBase {
public _originalBackground: any; public _originalBackground: any;
private _android: AndroidFrame; private _android: AndroidFrame;
private _containerViewId: number = -1; private _containerViewId = -1;
private _tearDownPending = false; private _tearDownPending = false;
private _attachedToWindow = false; private _attachedToWindow = false;
private _cachedTransitionState: TransitionState; private _cachedTransitionState: TransitionState;
@ -190,7 +190,7 @@ export class Frame extends FrameBase {
// simulated navigation (NoTransition, zero duration animator) and thus the fragment immediately disappears; // simulated navigation (NoTransition, zero duration animator) and thus the fragment immediately disappears;
// the user only sees the animation of the entering fragment as per its specific enter animation settings. // the user only sees the animation of the entering fragment as per its specific enter animation settings.
// NOTE: we are restoring the animation settings in Frame.setCurrent(...) as navigation completes asynchronously // NOTE: we are restoring the animation settings in Frame.setCurrent(...) as navigation completes asynchronously
let cachedTransitionState = getTransitionState(this._currentEntry); const cachedTransitionState = getTransitionState(this._currentEntry);
if (cachedTransitionState) { if (cachedTransitionState) {
this._cachedTransitionState = cachedTransitionState; this._cachedTransitionState = cachedTransitionState;
@ -425,7 +425,7 @@ export class Frame extends FrameBase {
navigationTransition = null; navigationTransition = null;
} }
let isNestedDefaultTransition = !currentEntry; const isNestedDefaultTransition = !currentEntry;
_setAndroidFragmentTransitions(animated, navigationTransition, currentEntry, newEntry, this._android.frameId, transaction, isNestedDefaultTransition); _setAndroidFragmentTransitions(animated, navigationTransition, currentEntry, newEntry, this._android.frameId, transaction, isNestedDefaultTransition);
@ -659,7 +659,7 @@ function clearEntry(entry: BackstackEntry): void {
} }
let framesCounter = 0; let framesCounter = 0;
let framesCache = new Array<WeakRef<AndroidFrame>>(); const framesCache = new Array<WeakRef<AndroidFrame>>();
class AndroidFrame extends Observable implements AndroidFrameDefinition { class AndroidFrame extends Observable implements AndroidFrameDefinition {
public rootViewGroup: android.view.ViewGroup; public rootViewGroup: android.view.ViewGroup;
@ -689,7 +689,7 @@ class AndroidFrame extends Observable implements AndroidFrameDefinition {
} }
public get activity(): androidx.appcompat.app.AppCompatActivity { public get activity(): androidx.appcompat.app.AppCompatActivity {
let activity: androidx.appcompat.app.AppCompatActivity = this.owner._context; const activity: androidx.appcompat.app.AppCompatActivity = this.owner._context;
if (activity) { if (activity) {
return activity; return activity;
} }
@ -708,12 +708,12 @@ class AndroidFrame extends Observable implements AndroidFrameDefinition {
} }
public get actionBar(): android.app.ActionBar { public get actionBar(): android.app.ActionBar {
let activity = this.currentActivity; const activity = this.currentActivity;
if (!activity) { if (!activity) {
return undefined; return undefined;
} }
let bar = activity.getActionBar(); const bar = activity.getActionBar();
if (!bar) { if (!bar) {
return undefined; return undefined;
} }
@ -727,7 +727,7 @@ class AndroidFrame extends Observable implements AndroidFrameDefinition {
return activity; return activity;
} }
let frames = _stack(); const frames = _stack();
for (let length = frames.length, i = length - 1; i >= 0; i--) { for (let length = frames.length, i = length - 1; i >= 0; i--) {
activity = frames[i].android.activity; activity = frames[i].android.activity;
if (activity) { if (activity) {
@ -812,7 +812,7 @@ function startActivity(activity: androidx.appcompat.app.AppCompatActivity, frame
function getFrameByNumberId(frameId: number): Frame { function getFrameByNumberId(frameId: number): Frame {
// Find the frame for this activity. // Find the frame for this activity.
for (let i = 0; i < framesCache.length; i++) { for (let i = 0; i < framesCache.length; i++) {
let aliveFrame = framesCache[i].get(); const aliveFrame = framesCache[i].get();
if (aliveFrame && aliveFrame.frameId === frameId) { if (aliveFrame && aliveFrame.frameId === frameId) {
return aliveFrame.owner; return aliveFrame.owner;
} }
@ -975,23 +975,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 {
try {
if (Trace.isEnabled()) { if (Trace.isEnabled()) {
Trace.write(`${fragment}.onDestroyView()`, Trace.categories.NativeLifecycle); Trace.write(`${fragment}.onDestroyView()`, Trace.categories.NativeLifecycle);
} }
const hasRemovingParent = fragment.getRemovingParentFragment();
if (hasRemovingParent) {
const bitmapDrawable = new android.graphics.drawable.BitmapDrawable(application.android.context.getResources(), this.backgroundBitmap);
this.frame._originalBackground = this.frame.backgroundColor || new Color('White');
this.frame.nativeViewProtected.setBackgroundDrawable(bitmapDrawable);
this.backgroundBitmap = null;
}
} finally {
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 {
@ -1024,18 +1012,8 @@ class FragmentCallbacksImplementation implements AndroidFragmentCallbacks {
@profile @profile
public onPause(fragment: org.nativescript.widgets.FragmentBase, superFunc: Function): void { public onPause(fragment: org.nativescript.widgets.FragmentBase, superFunc: Function): void {
try {
// Get view as bitmap and set it as background. This is workaround for the disapearing nested fragments.
// TODO: Consider removing it when update to androidx.fragment:1.2.0
const hasRemovingParent = fragment.getRemovingParentFragment();
if (hasRemovingParent) {
this.backgroundBitmap = this.loadBitmapFromView(this.frame.nativeViewProtected);
}
} finally {
superFunc.call(fragment); superFunc.call(fragment);
} }
}
@profile @profile
public onStop(fragment: androidx.fragment.app.Fragment, superFunc: Function): void { public onStop(fragment: androidx.fragment.app.Fragment, superFunc: Function): void {
@ -1101,7 +1079,7 @@ class ActivityCallbacksImplementation implements AndroidActivityCallbacks {
// If there is savedInstanceState and moduleLoaded is false we are restarted but process was killed. // If there is savedInstanceState and moduleLoaded is false we are restarted but process was killed.
// For now we treat it like first run (e.g. we are not passing savedInstanceState so no fragments are being restored). // For now we treat it like first run (e.g. we are not passing savedInstanceState so no fragments are being restored).
// When we add support for application save/load state - revise this logic. // When we add support for application save/load state - revise this logic.
let isRestart = !!savedInstanceState && moduleLoaded; const isRestart = !!savedInstanceState && moduleLoaded;
superFunc.call(activity, isRestart ? savedInstanceState : null); superFunc.call(activity, isRestart ? savedInstanceState : null);
// Try to get the rootViewId form the saved state in case the activity // Try to get the rootViewId form the saved state in case the activity
@ -1265,7 +1243,7 @@ class ActivityCallbacksImplementation implements AndroidActivityCallbacks {
} }
@profile @profile
public onRequestPermissionsResult(activity: any, requestCode: number, permissions: Array<String>, grantResults: Array<number>, superFunc: Function): void { public onRequestPermissionsResult(activity: any, requestCode: number, permissions: Array<string>, grantResults: Array<number>, superFunc: Function): void {
if (Trace.isEnabled()) { if (Trace.isEnabled()) {
Trace.write('NativeScriptActivity.onRequestPermissionsResult;', Trace.categories.NativeLifecycle); Trace.write('NativeScriptActivity.onRequestPermissionsResult;', Trace.categories.NativeLifecycle);
} }

View File

@ -82,49 +82,6 @@ function initializeNativeClasses() {
return tabItem.view.nativeViewProtected; return tabItem.view.nativeViewProtected;
} }
public onDestroyView() {
const hasRemovingParent = this.getRemovingParentFragment();
// Get view as bitmap and set it as background. This is workaround for the disapearing nested fragments.
// TODO: Consider removing it when update to androidx.fragment:1.2.0
if (hasRemovingParent && this.owner.selectedIndex === this.index) {
const bitmapDrawable = new android.graphics.drawable.BitmapDrawable(appResources, this.backgroundBitmap);
this.owner._originalBackground = this.owner.backgroundColor || new Color('White');
this.owner.nativeViewProtected.setBackground(bitmapDrawable);
this.backgroundBitmap = null;
}
super.onDestroyView();
}
public onPause(): void {
const hasRemovingParent = this.getRemovingParentFragment();
// Get view as bitmap and set it as background. This is workaround for the disapearing nested fragments.
// TODO: Consider removing it when update to androidx.fragment:1.2.0
if (hasRemovingParent && this.owner.selectedIndex === this.index) {
this.backgroundBitmap = this.loadBitmapFromView(this.owner.nativeViewProtected);
}
super.onPause();
}
private loadBitmapFromView(view: android.view.View): android.graphics.Bitmap {
// Another way to get view bitmap. Test performance vs setDrawingCacheEnabled
// const width = view.getWidth();
// const height = view.getHeight();
// const bitmap = android.graphics.Bitmap.createBitmap(width, height, android.graphics.Bitmap.Config.ARGB_8888);
// const canvas = new android.graphics.Canvas(bitmap);
// view.layout(0, 0, width, height);
// view.draw(canvas);
view.setDrawingCacheEnabled(true);
const bitmap = android.graphics.Bitmap.createBitmap(view.getDrawingCache());
view.setDrawingCacheEnabled(false);
return bitmap;
}
} }
const POSITION_UNCHANGED = -1; const POSITION_UNCHANGED = -1;
@ -442,7 +399,7 @@ export class TabView extends TabViewBase {
private _tabLayout: org.nativescript.widgets.TabLayout; private _tabLayout: org.nativescript.widgets.TabLayout;
private _viewPager: androidx.viewpager.widget.ViewPager; private _viewPager: androidx.viewpager.widget.ViewPager;
private _pagerAdapter: androidx.viewpager.widget.PagerAdapter; private _pagerAdapter: androidx.viewpager.widget.PagerAdapter;
private _androidViewId: number = -1; private _androidViewId = -1;
public _originalBackground: any; public _originalBackground: any;
constructor() { constructor() {
@ -549,8 +506,8 @@ export class TabView extends TabViewBase {
const lastIndex = items.length - 1; const lastIndex = items.length - 1;
const offsideItems = this.androidTabsPosition === 'top' ? this.androidOffscreenTabLimit : 1; const offsideItems = this.androidTabsPosition === 'top' ? this.androidOffscreenTabLimit : 1;
let toUnload = []; const toUnload = [];
let toLoad = []; const toLoad = [];
iterateIndexRange(newIndex, offsideItems, lastIndex, (i) => toLoad.push(i)); iterateIndexRange(newIndex, offsideItems, lastIndex, (i) => toLoad.push(i));
@ -623,7 +580,7 @@ export class TabView extends TabViewBase {
private disposeCurrentFragments(): void { private disposeCurrentFragments(): void {
const fragmentManager = this._getFragmentManager(); const fragmentManager = this._getFragmentManager();
const transaction = fragmentManager.beginTransaction(); const transaction = fragmentManager.beginTransaction();
let fragments = <Array<any>>fragmentManager.getFragments().toArray(); const fragments = <Array<any>>fragmentManager.getFragments().toArray();
for (let i = 0; i < fragments.length; i++) { for (let i = 0; i < fragments.length; i++) {
transaction.remove(fragments[i]); transaction.remove(fragments[i]);
} }
@ -769,7 +726,7 @@ export class TabView extends TabViewBase {
return getDefaultAccentColor(this._context); return getDefaultAccentColor(this._context);
} }
[androidSelectedTabHighlightColorProperty.setNative](value: number | Color) { [androidSelectedTabHighlightColorProperty.setNative](value: number | Color) {
let tabLayout = this._tabLayout; const tabLayout = this._tabLayout;
const color = value instanceof Color ? value.android : value; const color = value instanceof Color ? value.android : value;
tabLayout.setSelectedIndicatorColors([color]); tabLayout.setSelectedIndicatorColors([color]);
} }