8000 feat(core): reusable views (#9163) · NativeScript/NativeScript@e6df466 · GitHub
[go: up one dir, main page]

Skip to content

Commit e6df466

Browse files
edusperoniNathanWalker
authored andcommitted
feat(core): reusable views (#9163)
1 parent e9e4934 commit e6df466

File tree

6 files changed

+287
-38
lines changed

6 files changed

+287
-38
lines changed
Lines changed: 15 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,15 @@
1+
.test-label {
2+
padding: 10;
3+
background-color: black;
4+
color: white;
5+
}
6+
7+
.stack1 {
8+
background-color: green;
9+
color: white;
10+
}
11+
12+
.stack2 {
13+
background-color: blue;
14+
color: red;
15+
}

apps/ui/src/issues/issue-7469-page.ts

Lines changed: 158 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,158 @@
1+
import { EventData, Label, StackLayout } from '@nativescript/core';
2+
import { addCallback, removeCallback, start, stop } from '@nativescript/core/fps-meter';
3+
4+
let callbackId;
5+
let fpsLabel: any;
6+
export function startFPSMeter() {
7+
callbackId = addCallback((fps: number, minFps: number) => {
8+
// console.log(`Frames per seconds: ${fps.toFixed(2)}`);
9+
// console.log(minFps.toFixed(2));
10+
if (fpsLabel) {
11+
fpsLabel.text = `${fps}`;
12+
}
13+
});
14+
start();
15+
}
16+
17+
export function stopFPSMeter() {
18+
removeCallback(callbackId);
19+
stop();
20+
}
21+
22+
let timeouts = [];
23+
let intervals = [];
24+
25+
let reusableItem;
26+
let vcToggle;
27+
let loaded = false;
28+
let isIn1 = false;
29+
30+
function updateVcToggleText() {
31+
vcToggle.text = `Container is${reusableItem.reusable ? ' ' : ' NOT '}Reusable`
32+
}
33+
34+
export function pageLoaded(args) {
35+
startFPSMeter();
36+
if (loaded) {
37+
fpsLabel = null;
38+
// stopFPSMeter();
39+
timeouts.forEach((v) => clearTimeout(v));
40+
intervals.forEach((v) => clearInterval(v));
41+
reusableItem._tearDownUI(true);
42+
}
43+
loaded = true;
44+
reusableItem = args.object.getViewById('reusableItem');
45+
vcToggle = args.object.getViewById('vcToggle');
46+
updateVcToggleText();
47+
fpsLabel = args.object.getViewById('fpslabel');
48+
const stack1: StackLayout = args.object.getViewById('stack1');
49+
const stack2: StackLayout = args.object.getViewById('stack2');
50+
setTimeout(() => {
51+
// label.android.setTextColor(new Color("red").android);
52+
// label.android.setBackgroundColor(new Color("red").android);
53+
startFPSMeter();
54+
console.log('setRed');
55+
}, 1000);
56+
// console.log(label._context);
57+
// isIn1 = false;
58+
// timeouts.push(setTimeout(() => {
59+
// intervals.push(setInterval(() => {
60+
// label.parent.removeChild(label);
61+
// // console.log(label.nativeView);
62+
// if(isIn1) {
63+
// isIn1 = false;
64+
// stack2.addChild(label);
65+
// } else {
66+
// isIn1 = true;
67+
// stack1.addChild(label);
68+
// }
69+
// }, 10));
70+
// }, 1001));
71+
}
72+
73+
export function pageUnloaded(args) {
74+
//
75+
}
76+
77+
export function makeReusable(args: EventData) {
78+
console.log('loaded:', args.object);
79+
// console.log("making reusable");
80+
if ((args.object as any).___reusableRan) {
81+
return;
82+
}
83+
(args.object as any).___reusableRan = true;
84+
(args.object as any).reusable = true;
85+
if(args.object === reusableItem) {
86+
updateVcToggleText();
87+
}
88+
}
89+
90+
export function onReusableUnloaded(args: EventData) {
91+
console.log('unloaded:', args.object);
92+
}
93+
var testLabel: Label;
94+
95+
export function test(args: any) {
96+
const page = args.object.page;
97+
reusableItem = page.getViewById('reusableItem');
98+
const stack1: StackLayout = page.getViewById('stack1');
99+
const stack2: StackLayout = page.getViewById('stack2');
100+
if (!testLabel) {
101+
testLabel = new Label();
102+
testLabel.text = 'This label is not reusable and is dynamic';
103+
testLabel.on('loaded', () => {
104+
console.log('LODADED testLabel');
105+
});
106+
testLabel.on('unloaded', () => {
107+
console.log('UNLODADED testLabel');
108+
});
109+
}
110+
reusableItem.parent.removeChild(reusableItem);
111+
if (!reusableItem._suspendNativeUpdatesCount) {
112+
console.log('reusableItem SHOULD BE UNLOADED');
113+
}
114+
if (!testLabel._suspendNativeUpdatesCount) {
115+
console.log('testLabel SHOULD BE UNLOADED');
116+
}
117+
if (!testLabel.parent) {
118+
reusableItem.addChild(testLabel);
119+
}
120+
if (!testLabel.nativeView) {
121+
console.log('testLabel NATIVE VIEW SHOULD BE CREATED');
122+
}
123+
if (!testLabel._suspendNativeUpdatesCount) {
124+
console.log('testLabel SHOULD BE UNLOADED');
125+
}
126+
if (isIn1) {
127+
isIn1 = false;
128+
stack2.addChild(reusableItem);
129+
} else {
130+
isIn1 = true;
131+
stack1.addChild(reusableItem);
132+
}
133+
if (reusableItem._suspendNativeUpdatesCount) {
134+
console.log('reusableItem SHOULD BE LOADED AND RECEIVING UPDATES');
135+
}
136+
if (testLabel._suspendNativeUpdatesCount) {
137+
console.log('testLabel SHOULD BE LOADED AND RECEIVING UPDATES');
138+
}
139+
// console.log("onTap");
140+
// alert("onTap");
141+
}
142+
let ignoreInput = false;
143+
144+
export function toggleReusable(args: EventData) {
145+
if (ignoreInput) {
146+
return;
147+
}
148+
ignoreInput = true;
149+
setTimeout(() => (ignoreInput = false), 0); // hack to avoid gesture collision
150+
const target: any = args.object;
151+
target.reusable = !target.reusable;
152+
console.log(`${target} is now ${target.reusable ? '' : 'NOT '}reusable`);
153+
}
154+
155+
export function toggleVCReusable() {
156+
reusableItem.reusable = !reusableItem.reusable;
157+
updateVcToggleText();
158+
}
Lines changed: 37 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,37 @@
1+
<Page
2+
xmlns="http://schemas.nativescript.org/tns.xsd" class="page" loaded="pageLoaded" unloaded="pageUnloaded">
3+
<StackLayout class="p-20">
4+
<Button text="Swap locations" tap="test"/>
5+
<Button id="vcToggle" text="" tap="toggleVCReusable"/>
6+
<Label text="Longpress items to toggle reusability"></Label>
7+
<Label id="fpslabel" text=""></Label>
8+
<StackLayout id="reusableItem" loaded="makeReusable">
9+
<Label longPress="toggleReusable" loaded="makeReusable" unloaded="onReusableUnloaded" text="abc"></Label>
10+
<Label longPress="toggleReusable" loaded="makeReusable" unloaded="onReusableUnloaded" text="abc"></Label>
11+
<Label longPress="toggleReusable" loaded="makeReusable" unloaded="onReusableUnloaded" text="abc"></Label>
12+
<Label longPress="toggleReusable" loaded="makeReusable" unloaded="onReusableUnloaded" text="abc"></Label>
13+
<Label longPress="toggleReusable" loaded="makeReusable" unloaded="onReusableUnloaded" text="abc"></Label>
14+
<Label longPress="toggleReusable" loaded="makeReusable" unloaded="onReusableUnloaded" text="abc"></Label>
15+
<Label longPress="toggleReusable" loaded="makeReusable" unloaded="onReusableUnloaded" text="abc"></Label>
16+
<Label longPress="toggleReusable" loaded="makeReusable" unloaded="onReusableUnloaded" text="abc"></Label>
17+
<Label longPress="toggleReusable" loaded="makeReusable" unloaded="onReusableUnloaded" text="abc"></Label>
18+
<Label longPress="toggleReusable" loaded="makeReusable" unloaded="onReusableUnloaded" text="abc"></Label>
19+
<Label longPress="toggleReusable" loaded="makeReusable" unloaded="onReusableUnloaded" text="abc"></Label>
20+
<Label longPress="toggleReusable" loaded="makeReusable" unloaded="onReusableUnloaded" text="abc"></Label>
21+
<Label longPress="toggleReusable" loaded="makeReusable" unloaded="onReusableUnloaded" text="abc"></Label>
22+
<Label longPress="toggleReusable" loaded="makeReusable" unloaded="onReusableUnloaded" text="abc"></Label>
23+
<Label longPress="toggleReusable" loaded="makeReusable" unloaded="onReusableUnloaded" text="abc"></Label>
24+
<Label longPress="toggleReusable" loaded="makeReusable" unloaded="onReusableUnloaded" text="abc"></Label>
25+
<Label longPress="toggleReusable" loaded="makeReusable" unloaded="onReusableUnloaded" text="abc"></Label>
26+
<Label longPress="toggleReusable" loaded="makeReusable" unloaded="onReusableUnloaded" text="abc"></Label>
27+
<Label longPress="toggleReusable" loaded="makeReusable" unloaded="onReusableUnloaded" text="abc"></Label>
28+
<WebView longPress="toggleReusable" loaded="makeReusable" unloaded="onReusableUnloaded" width="100" height="100" src="https://google.com"></WebView>
29+
</StackLayout>
30+
<StackLayout id="stack1" class="stack1">
31+
<Label text="Stack 1"></Label>
32+
</StackLayout>
33+
<StackLayout id="stack2" class="stack2">
34+
<Label text="Stack 2"></Label>
35+
</StackLayout>
36+
</StackLayout>
37+
</Page>

apps/ui/src/issues/main-page.ts

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -32,6 +32,7 @@ export function loadExamples() {
3232
examples.set('ng-repo-1626', 'issues/issue-ng-repo-1626-page');
3333
examples.set('6439', 'issues/issue-6439-page');
3434
examples.set('open-file-6895', 'issues/open-file-6895-page');
35+
examples.set("7469", "issues/issue-7469-page");
3536

3637
return examples;
3738
}

packages/core/ui/core/view-base/index.d.ts

Lines changed: 13 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -236,6 +236,12 @@ export abstract class ViewBase extends Observable {
236236
public nativeView: any;
237237
public bindingContext: any;
238238

239+
/**
240+
* Gets or sets if the view is reusable.
241+
* Reusable views are not automatically destroyed when removed from the View tree.
242+
*/
243+
public reusable: boolean;
244+
239245
/**
240246
* Gets the name of the constructor function for this instance. E.g. for a Button class this will return "Button".
241247
*/
@@ -365,6 +371,13 @@ export abstract class ViewBase extends Observable {
365371
*/
366372
_tearDownUI(force?: boolean): void;
367373

374+
/**
375+
* Tears down the UI of a reusable node by making it no longer reusable.
376+
* @see _tearDownUI
377+
* @param forceDestroyChildren Force destroy the children (even if they are reusable)
378+
*/
379+
destroyNode(forceDestroyChildren?: boolean): void;
380+
368381
/**
369382
* Creates a native view.
370383
* Returns either android.view.View or UIView.

0 commit comments

Comments
 (0)
0