diff --git a/apps/automated/src/ui/layouts/grid-layout-tests.ts b/apps/automated/src/ui/layouts/grid-layout-tests.ts index 866d44c64d..2f2edd70a6 100644 --- a/apps/automated/src/ui/layouts/grid-layout-tests.ts +++ b/apps/automated/src/ui/layouts/grid-layout-tests.ts @@ -16,15 +16,31 @@ class RemovalTrackingGridLayout extends GridLayout { public removedRows = 0; public removedCols = 0; + // this trick wont work on android anymore as removeRows + // is done in one call and does not go through _onRowRemoved public _onRowRemoved(itemSpec: ItemSpec, index: number) { this.removedRows++; super._onRowRemoved(itemSpec, index); } + // this trick wont work on android anymore as removeColumns + // is done in one call and does not go through _onColumnRemoved public _onColumnRemoved(itemSpec: ItemSpec, index: number) { this.removedCols++; super._onColumnRemoved(itemSpec, index); } + public removeRows() { + if (__ANDROID__) { + this.removedRows = this.getRows().length; + } + super.removeRows(); + } + public removeColumns() { + if (__ANDROID__) { + this.removedCols = this.getColumns().length; + } + super.removeColumns(); + } } export class GridLayoutTest extends testModule.UITest { @@ -39,7 +55,7 @@ export class GridLayoutTest extends testModule.UITest public test_item_recycling() { helper.nativeView_recycling_test( () => new Button(), - () => new GridLayout() + () => new GridLayout(), ); } @@ -350,6 +366,21 @@ export class GridLayoutTest extends testModule.UITest TKUnit.assertTrue(this.testView.getMeasuredWidth() < platform.Screen.mainScreen.widthPixels); } + private getGridLayoutRowActualLength(view: GridLayout, index: number) { + if (__ANDROID__) { + return Math.round(layoutHelper.dp(view.nativeViewProtected.getRowActualLength(index))); + } else { + return view.getRows()[index].actualLength; + } + } + private getGridLayoutColumnActualLength(view: GridLayout, index: number) { + if (__ANDROID__) { + return Math.round(layoutHelper.dp(view.nativeViewProtected.getColumnActualLength(index))); + } else { + return view.getColumns()[index].actualLength; + } + } + public test_measuredWidth_when_not_stretched_two_columns() { this.testView.horizontalAlignment = 'center'; this.testView.addColumn(new ItemSpec(layoutHelper.dp(80), 'pixel')); @@ -363,9 +394,8 @@ export class GridLayoutTest extends testModule.UITest this.waitUntilTestElementLayoutIsValid(); - var cols = this.testView.getColumns(); - TKUnit.assertAreClose(cols[0].actualLength, Math.round(layoutHelper.dp(80)), DELTA, 'column 0'); - TKUnit.assertAreClose(cols[1].actualLength, Math.round(layoutHelper.dp(20)), DELTA, 'column 1'); + TKUnit.assertAreClose(this.getGridLayoutColumnActualLength(this.testView, 0), Math.round(layoutHelper.dp(80)), DELTA, 'column 0'); + TKUnit.assertAreClose(this.getGridLayoutColumnActualLength(this.testView, 1), Math.round(layoutHelper.dp(20)), DELTA, 'column 1'); TKUnit.assertAreClose(this.testView.getMeasuredWidth(), 100, DELTA, 'measured width'); } @@ -392,9 +422,9 @@ export class GridLayoutTest extends testModule.UITest this.waitUntilTestElementLayoutIsValid(); var cols = this.testView.getColumns(); - TKUnit.assertAreClose(cols[0].actualLength, Math.round(layoutHelper.dp(80)), DELTA, 'column 0'); - TKUnit.assertAreClose(cols[1].actualLength, Math.round(layoutHelper.dp(40)), DELTA, 'column 1'); - TKUnit.assertAreClose(cols[2].actualLength, Math.round(layoutHelper.dp(60)), DELTA, 'column 2'); + TKUnit.assertAreClose(this.getGridLayoutColumnActualLength(this.testView, 0), Math.round(layoutHelper.dp(80)), DELTA, 'column 0'); + TKUnit.assertAreClose(this.getGridLayoutColumnActualLength(this.testView, 1), Math.round(layoutHelper.dp(40)), DELTA, 'column 1'); + TKUnit.assertAreClose(this.getGridLayoutColumnActualLength(this.testView, 2), Math.round(layoutHelper.dp(60)), DELTA, 'column 2'); TKUnit.assertAreClose(this.testView.getMeasuredWidth(), 180, DELTA, 'measured width'); } @@ -484,7 +514,7 @@ export class GridLayoutTest extends testModule.UITest for (var c = 0; c < 4; c++) { var btn = this.testView.getChildAt(i++); if (cols[c].isAbsolute) { - width += layoutHelper.dip(cols[c].actualLength); + width += layoutHelper.dip(this.getGridLayoutColumnActualLength(this.testView, c)); } else { width += btn.getMeasuredWidth(); } @@ -495,7 +525,7 @@ export class GridLayoutTest extends testModule.UITest maxWidth = Math.max(maxWidth, width); if (rows[r].isAbsolute) { - maxHeight += layoutHelper.dip(rows[r].actualLength); + maxHeight += layoutHelper.dip(this.getGridLayoutRowActualLength(this.testView, r)); } else { maxHeight += height; } @@ -510,21 +540,19 @@ export class GridLayoutTest extends testModule.UITest public test_columnsActualWidth_isCorrect() { this.prepareGridLayout(true); - var cols = this.testView.getColumns(); - TKUnit.assertEqual(cols[0].actualLength, Math.round(layoutHelper.dp(50)), 'Star column should be 50px width'); - TKUnit.assertEqual(cols[1].actualLength, Math.round(layoutHelper.dp(100)), '2*Star column should be 100px width'); - TKUnit.assertEqual(cols[2].actualLength, Math.round(layoutHelper.dp(50)), 'Absolute column should be 50px width'); - TKUnit.assertEqual(cols[3].actualLength, Math.round(layoutHelper.dp(100)), 'Auto column should be 100px width'); + TKUnit.assertEqual(this.getGridLayoutColumnActualLength(this.testView, 0), Math.round(layoutHelper.dp(50)), 'Star column should be 50px width'); + TKUnit.assertEqual(this.getGridLayoutColumnActualLength(this.testView, 1), Math.round(layoutHelper.dp(100)), '2*Star column should be 100px width'); + TKUnit.assertEqual(this.getGridLayoutColumnActualLength(this.testView, 2), Math.round(layoutHelper.dp(50)), 'Absolute column should be 50px width'); + TKUnit.assertEqual(this.getGridLayoutColumnActualLength(this.testView, 3), Math.round(layoutHelper.dp(100)), 'Auto column should be 100px width'); } public test_rowsActualHeight_isCorrect() { this.prepareGridLayout(true); - var rows = this.testView.getRows(); - TKUnit.assertEqual(rows[0].actualLength, Math.round(layoutHelper.dp(50)), 'Star row should be 50px width'); - TKUnit.assertEqual(rows[1].actualLength, Math.round(layoutHelper.dp(100)), '2*Star row should be 100px width'); - TKUnit.assertEqual(rows[2].actualLength, Math.round(layoutHelper.dp(50)), 'Absolute row should be 50px width'); - TKUnit.assertEqual(rows[3].actualLength, Math.round(layoutHelper.dp(100)), 'Auto row should be 100px width'); + TKUnit.assertEqual(this.getGridLayoutRowActualLength(this.testView, 0), Math.round(layoutHelper.dp(50)), 'Star row should be 50px width'); + TKUnit.assertEqual(this.getGridLayoutRowActualLength(this.testView, 1), Math.round(layoutHelper.dp(100)), '2*Star row should be 100px width'); + TKUnit.assertEqual(this.getGridLayoutRowActualLength(this.testView, 2), Math.round(layoutHelper.dp(50)), 'Absolute row should be 50px width'); + TKUnit.assertEqual(this.getGridLayoutRowActualLength(this.testView, 3), Math.round(layoutHelper.dp(100)), 'Auto row should be 100px width'); } public test_Measure_and_Layout_Children_withCorrect_size() { @@ -575,12 +603,10 @@ export class GridLayoutTest extends testModule.UITest this.waitUntilTestElementLayoutIsValid(); - var cols = this.testView.getColumns(); - - TKUnit.assertAreClose(cols[0].actualLength, Math.round(layoutHelper.dp(28)), DELTA, 'Column[0] actual length should be 28'); - TKUnit.assertAreClose(cols[1].actualLength, Math.round(layoutHelper.dp(27)), DELTA, 'Column[1] actual length should be 27'); - TKUnit.assertAreClose(cols[2].actualLength, Math.round(layoutHelper.dp(28)), DELTA, 'Column[2] actual length should be 28'); - TKUnit.assertAreClose(cols[3].actualLength, Math.round(layoutHelper.dp(27)), DELTA, 'Column[3] actual length should be 27'); + TKUnit.assertAreClose(this.getGridLayoutColumnActualLength(this.testView, 0), Math.round(layoutHelper.dp(28)), DELTA, 'Column[0] actual length should be 28'); + TKUnit.assertAreClose(this.getGridLayoutColumnActualLength(this.testView, 1), Math.round(layoutHelper.dp(27)), DELTA, 'Column[1] actual length should be 27'); + TKUnit.assertAreClose(this.getGridLayoutColumnActualLength(this.testView, 2), Math.round(layoutHelper.dp(28)), DELTA, 'Column[2] actual length should be 28'); + TKUnit.assertAreClose(this.getGridLayoutColumnActualLength(this.testView, 3), Math.round(layoutHelper.dp(27)), DELTA, 'Column[3] actual length should be 27'); } public test_margins_and_verticalAlignment_center() { @@ -799,15 +825,13 @@ export class GridLayoutTest extends testModule.UITest GridLayout.setRowSpan(btn, 3); this.waitUntilTestElementLayoutIsValid(); - var cols = grid.getColumns(); - TKUnit.assertAreClose(cols[0].actualLength, layoutHelper.dp(33), DELTA, 'Column[0] actual length should be 67'); - TKUnit.assertAreClose(cols[1].actualLength, layoutHelper.dp(100), DELTA, 'Column[1] actual length should be 100'); - TKUnit.assertAreClose(cols[2].actualLength, layoutHelper.dp(67), DELTA, 'Column[2] actual length should be 133'); + TKUnit.assertAreClose(this.getGridLayoutColumnActualLength(grid, 0), layoutHelper.dp(33), DELTA, 'Column[0] actual length should be 67'); + TKUnit.assertAreClose(this.getGridLayoutColumnActualLength(grid, 1), layoutHelper.dp(100), DELTA, 'Column[1] actual length should be 100'); + TKUnit.assertAreClose(this.getGridLayoutColumnActualLength(grid, 2), layoutHelper.dp(67), DELTA, 'Column[2] actual length should be 133'); - var rows = grid.getRows(); - TKUnit.assertAreClose(rows[0].actualLength, layoutHelper.dp(67), DELTA, 'Row[0] actual length should be 133'); - TKUnit.assertAreClose(rows[1].actualLength, layoutHelper.dp(100), DELTA, 'Row[1] actual length should be 100'); - TKUnit.assertAreClose(rows[2].actualLength, layoutHelper.dp(133), DELTA, 'Row[2] actual length should be 267'); + TKUnit.assertAreClose(this.getGridLayoutRowActualLength(grid, 0), layoutHelper.dp(67), DELTA, 'Row[0] actual length should be 133'); + TKUnit.assertAreClose(this.getGridLayoutRowActualLength(grid, 1), layoutHelper.dp(100), DELTA, 'Row[1] actual length should be 100'); + TKUnit.assertAreClose(this.getGridLayoutRowActualLength(grid, 2), layoutHelper.dp(133), DELTA, 'Row[2] actual length should be 267'); } } diff --git a/packages/core/platforms/android/widgets-release.aar b/packages/core/platforms/android/widgets-release.aar index e5d42027ca..c9f813d5ed 100644 Binary files a/packages/core/platforms/android/widgets-release.aar and b/packages/core/platforms/android/widgets-release.aar differ diff --git a/packages/core/ui/layouts/grid-layout/grid-layout-common.ts b/packages/core/ui/layouts/grid-layout/grid-layout-common.ts index 0e18444df9..8a68e2de97 100644 --- a/packages/core/ui/layouts/grid-layout/grid-layout-common.ts +++ b/packages/core/ui/layouts/grid-layout/grid-layout-common.ts @@ -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(); @@ -147,8 +150,8 @@ export class ItemSpec extends Observable implements ItemSpecDefinition { @CSSType('GridLayout') export class GridLayoutBase extends LayoutBase implements GridLayoutDefinition { - private _rows: Array = new Array(); - private _cols: Array = new Array(); + protected _rows: Array = new Array(); + protected _cols: Array = new Array(); public static getColumn(element: View): number { return validateArgs(element).col; @@ -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); @@ -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); } } diff --git a/packages/core/ui/layouts/grid-layout/index.android.ts b/packages/core/ui/layouts/grid-layout/index.android.ts index 737e444c50..25727c55de 100644 --- a/packages/core/ui/layouts/grid-layout/index.android.ts +++ b/packages/core/ui/layouts/grid-layout/index.android.ts @@ -20,32 +20,26 @@ View.prototype[columnProperty.setNative] = makeNativeSetter((lp, value) View.prototype[rowSpanProperty.setNative] = makeNativeSetter((lp, value) => (lp.rowSpan = value)); View.prototype[columnSpanProperty.setNative] = makeNativeSetter((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 { @@ -58,54 +52,86 @@ 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 = this.rowsInternal[i]; - this._onRowRemoved(itemSpec, i); - } - - for (let i = this.columnsInternal.length; i--; i >= 0) { - const 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); + this.nativeViewProtected.addRowsFromJSON(JSON.stringify([itemSpec.toJSON()])); + } + } + + public addRows(itemSpecs: ItemSpec[]) { + const 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[]) { + const 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); + this.nativeViewProtected.addColumnsFromJSON(JSON.stringify([itemSpec.toJSON()])); + } + } + + 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); } diff --git a/packages/core/ui/page/index.android.ts b/packages/core/ui/page/index.android.ts index 8adff9fd5d..d8ffc600de 100644 --- a/packages/core/ui/page/index.android.ts +++ b/packages/core/ui/page/index.android.ts @@ -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; } diff --git a/packages/core/ui/tab-view/index.android.ts b/packages/core/ui/tab-view/index.android.ts index b15dd0ce8e..cc8405311a 100644 --- a/packages/core/ui/tab-view/index.android.ts +++ b/packages/core/ui/tab-view/index.android.ts @@ -490,18 +490,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. diff --git a/packages/types-android/src/lib/android/org.nativescript.widgets.d.ts b/packages/types-android/src/lib/android/org.nativescript.widgets.d.ts index 416fde66a4..e0d9e7deb9 100644 --- a/packages/types-android/src/lib/android/org.nativescript.widgets.d.ts +++ b/packages/types-android/src/lib/android/org.nativescript.widgets.d.ts @@ -292,17 +292,24 @@ 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; public getRows(): Array; + + public clearRows(); + public clearColumns(); + public reset(); + + public addRowsFromJSON(value: string); + public addColumnsFromJSON(value: string); + public addRowsAndColumnsFromJSON(rows: string, columns: string); + public getRowActualLength(index: number): number; // for testing purpose + public getColumnActualLength(index: number): number; // for testing purpose } export class FlexboxLayout extends LayoutBase { diff --git a/packages/ui-mobile-base/android/widgets/src/main/java/org/nativescript/widgets/GridLayout.java b/packages/ui-mobile-base/android/widgets/src/main/java/org/nativescript/widgets/GridLayout.java index 258f7c5e4d..f0571cdf02 100644 --- a/packages/ui-mobile-base/android/widgets/src/main/java/org/nativescript/widgets/GridLayout.java +++ b/packages/ui-mobile-base/android/widgets/src/main/java/org/nativescript/widgets/GridLayout.java @@ -1,11 +1,16 @@ package org.nativescript.widgets; import android.content.Context; +import android.util.Log; import android.view.Gravity; import android.view.View; import android.view.View.MeasureSpec; import android.util.AttributeSet; +import org.json.JSONArray; +import org.json.JSONException; +import org.json.JSONObject; + import java.util.ArrayList; import java.util.HashMap; @@ -13,6 +18,7 @@ * @author hhristov */ public class GridLayout extends LayoutBase { + protected final static String TAG = "GridLayout"; private final MeasureHelper helper = new MeasureHelper(this); @@ -31,6 +37,14 @@ public GridLayout(Context context, AttributeSet attrs) { public GridLayout(Context context, AttributeSet attrs, int defStyleAttr) { super(context, attrs, defStyleAttr); } + public GridLayout(Context context, String rows) { + this(context); + this.addRowsFromJSON(rows); + } + public GridLayout(Context context, String rows, String columns) { + this(context, rows); + this.addColumnsFromJSON(rows); + } private static void validateItemSpec(ItemSpec itemSpec) { if (itemSpec == null) { @@ -53,6 +67,10 @@ public void addRow(ItemSpec itemSpec) { this.requestLayout(); } + public void addRow(int value, GridUnitType type) { + addRow(new ItemSpec(value, type)); + } + public void addColumn(ItemSpec itemSpec) { validateItemSpec(itemSpec); itemSpec.owner = this; @@ -64,18 +82,45 @@ public void addColumn(ItemSpec itemSpec) { this.requestLayout(); } - public void removeColumn(ItemSpec itemSpec) { - if (itemSpec == null) { - throw new Error("itemSpec is null."); - } + public void addColumn(int value, GridUnitType type) { + addColumn(new ItemSpec(value, type)); + } - int index = this._cols.indexOf(itemSpec); - if (itemSpec.owner != this || index < 0) { - throw new Error("itemSpec is not child of this GridLayout"); + public void addRowsFromJSON(String value) { + try { + if (value == null) { + return; + } + JSONArray rows = new JSONArray(value); + for (int i = 0; i < rows.length() ; i++) { + JSONObject row = rows.getJSONObject(i); + addRow(row.getInt("value"), GridUnitType.values()[row.getInt("type")]); + } + } catch (JSONException exception) { + Log.e(TAG, "Caught JSONException..."); + exception.printStackTrace(); + } + } + public void addColumnsFromJSON(String value) { + try { + if (value == null) { + return; + } + JSONArray columns = new JSONArray(value); + for (int i = 0; i < columns.length() ; i++) { + JSONObject column = columns.getJSONObject(i); + addColumn(column.getInt("value"), GridUnitType.values()[column.getInt("type")]); + } + } catch (JSONException exception) { + Log.e(TAG, "Caught JSONException..."); + exception.printStackTrace(); } - - this.removeColumnAt(index); } + public void addRowsAndColumnsFromJSON(String rowsString, String jsonString) { + addRowsFromJSON(rowsString); + addColumnsFromJSON(jsonString); + } + public void removeColumnAt(int index) { this._cols.remove(index); @@ -84,19 +129,6 @@ public void removeColumnAt(int index) { this.requestLayout(); } - public void removeRow(ItemSpec itemSpec) { - if (itemSpec == null) { - throw new Error("itemSpec is null."); - } - - int index = this._rows.indexOf(itemSpec); - if (itemSpec.owner != this || index < 0) { - throw new Error("itemSpec is not child of this GridLayout"); - } - - this.removeRowAt(index); - } - public void removeRowAt(int index) { this._rows.remove(index); this.helper.rows.get(index).children.clear(); @@ -116,6 +148,30 @@ public ItemSpec[] getRows() { return copy; } + public void reset() { + clearRows(); + clearColumns(); + } + + + public void clearRows() { + this._rows.clear(); + for (int i = 0; i < this.helper.rows.size(); i++) { + this.helper.rows.get(i).children.clear(); + } + this.helper.rows.clear(); + this.requestLayout(); + } + + public void clearColumns() { + this._cols.clear(); + for (int i = 0; i < this.helper.columns.size(); i++) { + this.helper.columns.get(i).children.clear(); + } + this.helper.columns.clear(); + this.requestLayout(); + } + @Override public void addView(View child) { super.addView(child); @@ -235,6 +291,16 @@ private void removeFromMap(View child) { } } + /* only used for N tests */ + public float getRowActualLength(int index) { + return this.helper.rows.get(index).rowOrColumn._actualLength; + } + + /* only used for N tests */ + public float getColumnActualLength(int index) { + return this.helper.columns.get(index).rowOrColumn._actualLength; + } + @Override protected void onMeasure(int widthMeasureSpec, int heightMeasureSpec) { CommonLayoutParams.adjustChildrenLayoutParams(this, widthMeasureSpec, heightMeasureSpec); @@ -823,7 +889,6 @@ public void measure() { this.measureChild(measureSpec, false); } } - // try fix stars! boolean fixColumns = canFix(this.columns); boolean fixRows = canFix(this.rows);