Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

fix(android): rewrite the GridLayout to make as less JNI calls as pos… #10402

Open
wants to merge 4 commits into
base: main
Choose a base branch
from
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Jump to
Jump to file
Failed to load files.
Diff view
Diff view
47 changes: 39 additions & 8 deletions packages/core/ui/layouts/grid-layout/grid-layout-common.ts
Original file line number Diff line number Diff line change
Expand Up @@ -63,20 +63,23 @@ function convertGridLength(value: string): ItemSpec {
}
}

function parseAndAddItemSpecs(value: string, func: (itemSpec: ItemSpec) => void): void {
function parseAndAddItemSpecs(value: string) {
// ensure value is a string since view bindings could be parsed as number/int's here
const specs: ItemSpec[] = [];
const arr = `${value}`.split(/[\s,]+/);
for (let i = 0, length = arr.length; i < length; i++) {
const str = arr[i].trim();
if (str.length > 0) {
func(convertGridLength(arr[i].trim()));
specs.push(convertGridLength(arr[i].trim()));
}
}
return specs;
}

export class ItemSpec extends Observable implements ItemSpecDefinition {
private _value: number;
private _unitType: GridUnitType;
toJSON?: () => any;

constructor(...args) {
super();
Expand Down Expand Up @@ -147,8 +150,8 @@ export class ItemSpec extends Observable implements ItemSpecDefinition {

@CSSType('GridLayout')
export class GridLayoutBase extends LayoutBase implements GridLayoutDefinition {
private _rows: Array<ItemSpec> = new Array<ItemSpec>();
private _cols: Array<ItemSpec> = new Array<ItemSpec>();
protected _rows: Array<ItemSpec> = new Array<ItemSpec>();
protected _cols: Array<ItemSpec> = new Array<ItemSpec>();

public static getColumn(element: View): number {
return validateArgs(element).col;
Expand Down Expand Up @@ -182,22 +185,48 @@ export class GridLayoutBase extends LayoutBase implements GridLayoutDefinition {
validateArgs(element).rowSpan = value;
}

public addRow(itemSpec: ItemSpec) {
public _addRow(itemSpec: ItemSpec) {
validateItemSpec(itemSpec);
itemSpec.owner = this;
this._rows.push(itemSpec);
}

public addRow(itemSpec: ItemSpec) {
this._addRow(itemSpec);
this._onRowAdded(itemSpec);
this.invalidate();
}

public addColumn(itemSpec: ItemSpec) {
public addRows(itemSpecs: ItemSpec[]) {
for (let index = 0; index < itemSpecs.length; index++) {
const itemSpec = itemSpecs[index];
this._addRow(itemSpec);
this._onRowAdded(itemSpec);
}
this.invalidate();
}

public _addColumn(itemSpec: ItemSpec) {
validateItemSpec(itemSpec);
itemSpec.owner = this;
this._cols.push(itemSpec);
}

public addColumn(itemSpec: ItemSpec) {
this._addColumn(itemSpec);
this._onColumnAdded(itemSpec);
this.invalidate();
}

public addColumns(itemSpecs: ItemSpec[]) {
for (let index = 0; index < itemSpecs.length; index++) {
const itemSpec = itemSpecs[index];
this._addColumn(itemSpec);
this._onColumnAdded(itemSpec);
}
this.invalidate();
}

public addChildAtCell(view: View, row: number, column: number, rowSpan?: number, columnSpan?: number): void {
this.addChild(view);
GridLayoutBase.setRow(view, row);
Expand Down Expand Up @@ -316,12 +345,14 @@ export class GridLayoutBase extends LayoutBase implements GridLayoutDefinition {

set rows(value: string) {
this.removeRows();
parseAndAddItemSpecs(value, (spec: ItemSpec) => this.addRow(spec));
const specs = parseAndAddItemSpecs(value);
this.addRows(specs);
}

set columns(value: string) {
this.removeColumns();
parseAndAddItemSpecs(value, (spec: ItemSpec) => this.addColumn(spec));
const specs = parseAndAddItemSpecs(value);
this.addColumns(specs);
}
}

Expand Down
118 changes: 73 additions & 45 deletions packages/core/ui/layouts/grid-layout/index.android.ts
Original file line number Diff line number Diff line change
Expand Up @@ -20,32 +20,26 @@ View.prototype[columnProperty.setNative] = makeNativeSetter<number>((lp, value)
View.prototype[rowSpanProperty.setNative] = makeNativeSetter<number>((lp, value) => (lp.rowSpan = value));
View.prototype[columnSpanProperty.setNative] = makeNativeSetter<number>((lp, value) => (lp.columnSpan = value));

function createNativeSpec(itemSpec: ItemSpec): org.nativescript.widgets.ItemSpec {
switch (itemSpec.gridUnitType) {
ItemSpecBase.prototype.toJSON = function () {
let result;
switch (this.gridUnitType) {
case GridUnitType.AUTO:
return new org.nativescript.widgets.ItemSpec(itemSpec.value, org.nativescript.widgets.GridUnitType.auto);

case GridUnitType.STAR:
return new org.nativescript.widgets.ItemSpec(itemSpec.value, org.nativescript.widgets.GridUnitType.star);

result = { type: 0 /* org.nativescript.widgets.GridUnitType.auto */, value: this.value };
break;
case GridUnitType.PIXEL:
return new org.nativescript.widgets.ItemSpec(itemSpec.value * layout.getDisplayDensity(), org.nativescript.widgets.GridUnitType.pixel);

result = { type: 1 /* org.nativescript.widgets.GridUnitType.pixel */, value: this.value * layout.getDisplayDensity() };
break;
case GridUnitType.STAR:
result = { type: 2 /* org.nativescript.widgets.GridUnitType.star */, value: this.value };
break;
default:
throw new Error('Invalid gridUnitType: ' + itemSpec.gridUnitType);
return null;
}
}

export class ItemSpec extends ItemSpecBase {
nativeSpec: org.nativescript.widgets.ItemSpec;
return result;
};

public get actualLength(): number {
if (this.nativeSpec) {
return Math.round(this.nativeSpec.getActualLength() / layout.getDisplayDensity());
}

return 0;
}
interface ItemSpec extends ItemSpecBase {
toJSON(): { value: number; type: number };
}

export class GridLayout extends GridLayoutBase {
Expand All @@ -58,54 +52,88 @@ export class GridLayout extends GridLayoutBase {
public initNativeView(): void {
super.initNativeView();
// Update native GridLayout
this.rowsInternal.forEach((itemSpec: ItemSpec, index, rows) => {
this._onRowAdded(itemSpec);
}, this);
this.columnsInternal.forEach((itemSpec: ItemSpec, index, rows) => {
this._onColumnAdded(itemSpec);
}, this);
const jsonRows = JSON.stringify(this.rowsInternal.map((itemSpec: ItemSpec) => itemSpec.toJSON()).filter((j) => !!j));
const jsonColumns = JSON.stringify(this.columnsInternal.map((itemSpec: ItemSpec) => itemSpec.toJSON()).filter((j) => !!j));
this.nativeViewProtected.addRowsAndColumnsFromJSON(jsonRows, jsonColumns);
}

public resetNativeView() {
// Update native GridLayout
for (let i = this.rowsInternal.length; i--; i >= 0) {
const itemSpec = <ItemSpec>this.rowsInternal[i];
this._onRowRemoved(itemSpec, i);
}

for (let i = this.columnsInternal.length; i--; i >= 0) {
const itemSpec = <ItemSpec>this.columnsInternal[i];
this._onColumnRemoved(itemSpec, i);
}

this.nativeViewProtected.reset();
super.resetNativeView();
}

public _onRowAdded(itemSpec: ItemSpec) {
if (this.nativeViewProtected) {
const nativeSpec = createNativeSpec(itemSpec);
itemSpec.nativeSpec = nativeSpec;
this.nativeViewProtected.addRow(nativeSpec);
const nativeData = itemSpec.toJSON();
this.nativeViewProtected.addRow(nativeData.value, nativeData.type);
}
}

public addRows(itemSpecs: ItemSpec[]) {
let jsonArray = [];
const nativeView = this.nativeViewProtected;
const initialized = !!nativeView;
for (let index = 0; index < itemSpecs.length; index++) {
const itemSpec = itemSpecs[index];
this._addRow(itemSpec);
if (initialized) {
jsonArray.push(itemSpec.toJSON());
}
}
if (initialized) {
nativeView.addRowsFromJSON(JSON.stringify(jsonArray.filter((s) => !!s)));
}
}

public addColumns(itemSpecs: ItemSpec[]) {
let jsonArray = [];
const nativeView = this.nativeViewProtected;
const initialized = !!nativeView;
for (let index = 0; index < itemSpecs.length; index++) {
const itemSpec = itemSpecs[index];
this._addColumn(itemSpec);
if (initialized) {
jsonArray.push(itemSpec.toJSON());
}
}
if (initialized) {
nativeView.addColumnsFromJSON(JSON.stringify(jsonArray.filter((s) => !!s)));
}
}

public _onColumnAdded(itemSpec: ItemSpec) {
if (this.nativeViewProtected) {
const nativeSpec = createNativeSpec(itemSpec);
itemSpec.nativeSpec = nativeSpec;
this.nativeViewProtected.addColumn(nativeSpec);
const nativeData = itemSpec.toJSON();
this.nativeViewProtected.addColumn(nativeData.value, nativeData.type);
}
}

public removeColumns() {
if (this._cols.length) {
if (this.nativeViewProtected) {
this.nativeViewProtected.clearColumns();
}
this._cols.length = 0;
}
}

public removeRows() {
if (this._rows.length) {
if (this.nativeViewProtected) {
this.nativeViewProtected.clearRows();
}
this._rows.length = 0;
}
}

public _onRowRemoved(itemSpec: ItemSpec, index: number) {
itemSpec.nativeSpec = null;
if (this.nativeViewProtected) {
this.nativeViewProtected.removeRowAt(index);
}
}

public _onColumnRemoved(itemSpec: ItemSpec, index: number) {
itemSpec.nativeSpec = null;
if (this.nativeViewProtected) {
this.nativeViewProtected.removeColumnAt(index);
}
Expand Down
10 changes: 7 additions & 3 deletions packages/core/ui/page/index.android.ts
Original file line number Diff line number Diff line change
Expand Up @@ -19,9 +19,13 @@ export class Page extends PageBase {

public createNativeView() {
const layout = new org.nativescript.widgets.GridLayout(this._context);
layout.addRow(new org.nativescript.widgets.ItemSpec(1, org.nativescript.widgets.GridUnitType.auto));
layout.addRow(new org.nativescript.widgets.ItemSpec(1, org.nativescript.widgets.GridUnitType.star));

layout.addRowsFromJSON(
JSON.stringify([
{ value: 1, type: 0 /* org.nativescript.widgets.GridUnitType.auto */ },
{ value: 1, type: 2 /* org.nativescript.widgets.GridUnitType.star */ },
])
);
console.log('Page', 'createNativeView');
return layout;
}

Expand Down
18 changes: 12 additions & 6 deletions packages/core/ui/tab-view/index.android.ts
Original file line number Diff line number Diff line change
Expand Up @@ -489,18 +489,24 @@ export class TabView extends TabViewBase {
lp.row = 1;

if (this.androidTabsPosition === 'top') {
nativeView.addRow(new org.nativescript.widgets.ItemSpec(1, org.nativescript.widgets.GridUnitType.auto));
nativeView.addRow(new org.nativescript.widgets.ItemSpec(1, org.nativescript.widgets.GridUnitType.star));

nativeView.addRowsFromJSON(
JSON.stringify([
{ value: 1, type: 0 /* org.nativescript.widgets.GridUnitType.auto */ },
{ value: 1, type: 2 /* org.nativescript.widgets.GridUnitType.star */ },
])
);
viewPager.setLayoutParams(lp);

if (!this.androidSwipeEnabled) {
viewPager.setSwipePageEnabled(false);
}
} else {
nativeView.addRow(new org.nativescript.widgets.ItemSpec(1, org.nativescript.widgets.GridUnitType.star));
nativeView.addRow(new org.nativescript.widgets.ItemSpec(1, org.nativescript.widgets.GridUnitType.auto));

nativeView.addRowsFromJSON(
JSON.stringify([
{ value: 1, type: 2 /* org.nativescript.widgets.GridUnitType.star */ },
{ value: 1, type: 0 /* org.nativescript.widgets.GridUnitType.auto */ },
])
);
tabLayout.setLayoutParams(lp);
viewPager.setSwipePageEnabled(false);
// set completely transparent accent color for tab selected indicator.
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -292,17 +292,22 @@
export class GridLayout extends LayoutBase {
constructor(context: android.content.Context);

public addRow(itemSpec: ItemSpec): void;
public addColumn(itemSpec: ItemSpec): void;

public removeRow(itemSpec: ItemSpec): void;
public removeColumn(itemSpec: ItemSpec): void;
public addRow(value: number, type: org.nativescript.widgets.GridUnitType): void;
public addColumn(value: number, type: org.nativescript.widgets.GridUnitType): void;

public removeRowAt(index: number): void;
public removeColumnAt(index: number): void;

public getColumns(): Array<ItemSpec>;
public getRows(): Array<ItemSpec>;

public clearRows();
public clearColumns();
public reset();

public addRowsFromJSON(value: string);
public addColumnsFromJSON(value: string);
public addRowsAndColumnsFromJSON(rows: string, columns: string);
}

export class FlexboxLayout extends LayoutBase {
Expand Down