diff --git a/apps/app/ui-tests-app/app.ts b/apps/app/ui-tests-app/app.ts index f06fff45e..81b432f09 100644 --- a/apps/app/ui-tests-app/app.ts +++ b/apps/app/ui-tests-app/app.ts @@ -69,13 +69,10 @@ application.on(application.lowMemoryEvent, function (args: application.Applicati }); application.on(application.uncaughtErrorEvent, function (args: application.UnhandledErrorEventData) { - if (args.android) { - // For Android applications, args.android is NativeScriptError. - console.log("### NativeScriptError: " + args.android); - } else if (args.ios) { - // For iOS applications, args.ios is NativeScriptError. - console.log("### NativeScriptError: " + args.ios); - } + console.log("### NativeScriptError: " + args.error); + console.log("### nativeException: " + (args.error).nativeException); + console.log("### stackTace: " + (args.error).stackTrace); + console.log("### stack: " + args.error.stack); }); application.setCssFileName("ui-tests-app/app.css"); diff --git a/apps/app/ui-tests-app/mainPage.ts b/apps/app/ui-tests-app/mainPage.ts index e27ab75ef..296a51e2e 100644 --- a/apps/app/ui-tests-app/mainPage.ts +++ b/apps/app/ui-tests-app/mainPage.ts @@ -15,9 +15,7 @@ var oldExamples: Map = new Map(); export function pageLoaded(args: EventData) { let page = args.object; - let view = require("ui/core/view"); - let wrapLayout = view.getViewById(page, "wrapLayoutWithExamples"); - + let wrapLayout = page.getViewById("wrapLayoutWithExamples"); examples.set("action-bar", "action-bar/main-page"); examples.set("bindings", "bindings/main-page"); examples.set("css", "css/main-page"); @@ -46,8 +44,8 @@ export function pageLoaded(args: EventData) { let viewModel = new MainPageViewModel(wrapLayout, examples); page.bindingContext = viewModel; - var parent = page.getViewById('parentLayout'); - var searchBar = page.getViewById('textView'); + var parent = page.getViewById('parentLayout'); + var searchBar = page.getViewById('textView'); if (parent.android) { parent.android.setFocusableInTouchMode(true); @@ -57,7 +55,7 @@ export function pageLoaded(args: EventData) { refresh(); } - + // should be removes export function refresh() { oldExamples.set("actStyle", "action-bar/all"); @@ -217,7 +215,7 @@ export class MainPageViewModel extends observable.Observable { } btn.style.color = new colorModule.Color(this.colors[count++ % 3]); - btn.on(buttonModule.Button.tapEvent, function(eventData) { + btn.on(buttonModule.Button.tapEvent, function (eventData) { let text = btn.text; this.loadExample(text); }, this); diff --git a/tests/app/TKUnit.ts b/tests/app/TKUnit.ts index 0f80de5d0..1da82183f 100644 --- a/tests/app/TKUnit.ts +++ b/tests/app/TKUnit.ts @@ -366,13 +366,13 @@ export var wait = function (seconds: number) { }, seconds, false); }; -export var waitUntilReady = function (isReady: () => boolean, timeoutSec: number = 20, shouldThrow: boolean = true) { +export var waitUntilReady = function (isReady: () => boolean, timeoutSec: number = 3, shouldThrow: boolean = true) { if (!isReady) { return; } if (Application.ios) { - var waitTime = 20 / 1000; + const waitTime = 20 / 1000; var totalWaitTime = 0; while (true) { utils.ios.getter(NSRunLoop, NSRunLoop.currentRunLoop).runUntilDate(NSDate.dateWithTimeIntervalSinceNow(waitTime)); diff --git a/tests/app/testRunner.ts b/tests/app/testRunner.ts index 9912756cd..6f8167b1e 100644 --- a/tests/app/testRunner.ts +++ b/tests/app/testRunner.ts @@ -344,6 +344,7 @@ function printRunTestStats() { page.style.backgroundColor = unsetValue; page.content = stack; messageContainer.focus(); + page.style.fontSize = 11; if (page.android) { setTimeout(() => messageContainer.dismissSoftInput()); } diff --git a/tests/app/ui/action-bar/action-bar-tests.android.ts b/tests/app/ui/action-bar/action-bar-tests.android.ts index 667159259..0f6a23bda 100644 --- a/tests/app/ui/action-bar/action-bar-tests.android.ts +++ b/tests/app/ui/action-bar/action-bar-tests.android.ts @@ -11,7 +11,7 @@ export function test_actionItem_visibility() { actionItem.text = "Test"; const page = actionTestsCommon.createPageAndNavigate(); page.actionBar.actionItems.addItem(actionItem); - const toolbar = (page.actionBar)._toolbar; + const toolbar = page.actionBar.nativeView; const menu = toolbar.getMenu(); TKUnit.assertTrue(menu.hasVisibleItems(), "Visibility does not work"); @@ -25,7 +25,7 @@ export function test_navigationButton_visibility() { const page = actionTestsCommon.createPageAndNavigate(); page.actionBar.navigationButton = actionItem; - const toolbar = (page.actionBar)._toolbar; + const toolbar = page.actionBar.nativeView; TKUnit.assertNotNull(toolbar.getNavigationIcon(), "Visibility does not work"); actionItem.visibility = Visibility.collapse; @@ -72,4 +72,4 @@ export function test_add_actionItem_with_actionView_propagates_context() { TKUnit.assertNull(actionButton._context, "Action button context should be null before added"); actionItem.actionView = actionButton; TKUnit.assertNotNull(actionButton._context, "Action button context should not be null after add"); -} +} \ No newline at end of file diff --git a/tests/app/ui/layouts/common-layout-tests.ts b/tests/app/ui/layouts/common-layout-tests.ts index 521afdd3e..be81773fd 100644 --- a/tests/app/ui/layouts/common-layout-tests.ts +++ b/tests/app/ui/layouts/common-layout-tests.ts @@ -30,7 +30,7 @@ export function percent_support_nativeLayoutParams_are_correct(test: testModule. test.waitUntilTestElementLayoutIsValid(); - let lp = getNativeLayoutParams(btn._nativeView); + let lp = getNativeLayoutParams(btn.nativeView); TKUnit.assertEqual(lp.width, 100, "width"); TKUnit.assertEqual(lp.widthPercent, -1, "widthPercent"); TKUnit.assertEqual(lp.height, 100, "height"); diff --git a/tests/app/ui/layouts/flexbox-layout-tests.ts b/tests/app/ui/layouts/flexbox-layout-tests.ts index c1f4cbcb6..f996411a1 100644 --- a/tests/app/ui/layouts/flexbox-layout-tests.ts +++ b/tests/app/ui/layouts/flexbox-layout-tests.ts @@ -174,6 +174,7 @@ export function testFlexboxPage() { function view(id: string) { return page.getViewById(id); } + TKUnit.waitUntilReady(() => page.isLayoutValid); isLeftOf(view("six"), view("one")); isAbove(view("one"), view("scrollview")); isAbove(view("title"), view("firstlabel")); diff --git a/tests/app/ui/layouts/layout-helper.android.ts b/tests/app/ui/layouts/layout-helper.android.ts index b8d27b058..ba99ae74c 100644 --- a/tests/app/ui/layouts/layout-helper.android.ts +++ b/tests/app/ui/layouts/layout-helper.android.ts @@ -1,6 +1,6 @@ -import {Button} from "tns-core-modules/ui/button"; -import {StackLayout} from "tns-core-modules/ui/layouts/stack-layout"; -import {GridLayout} from "tns-core-modules/ui/layouts/grid-layout"; +import { Button } from "tns-core-modules/ui/button"; +import { StackLayout } from "tns-core-modules/ui/layouts/stack-layout"; +import { GridLayout } from "tns-core-modules/ui/layouts/grid-layout"; import * as utils from "tns-core-modules/utils/utils"; import * as TKUnit from "../../TKUnit"; @@ -8,12 +8,9 @@ import * as def from "./layout-helper"; var DELTA = 0.1; -class NativeButton extends android.widget.Button { - private owner: def.MeasuredView; - - constructor(context: android.content.Context, owner: def.MeasuredView) { +class NativeButton extends android.widget.Button { + constructor(context: android.content.Context, public owner: def.MeasuredView) { super(context); - this.owner = owner; return global.__native(this); } @@ -31,11 +28,8 @@ class NativeButton extends android.widget.Button { } class NativeStackLayout extends org.nativescript.widgets.StackLayout { - private owner: def.MeasuredView; - - constructor(context: android.content.Context, owner: def.MeasuredView) { + constructor(context: android.content.Context, public owner: def.MeasuredView) { super(context); - this.owner = owner; return global.__native(this); } @@ -53,11 +47,8 @@ class NativeStackLayout extends org.nativescript.widgets.StackLayout { } class NativeGridLayout extends org.nativescript.widgets.GridLayout { - private owner: def.MeasuredView; - - constructor(context: android.content.Context, owner: def.MeasuredView) { + constructor(context: android.content.Context, public owner: def.MeasuredView) { super(context); - this.owner = owner; return global.__native(this); } @@ -75,19 +66,18 @@ class NativeGridLayout extends org.nativescript.widgets.GridLayout { } export class MyButton extends Button implements def.MyButton { - private _layout: android.view.View; - - get android(): android.view.View { - return this._layout; - } - - get _nativeView(): android.view.View { - return this._layout; - } + nativeView: NativeButton; public _createNativeView() { - this._layout = new NativeButton(this._context, this); - return this._layout; + return new NativeButton(this._context, this); + } + + public _initNativeView(): void { + this.nativeView.owner = this; + } + + public _disposeNativeView() { + this.nativeView.owner = undefined; } public measureCount: number = 0; @@ -112,36 +102,35 @@ export class MyButton extends Button implements def.MyButton { } get layoutWidth(): number { - return this._layout.getWidth(); + return this.nativeView.getWidth(); } get layoutHeight(): number { - return this._layout.getHeight(); + return this.nativeView.getHeight(); } get layoutLeft(): number { - return this._layout.getLeft(); + return this.nativeView.getLeft(); } get layoutTop(): number { - return this._layout.getTop(); + return this.nativeView.getTop(); } } export class MyStackLayout extends StackLayout implements def.MyStackLayout { - private _layout: android.view.View; - - get android(): android.view.View { - return this._layout; - } - - get _nativeView(): android.view.View { - return this._layout; - } + nativeView: NativeStackLayout; public _createNativeView() { - this._layout = new NativeStackLayout(this._context, this); - return this._layout; + return new NativeStackLayout(this._context, this); + } + + public _initNativeView(): void { + this.nativeView.owner = this; + } + + public _disposeNativeView() { + this.nativeView.owner = undefined; } public measureCount: number = 0; @@ -166,36 +155,35 @@ export class MyStackLayout extends StackLayout implements def.MyStackLayout { } get layoutWidth(): number { - return this._layout.getWidth(); + return this.nativeView.getWidth(); } get layoutHeight(): number { - return this._layout.getHeight(); + return this.nativeView.getHeight(); } get layoutLeft(): number { - return this._layout.getLeft(); + return this.nativeView.getLeft(); } get layoutTop(): number { - return this._layout.getTop(); + return this.nativeView.getTop(); } } export class MyGridLayout extends GridLayout implements def.MyGridLayout { - private _layout: android.view.View; - - get android(): android.view.View { - return this._layout; - } - - get _nativeView(): android.view.View { - return this._layout; - } + nativeView: NativeGridLayout; public _createNativeView() { - this._layout = new NativeGridLayout(this._context, this); - return this._layout; + return new NativeGridLayout(this._context, this); + } + + public _initNativeView(): void { + this.nativeView.owner = this; + } + + public _disposeNativeView() { + this.nativeView.owner = undefined; } public measureCount: number = 0; @@ -220,19 +208,19 @@ export class MyGridLayout extends GridLayout implements def.MyGridLayout { } get layoutWidth(): number { - return this._layout.getWidth(); + return this.nativeView.getWidth(); } get layoutHeight(): number { - return this._layout.getHeight(); + return this.nativeView.getHeight(); } get layoutLeft(): number { - return this._layout.getLeft(); + return this.nativeView.getLeft(); } get layoutTop(): number { - return this._layout.getTop(); + return this.nativeView.getTop(); } } @@ -258,4 +246,4 @@ export function dp(value: number): number { export function dip(value: number): number { return utils.layout.toDevicePixels(value); -} +} \ No newline at end of file diff --git a/tests/app/ui/list-picker/list-picker-tests-native.android.ts b/tests/app/ui/list-picker/list-picker-tests-native.android.ts index 148c977eb..aee35e9aa 100644 --- a/tests/app/ui/list-picker/list-picker-tests-native.android.ts +++ b/tests/app/ui/list-picker/list-picker-tests-native.android.ts @@ -1,7 +1,7 @@ import * as listPickerModule from "tns-core-modules/ui/list-picker"; export function getNativeItemsCount(listPicker: listPickerModule.ListPicker): number { - var maxValue = listPicker.android.getMaxValue(); + var maxValue = listPicker.nativeView.getMaxValue(); if (listPicker.items.length === 0 && maxValue === 0) { return 0; @@ -12,6 +12,6 @@ export function getNativeItemsCount(listPicker: listPickerModule.ListPicker): nu export function selectNativeItem(listPicker: listPickerModule.ListPicker, index: number): void { var oldIndex = listPicker.selectedIndex; - listPicker.android.setValue(index); - (listPicker)._valueChangedListener.onValueChange(listPicker.android, oldIndex, index); -} + listPicker.nativeView.setValue(index); + listPicker.nativeView.valueChangedListener.onValueChange(listPicker.android, oldIndex, index); +} \ No newline at end of file diff --git a/tests/app/ui/list-picker/list-picker-tests.ts b/tests/app/ui/list-picker/list-picker-tests.ts index a1a4c2c3d..51ce8a08b 100644 --- a/tests/app/ui/list-picker/list-picker-tests.ts +++ b/tests/app/ui/list-picker/list-picker-tests.ts @@ -170,7 +170,7 @@ export var testWhenSelectingAnItemNativelySelectedIndexIsUpdatedProperly = funct TKUnit.waitUntilReady(() => listPicker.selectedIndex === 1); let actualValue = listPicker.selectedIndex; - TKUnit.assert(actualValue === expectedValue, "Actual: " + actualValue + "; Expected: " + expectedValue); + TKUnit.assertEqual(actualValue, expectedValue); } export var test_Android_MaxValueIsOneLessThanItemsCount = function () { @@ -182,8 +182,8 @@ export var test_Android_MaxValueIsOneLessThanItemsCount = function () { var listPicker = views[0]; listPicker.items = ["One", "Two", "Three"]; var expectedValue = listPicker.items.length - 1; - var actualValue = (listPicker).android.getMaxValue(); - TKUnit.assert(actualValue === expectedValue, "Actual: " + actualValue + "; Expected: " + expectedValue); + var actualValue = listPicker.nativeView.getMaxValue(); + TKUnit.assertEqual(actualValue, expectedValue); }); } @@ -195,8 +195,8 @@ export var test_Android_WhenItemsAreEmptyNativeControlDoesNotShowZero = function helper.buildUIAndRunTest(_createListPicker(), function (views: Array) { var listPicker = views[0]; var expectedValue = " "; - var actualValue = (listPicker)._editText.getText().toString(); - TKUnit.assert(actualValue === expectedValue, "Actual: " + actualValue + "; Expected: " + expectedValue); + var actualValue = listPicker.nativeView.editText.getText().toString(); + TKUnit.assertEqual(actualValue, expectedValue); }); } @@ -209,8 +209,8 @@ export var test_Android_WhenBoundToSingleElementArrayEditTextIsUpdatedProperly = var listPicker = views[0]; listPicker.items = ["One"]; var expectedValue = "One"; - var actualValue = (listPicker)._editText.getText().toString(); - TKUnit.assert(actualValue === expectedValue, "Actual: " + actualValue + "; Expected: " + expectedValue); + var actualValue = listPicker.nativeView.editText.getText().toString(); + TKUnit.assertEqual(actualValue, expectedValue); }); } @@ -224,7 +224,7 @@ export var test_Android_WhenSelectedIndexChangesEditTextIsUpdatedProperly = func listPicker.items = ["One", "Two"]; listPicker.selectedIndex = 1; var expectedValue = "Two"; - var actualValue = (listPicker)._editText.getText().toString(); - TKUnit.assert(actualValue === expectedValue, "Actual: " + actualValue + "; Expected: " + expectedValue); + var actualValue = listPicker.nativeView.editText.getText().toString(); + TKUnit.assertEqual(actualValue, expectedValue); }); -} +} \ No newline at end of file diff --git a/tests/app/ui/page/page-tests-common.ts b/tests/app/ui/page/page-tests-common.ts index 3d09fe4b9..14987c2af 100644 --- a/tests/app/ui/page/page-tests-common.ts +++ b/tests/app/ui/page/page-tests-common.ts @@ -259,6 +259,8 @@ export function test_NavigateTo_WithBindingContext() { } export function test_FrameBackStack_WhenNavigatingForwardAndBack() { + + helper.navigate(() => new Page()); let testPage: Page; let pageFactory = function () { testPage = new Page(); diff --git a/tests/app/ui/page/page-tests.android.ts b/tests/app/ui/page/page-tests.android.ts index a16d22419..e24bcbaca 100644 --- a/tests/app/ui/page/page-tests.android.ts +++ b/tests/app/ui/page/page-tests.android.ts @@ -43,19 +43,19 @@ export function test_NavigateToNewPage_WithAndroidCache() { androidFrame.cachePagesOnNavigate = cachingBefore; } - TKUnit.assert(testPage.parent === undefined, "Page.parent should become undefined after navigating back"); - TKUnit.assert(testPage.isLoaded === false, "Page.isLoaded should become false after navigating back"); - TKUnit.assert(testPage.frame === undefined, "Page.frame should become undefined after navigating back"); - TKUnit.assert(testPage._isAddedToNativeVisualTree === false, "Page._isAddedToNativeVisualTree should become false after navigating back"); + TKUnit.assertNull(testPage.parent, "Page.parent should become undefined after navigating back"); + TKUnit.assertFalse(testPage.isLoaded, "Page.isLoaded should become false after navigating back"); + TKUnit.assertNull(testPage.frame, "Page.frame should become undefined after navigating back"); + TKUnit.assertFalse(testPage._isAddedToNativeVisualTree, "Page._isAddedToNativeVisualTree should become false after navigating back"); - TKUnit.assert(label._context === null, "InnerControl._context should not be set after navigate back."); - TKUnit.assert(label.android === undefined, "InnerControl.android should not be set after navigate back."); - TKUnit.assert(label._nativeView === undefined, "InnerControl._nativeView hould not be set after navigate back."); - TKUnit.assert(label.isLoaded === false, "InnerControl.isLoaded should become false after navigating back"); - TKUnit.assert(label._isAddedToNativeVisualTree === false, "InnerControl._isAddedToNativeVisualTree should not be true after navigating back"); + TKUnit.assertNull(label._context, "InnerControl._context should not be set after navigate back."); + TKUnit.assertNull(label.android, "InnerControl.android should not be set after navigate back."); + TKUnit.assertNull(label.nativeView, "InnerControl.nativeView hould not be set after navigate back."); + TKUnit.assertFalse(label.isLoaded, "InnerControl.isLoaded should become false after navigating back"); + TKUnit.assertFalse(label._isAddedToNativeVisualTree, "InnerControl._isAddedToNativeVisualTree should not be true after navigating back"); } -export var test_NavigateToNewPage_InnerControl = function () { +export function test_NavigateToNewPage_InnerControl() { var testPage: PageModule.Page; var pageFactory = function () { testPage = new PageModule.Page(); @@ -69,11 +69,11 @@ export var test_NavigateToNewPage_InnerControl = function () { var label = testPage.content; - TKUnit.assert(label._context === null, "InnerControl._context should be undefined after navigate back."); - TKUnit.assert(label.android === undefined, "InnerControl.android should be undefined after navigate back."); - TKUnit.assert(label._nativeView === undefined, "InnerControl._nativeView should be undefined after navigate back."); - TKUnit.assert(label.isLoaded === false, "InnerControl.isLoaded should become false after navigating back"); - TKUnit.assert(label._isAddedToNativeVisualTree === false, "InnerControl._isAddedToNativeVisualTree should become false after navigating back"); + TKUnit.assertNull(label._context, "InnerControl._context should be undefined after navigate back."); + TKUnit.assertNull(label.android, "InnerControl.android should be undefined after navigate back."); + TKUnit.assertNull(label.nativeView, "InnerControl.nativeView should be undefined after navigate back."); + TKUnit.assertFalse(label.isLoaded, "InnerControl.isLoaded should become false after navigating back"); + TKUnit.assertFalse(label._isAddedToNativeVisualTree, "InnerControl._isAddedToNativeVisualTree should become false after navigating back"); } export var test_ChangePageCaching_AfterNavigated_Throws = function () { diff --git a/tests/app/ui/page/page-tests.ios.ts b/tests/app/ui/page/page-tests.ios.ts index 2293a5793..694ced7c9 100644 --- a/tests/app/ui/page/page-tests.ios.ts +++ b/tests/app/ui/page/page-tests.ios.ts @@ -23,8 +23,8 @@ export function test_NavigateToNewPage_InnerControl() { helper.goBack(); - TKUnit.assertEqual(label._context, null, "label._context should be undefined after navigate back."); - TKUnit.assertEqual(label.android, undefined, "label.android should be undefined after navigate back."); + TKUnit.assertNull(label._context, "label._context should be undefined after navigate back."); + TKUnit.assertNull(label.android, "label.android should be undefined after navigate back."); TKUnit.assertFalse(label.isLoaded, "label.isLoaded should become false after navigating back"); } diff --git a/tests/app/ui/segmented-bar/segmented-bar-tests-native.ios.ts b/tests/app/ui/segmented-bar/segmented-bar-tests-native.ios.ts index 3e6dbd30c..25a6c265b 100644 --- a/tests/app/ui/segmented-bar/segmented-bar-tests-native.ios.ts +++ b/tests/app/ui/segmented-bar/segmented-bar-tests-native.ios.ts @@ -1,19 +1,19 @@ import * as segmentedBarModule from "tns-core-modules/ui/segmented-bar"; export function getNativeItemsCount(bar: segmentedBarModule.SegmentedBar): number { - return (bar.ios).numberOfSegments; + return (bar.nativeView).numberOfSegments; } export function checkNativeItemsTextColor(bar: segmentedBarModule.SegmentedBar): boolean { var isValid = true; - var attrs = (bar.ios).titleTextAttributesForState(UIControlState.Normal); + var attrs = (bar.nativeView).titleTextAttributesForState(UIControlState.Normal); isValid = bar.color && attrs && attrs.valueForKey(NSForegroundColorAttributeName) === bar.color.ios; return isValid; } export function setNativeSelectedIndex(bar: segmentedBarModule.SegmentedBar, index: number): void { - bar.ios.selectedSegmentIndex = index; - (bar.ios).sendActionsForControlEvents(UIControlEvents.ValueChanged); + (bar.nativeView).selectedSegmentIndex = index; + (bar.nativeView).sendActionsForControlEvents(UIControlEvents.ValueChanged); } diff --git a/tests/app/ui/switch/switch-tests.ts b/tests/app/ui/switch/switch-tests.ts index 2c631af5a..1bff068f4 100644 --- a/tests/app/ui/switch/switch-tests.ts +++ b/tests/app/ui/switch/switch-tests.ts @@ -13,10 +13,10 @@ import * as switchModule from "tns-core-modules/ui/switch"; // >> article-binding-switch-property function pageLoaded(args) { - var page = args.object; - var obj = new observable.Observable(); - obj.set("someProperty", false); - page.bindingContext = obj; + var page = args.object; + var obj = new observable.Observable(); + obj.set("someProperty", false); + page.bindingContext = obj; } exports.pageLoaded = pageLoaded; // << article-binding-switch-property @@ -137,17 +137,18 @@ export function test_binding_value_to_model() { } function getNativeValue(mySwitch: switchModule.Switch): boolean { - if (mySwitch.android) { - return mySwitch.android.isChecked(); - } - else if (mySwitch.ios) { + if (platform.isAndroid) { + const nativeView: android.widget.Switch = mySwitch.nativeView; + return nativeView.isChecked(); + } else if (mySwitch.ios) { return mySwitch.ios.on; } } function setNativeValue(mySwitch: switchModule.Switch, value: boolean) { - if (mySwitch.android) { - mySwitch.android.setChecked(value); + if (platform.isAndroid) { + const nativeView: android.widget.Switch = mySwitch.nativeView; + nativeView.setChecked(value); } else if (mySwitch.ios) { mySwitch.ios.on = value; diff --git a/tests/app/ui/tab-view/tab-view-tests.ts b/tests/app/ui/tab-view/tab-view-tests.ts index f2243fa4c..2a5caa5ee 100644 --- a/tests/app/ui/tab-view/tab-view-tests.ts +++ b/tests/app/ui/tab-view/tab-view-tests.ts @@ -185,7 +185,7 @@ export class TabViewTest extends testModule.UITest { var tabView = this.testView; this.waitUntilTestElementIsLoaded(); - TKUnit.assertThrows(function () { + TKUnit.assertThrows(() => { let item = new tabViewModule.TabViewItem(); item.title = "Tab 0"; item.view = undefined; @@ -198,7 +198,7 @@ export class TabViewTest extends testModule.UITest { var tabView = this.testView; this.waitUntilTestElementIsLoaded(); - TKUnit.assertThrows(function () { + TKUnit.assertThrows(() => { let item = new tabViewModule.TabViewItem(); item.title = "Tab 0"; item.view = null; diff --git a/tests/app/ui/text-view/text-view-tests.ts b/tests/app/ui/text-view/text-view-tests.ts index f37145eaa..1001c1d5e 100644 --- a/tests/app/ui/text-view/text-view-tests.ts +++ b/tests/app/ui/text-view/text-view-tests.ts @@ -46,7 +46,7 @@ var _createTextViewFunc = function (): textViewModule.TextView { export var testSetText = function () { helper.buildUIAndRunTest(_createTextViewFunc(), function (views: Array) { var textView = views[0]; - + // >> set-text-value textView.text = "Hello, world!"; // << set-text-value @@ -60,7 +60,7 @@ export var testSetText = function () { export var testSetTextNull = function () { helper.buildUIAndRunTest(_createTextViewFunc(), function (views: Array) { var textView = views[0]; - + textView.text = null; var expectedValue = ""; @@ -304,14 +304,14 @@ export var testBindEditableDirectlyToModel = function () { textView.bind(options, model); // textView.editable is now false // >> (hide) - TKUnit.assert(textView.editable === false, "Actual: " + textView.text + "; Expected: " + false); - TKUnit.assert(textViewTestsNative.getNativeEditable(textView) === false, "Actual: " + textViewTestsNative.getNativeEditable(textView) + "; Expected: " + false); + TKUnit.assertFalse(textView.editable, ".ediable property should be false"); + TKUnit.assertFalse(textViewTestsNative.getNativeEditable(textView), "native Editable should be false"); // << (hide) model.set("editable", true); // textView.editable is now true // >> (hide) - TKUnit.assert(textView.editable === true, "Actual: " + textView.text + "; Expected: " + true); - TKUnit.assert(textViewTestsNative.getNativeEditable(textView) === true, "Actual: " + textViewTestsNative.getNativeEditable(textView) + "; Expected: " + true); + TKUnit.assertTrue(textView.editable, ".ediable property should be true"); + TKUnit.assertTrue(textViewTestsNative.getNativeEditable(textView), "native Editable should be true"); // << (hide) // << binding-editable-property }); @@ -332,12 +332,12 @@ export var testBindEditableToBindingConext = function () { } textView.bind(options); - TKUnit.assert(textView.editable === false, "Actual: " + textView.text + "; Expected: " + false); - TKUnit.assert(textViewTestsNative.getNativeEditable(textView) === false, "Actual: " + textViewTestsNative.getNativeEditable(textView) + "; Expected: " + false); + TKUnit.assertFalse(textView.editable, ".ediable property should be false"); + TKUnit.assertFalse(textViewTestsNative.getNativeEditable(textView), "native Editable should be false"); model.set("editable", true); - TKUnit.assert(textView.editable === true, "Actual: " + textView.text + "; Expected: " + true); - TKUnit.assert(textViewTestsNative.getNativeEditable(textView) === true, "Actual: " + textViewTestsNative.getNativeEditable(textView) + "; Expected: " + true); + TKUnit.assertTrue(textView.editable, ".ediable property should be true"); + TKUnit.assertTrue(textViewTestsNative.getNativeEditable(textView), "native Editable should be true"); }); } @@ -499,7 +499,7 @@ export function test_IntegrationTest_Transform_Decoration_Spacing_WithoutFormatt helper.buildUIAndRunTest(view, function (views: Array) { view.text = "NormalText"; view.setInlineStyle("text-transform: uppercase; text-decoration: underline; letter-spacing: 1;"); - + TKUnit.assertEqual(view.style.textTransform, enums.TextTransform.uppercase, "TextTransform"); TKUnit.assertEqual(view.style.textDecoration, enums.TextDecoration.underline, "TextDecoration"); TKUnit.assertEqual(view.style.letterSpacing, 1, "LetterSpacing"); @@ -512,7 +512,7 @@ export function test_IntegrationTest_Transform_Decoration_Spacing_WithFormattedT helper.buildUIAndRunTest(view, function (views: Array) { view.formattedText = formattedString; view.setInlineStyle("text-transform: uppercase; text-decoration: underline; letter-spacing: 1;"); - + TKUnit.assertEqual(view.style.textTransform, enums.TextTransform.uppercase, "TextTransform"); TKUnit.assertEqual(view.style.textDecoration, enums.TextDecoration.underline, "TextDecoration"); TKUnit.assertEqual(view.style.letterSpacing, 1, "LetterSpacing"); diff --git a/tests/app/ui/time-picker/time-picker-tests.ts b/tests/app/ui/time-picker/time-picker-tests.ts index 3fc8e416c..b39a5d265 100644 --- a/tests/app/ui/time-picker/time-picker-tests.ts +++ b/tests/app/ui/time-picker/time-picker-tests.ts @@ -92,19 +92,19 @@ export class TimePickerTest extends testModule.UITest { this.testView.minuteInterval = 0; }, "Setting minuteInterval property to a value less than 1 should throw."); } public testMinuteIntervalThrowExceptionWhenGreaterThan30() { - TKUnit.assertThrows(function() { + TKUnit.assertThrows(() => { this.testView.minuteInterval = 31; }, "Setting minuteInterval property to a value greater than 30 should throw."); } public testMinuteIntervalThrowExceptionWhenNotFold60() { - TKUnit.assertThrows(function() { + TKUnit.assertThrows(() => { this.testView.minuteInterval = 7; }, "Setting minuteInterval property to a value not fold 60 should throw."); } @@ -112,14 +112,14 @@ export class TimePickerTest extends testModule.UITest { this.testView.hour = this.testView.minHour - 1; }, "Setting hour property to a value less than minHour property value should throw."); } public testMinHourThrowExceptionWhenHourLessThanMinHour() { this.testView.hour = 14; - TKUnit.assertThrows(function() { + TKUnit.assertThrows(() => { this.testView.minHour = this.testView.hour + 1; }, "Setting minHour property to a greater than hour property value should throw."); } @@ -127,14 +127,14 @@ export class TimePickerTest extends testModule.UITest { this.testView.hour = this.testView.maxHour + 1; }, "Setting hour property to a value greater than maxHour property value should throw."); } public testMaxHourThrowExceptionWhenHourGreaterThanMaxHour() { this.testView.hour = 14; - TKUnit.assertThrows(function() { + TKUnit.assertThrows(() => { this.testView.maxHour = this.testView.hour - 1; }, "Setting maxHour property to a value less than hour property value should throw."); } @@ -145,7 +145,7 @@ export class TimePickerTest extends testModule.UITest { this.testView.minute = this.testView.minMinute - 1; }, "Setting minute property to a value less than minMinute property value should throw."); } @@ -155,7 +155,7 @@ export class TimePickerTest extends testModule.UITest { this.testView.minMinute = this.testView.minute + 1; }, "Setting minMinute property to a value greater than minute property value should throw."); } @@ -166,7 +166,7 @@ export class TimePickerTest extends testModule.UITest { this.testView.minute = this.testView.maxMinute + 1; }, "Setting minute property to a value greater than maxMinute property value should throw."); } @@ -176,7 +176,7 @@ export class TimePickerTest extends testModule.UITest { this.testView.maxMinute = this.testView.minute - 1; }, "Setting maxMinute property to a value less than minute property value should throw."); } diff --git a/tests/app/ui/view/view-tests-common.ts b/tests/app/ui/view/view-tests-common.ts index 00df21b5d..54defc16f 100644 --- a/tests/app/ui/view/view-tests-common.ts +++ b/tests/app/ui/view/view-tests-common.ts @@ -14,6 +14,7 @@ import * as helper from "../../ui/helper"; import * as observable from "tns-core-modules/data/observable"; import * as bindable from "tns-core-modules/ui/core/bindable"; import * as definition from "./view-tests"; +import { isIOS } from "tns-core-modules/platform"; export function test_eachDescendant() { const test = function (views: Array) { @@ -273,8 +274,20 @@ class TestView extends Layout { this.style["customCssProperty"] = value; } + private _nativeView; constructor(public name: string) { super(); + this._nativeView = this.nativeView; + this.nativeView = undefined; + } + + public _createNativeView() { + if (isIOS) { + this.nativeView = this._nativeView; + return this._nativeView; + } + + return super._createNativeView(); } public toString() { @@ -404,28 +417,55 @@ export function test_NativeSetter_called_when_add_and_remove() { secondView.custom = "testViewValue"; helper.buildUIAndRunTest(firstView, () => { - TKUnit.assertEqual(secondView.cssPropCounter, 0); - TKUnit.assertEqual(secondView.viewPropCounter, 0); + TKUnit.assertEqual(secondView.cssPropCounter, 0, "1"); + TKUnit.assertEqual(secondView.viewPropCounter, 0, "2"); // Add to visual tree firstView.addChild(secondView); - TKUnit.assertEqual(secondView.cssPropCounter, 1); - TKUnit.assertEqual(secondView.viewPropCounter, 1); - secondView.cssPropCounter = 0; - secondView.viewPropCounter = 0; + TKUnit.assertEqual(secondView.cssPropCounter, 1, "3"); + TKUnit.assertEqual(secondView.viewPropCounter, 1, "4"); // Set new value secondView.customCssProperty = "test2"; secondView.custom = "test2"; - TKUnit.assertEqual(secondView.cssPropCounter, 1); - TKUnit.assertEqual(secondView.viewPropCounter, 1); - secondView.cssPropCounter = 0; - secondView.viewPropCounter = 0; + TKUnit.assertEqual(secondView.cssPropCounter, 2, "5"); + TKUnit.assertEqual(secondView.viewPropCounter, 2, "6"); // Remove from visual tree firstView.removeChild(secondView); - TKUnit.assertEqual(secondView.cssPropCounter, 0); - TKUnit.assertEqual(secondView.viewPropCounter, 0); + TKUnit.assertEqual(secondView.cssPropCounter, 2, "7"); + TKUnit.assertEqual(secondView.viewPropCounter, 2, "8"); + }); +}; + +export function test_NativeSetter_called_when_add_and_remove_and_recycled() { + const firstView = new TestView("firstView"); + const secondView = new TestView("secondView"); + secondView.recycleNativeView = !isIOS; + secondView.customCssProperty = "testCssValue"; + secondView.custom = "testViewValue"; + + helper.buildUIAndRunTest(firstView, () => { + TKUnit.assertEqual(secondView.cssPropCounter, 0, "1"); + TKUnit.assertEqual(secondView.viewPropCounter, 0, "2"); + + // Add to visual tree + firstView.addChild(secondView); + TKUnit.assertEqual(secondView.cssPropCounter, 1, "3"); + TKUnit.assertEqual(secondView.viewPropCounter, 1, "4"); + + // Set new value + secondView.customCssProperty = "test2"; + secondView.custom = "test2"; + TKUnit.assertEqual(secondView.cssPropCounter, 2, "5"); + TKUnit.assertEqual(secondView.viewPropCounter, 2, "6"); + + // Remove from visual tree + firstView.removeChild(secondView); + + // we don't recycle nativeViews on iOS yet so reset is not called. + TKUnit.assertEqual(secondView.cssPropCounter, isIOS ? 2 : 3, "7"); + TKUnit.assertEqual(secondView.viewPropCounter, isIOS ? 2 : 3, "8"); }); }; diff --git a/tns-core-modules/application/application.android.ts b/tns-core-modules/application/application.android.ts index b7be2103c..f2e0ad5f1 100644 --- a/tns-core-modules/application/application.android.ts +++ b/tns-core-modules/application/application.android.ts @@ -47,7 +47,11 @@ export class AndroidApplication extends Observable implements AndroidApplication return this.foregroundActivity; } - public init(nativeApp: any) { + public init(nativeApp: android.app.Application) { + if (this.nativeApp === nativeApp) { + return; + } + if (this.nativeApp) { throw new Error("application.android already initialized."); } diff --git a/tns-core-modules/ui/action-bar/action-bar.android.ts b/tns-core-modules/ui/action-bar/action-bar.android.ts index 80dff4034..77970e4b2 100644 --- a/tns-core-modules/ui/action-bar/action-bar.android.ts +++ b/tns-core-modules/ui/action-bar/action-bar.android.ts @@ -7,7 +7,7 @@ import * as application from "../../application"; export * from "./action-bar-common"; const R_ID_HOME = 0x0102002c; -const ACTION_ITEM_ID_OFFSET = 1000; +const ACTION_ITEM_ID_OFFSET = 10000; let actionItemIdGenerator = ACTION_ITEM_ID_OFFSET; function generateItemId(): number { @@ -104,8 +104,7 @@ export class NavigationButton extends ActionItem { export class ActionBar extends ActionBarBase { private _android: AndroidActionBarSettings; - private _toolbar: android.support.v7.widget.Toolbar; - private _menuItemClickListener: android.support.v7.widget.Toolbar.OnMenuItemClickListener; + public nativeView: android.support.v7.widget.Toolbar; constructor() { super(); @@ -115,13 +114,6 @@ export class ActionBar extends ActionBarBase { get android(): AndroidActionBarSettings { return this._android; } - set android(value: AndroidActionBarSettings) { - throw new Error("ActionBar.android is read-only"); - } - - get _nativeView(): android.support.v7.widget.Toolbar { - return this._toolbar; - } public _addChildFromBuilder(name: string, value: any) { if (value instanceof NavigationButton) { @@ -137,12 +129,21 @@ export class ActionBar extends ActionBarBase { public _createNativeView() { initializeMenuItemClickListener(); - const toolbar = this._toolbar = new android.support.v7.widget.Toolbar(this._context); - this._menuItemClickListener = this._menuItemClickListener || new MenuItemClickListener(this); - this._toolbar.setOnMenuItemClickListener(this._menuItemClickListener); + const toolbar = new android.support.v7.widget.Toolbar(this._context); + const menuItemClickListener = new MenuItemClickListener(this); + toolbar.setOnMenuItemClickListener(menuItemClickListener); + (toolbar).menuItemClickListener = menuItemClickListener; return toolbar; } + public _initNativeView(): void { + (this.nativeView).menuItemClickListener.owner = this; + } + + public _disposeNativeView() { + (this.nativeView).menuItemClickListener.owner = null; + } + public onLoaded() { super.onLoaded(); this.update(); @@ -326,20 +327,15 @@ export class ActionBar extends ActionBarBase { } } - public _disposeNativeView() { - // don't clear _android field! - this.nativeView = undefined; - } - public _addViewToNativeVisualTree(child: View, atIndex: number = Number.MAX_VALUE): boolean { super._addViewToNativeVisualTree(child); - if (this.nativeView && child._nativeView) { - if (atIndex >= this._nativeView.getChildCount()) { - this.nativeView.addView(child._nativeView); + if (this.nativeView && child.nativeView) { + if (atIndex >= this.nativeView.getChildCount()) { + this.nativeView.addView(child.nativeView); } else { - this.nativeView.addView(child._nativeView, atIndex); + this.nativeView.addView(child.nativeView, atIndex); } return true; } @@ -350,8 +346,8 @@ export class ActionBar extends ActionBarBase { public _removeViewFromNativeVisualTree(child: View): void { super._removeViewFromNativeVisualTree(child); - if (this.nativeView && child._nativeView) { - this.nativeView.removeView(child._nativeView); + if (this.nativeView && child.nativeView) { + this.nativeView.removeView(child.nativeView); } } @@ -369,6 +365,8 @@ export class ActionBar extends ActionBarBase { } } +ActionBar.prototype.recycleNativeView = true; + let defaultTitleTextColor: number; function getDrawableOrResourceId(icon: string, resources: android.content.res.Resources): any { @@ -424,4 +422,4 @@ function getIconVisibility(iconVisibility: string): boolean { function getSystemResourceId(systemIcon: string): number { return android.content.res.Resources.getSystem().getIdentifier(systemIcon, "drawable", "android"); -} +} \ No newline at end of file diff --git a/tns-core-modules/ui/action-bar/action-bar.ios.ts b/tns-core-modules/ui/action-bar/action-bar.ios.ts index 8c7e46d04..11b51e0ad 100644 --- a/tns-core-modules/ui/action-bar/action-bar.ios.ts +++ b/tns-core-modules/ui/action-bar/action-bar.ios.ts @@ -338,4 +338,4 @@ export class ActionBar extends ActionBarBase { } [backgroundInternalProperty.setNative](value: UIColor) { // tslint:disable-line } -} +} \ No newline at end of file diff --git a/tns-core-modules/ui/activity-indicator/activity-indicator-common.ts b/tns-core-modules/ui/activity-indicator/activity-indicator-common.ts index 6d62e6b87..e974983eb 100644 --- a/tns-core-modules/ui/activity-indicator/activity-indicator-common.ts +++ b/tns-core-modules/ui/activity-indicator/activity-indicator-common.ts @@ -7,5 +7,7 @@ export class ActivityIndicatorBase extends View implements ActivityIndicatorDefi public busy: boolean; } +ActivityIndicatorBase.prototype.recycleNativeView = true; + export const busyProperty = new Property({ name: "busy", defaultValue: false, valueConverter: booleanConverter }); busyProperty.register(ActivityIndicatorBase); diff --git a/tns-core-modules/ui/activity-indicator/activity-indicator.android.ts b/tns-core-modules/ui/activity-indicator/activity-indicator.android.ts index 550be963a..a672528df 100644 --- a/tns-core-modules/ui/activity-indicator/activity-indicator.android.ts +++ b/tns-core-modules/ui/activity-indicator/activity-indicator.android.ts @@ -3,25 +3,21 @@ export * from "./activity-indicator-common"; export class ActivityIndicator extends ActivityIndicatorBase { - _progressBar: android.widget.ProgressBar; + nativeView: android.widget.ProgressBar; public _createNativeView() { - const progressBar = this._progressBar = new android.widget.ProgressBar(this._context); - this._progressBar.setVisibility(android.view.View.INVISIBLE); - this._progressBar.setIndeterminate(true); + const progressBar = new android.widget.ProgressBar(this._context); + progressBar.setVisibility(android.view.View.INVISIBLE); + progressBar.setIndeterminate(true); return progressBar; } - get android(): android.widget.ProgressBar { - return this._progressBar; - } - [busyProperty.getDefault](): boolean { return false; } [busyProperty.setNative](value: boolean) { if (this.visibility === Visibility.VISIBLE) { - this._progressBar.setVisibility(value ? android.view.View.VISIBLE : android.view.View.INVISIBLE); + this.nativeView.setVisibility(value ? android.view.View.VISIBLE : android.view.View.INVISIBLE); } } @@ -31,13 +27,13 @@ export class ActivityIndicator extends ActivityIndicatorBase { [visibilityProperty.setNative](value: Visibility) { switch (value) { case Visibility.VISIBLE: - this._progressBar.setVisibility(this.busy ? android.view.View.VISIBLE : android.view.View.INVISIBLE); + this.nativeView.setVisibility(this.busy ? android.view.View.VISIBLE : android.view.View.INVISIBLE); break; case Visibility.HIDDEN: - this._progressBar.setVisibility(android.view.View.INVISIBLE); + this.nativeView.setVisibility(android.view.View.INVISIBLE); break; case Visibility.COLLAPSE: - this._progressBar.setVisibility(android.view.View.GONE); + this.nativeView.setVisibility(android.view.View.GONE); break; default: throw new Error(`Invalid visibility value: ${value}. Valid values are: "${Visibility.VISIBLE}", "${Visibility.HIDDEN}", "${Visibility.COLLAPSE}".`); @@ -49,9 +45,9 @@ export class ActivityIndicator extends ActivityIndicatorBase { } [colorProperty.setNative](value: number | Color) { if (value instanceof Color) { - this._progressBar.getIndeterminateDrawable().setColorFilter(value.android, android.graphics.PorterDuff.Mode.SRC_IN); + this.nativeView.getIndeterminateDrawable().setColorFilter(value.android, android.graphics.PorterDuff.Mode.SRC_IN); } else { - this._progressBar.getIndeterminateDrawable().clearColorFilter(); + this.nativeView.getIndeterminateDrawable().clearColorFilter(); } } -} \ No newline at end of file +} diff --git a/tns-core-modules/ui/animation/animation.android.ts b/tns-core-modules/ui/animation/animation.android.ts index dc2ce32ea..5126147b6 100644 --- a/tns-core-modules/ui/animation/animation.android.ts +++ b/tns-core-modules/ui/animation/animation.android.ts @@ -198,7 +198,7 @@ export class Animation extends AnimationBase { } private _createAnimators(propertyAnimation: PropertyAnimation): void { - if (!propertyAnimation.target._nativeView) { + if (!propertyAnimation.target.nativeView) { return; } @@ -219,7 +219,7 @@ export class Animation extends AnimationBase { } let nativeArray; - let nativeView = propertyAnimation.target._nativeView; + let nativeView = propertyAnimation.target.nativeView; let animators = new Array(); let propertyUpdateCallbacks = new Array(); let propertyResetCallbacks = new Array(); @@ -439,7 +439,7 @@ export class Animation extends AnimationBase { private _enableHardwareAcceleration() { for (let i = 0, length = this._propertyAnimations.length; i < length; i++) { - let cache = this._propertyAnimations[i].target._nativeView; + let cache = this._propertyAnimations[i].target.nativeView; if (cache) { let layerType = cache.getLayerType(); if (layerType !== android.view.View.LAYER_TYPE_HARDWARE) { @@ -452,11 +452,11 @@ export class Animation extends AnimationBase { private _disableHardwareAcceleration() { for (let i = 0, length = this._propertyAnimations.length; i < length; i++) { - let cache = this._propertyAnimations[i].target._nativeView; + let cache = this._propertyAnimations[i].target.nativeView; if (cache && cache.layerType !== undefined) { cache.setLayerType(cache.layerType, null); cache.layerType = undefined; } } } -} +} \ No newline at end of file diff --git a/tns-core-modules/ui/animation/animation.ios.ts b/tns-core-modules/ui/animation/animation.ios.ts index 5d8794110..cce5b1add 100644 --- a/tns-core-modules/ui/animation/animation.ios.ts +++ b/tns-core-modules/ui/animation/animation.ios.ts @@ -223,7 +223,7 @@ export class Animation extends AnimationBase { let length = this._mergedPropertyAnimations.length; for (; i < length; i++) { let propertyAnimation = this._mergedPropertyAnimations[i]; - propertyAnimation.target._nativeView.layer.removeAllAnimations(); + propertyAnimation.target.nativeView.layer.removeAllAnimations(); if (propertyAnimation._propertyResetCallback) { propertyAnimation._propertyResetCallback(propertyAnimation._originalValue, this._valueSource); } @@ -259,7 +259,7 @@ export class Animation extends AnimationBase { private static _getNativeAnimationArguments(animation: PropertyAnimationInfo, valueSource: "animation" | "keyframe"): AnimationInfo { - let nativeView = animation.target._nativeView; + let nativeView = animation.target.nativeView; let propertyNameToAnimate = animation.property; let value = animation.value; let originalValue; @@ -381,7 +381,7 @@ export class Animation extends AnimationBase { private static _createNativeAnimation(propertyAnimations: Array, index: number, playSequentially: boolean, args: AnimationInfo, animation: PropertyAnimation, valueSource: "animation" | "keyframe", finishedCallback: (cancelled?: boolean) => void) { - let nativeView = animation.target._nativeView; + let nativeView = animation.target.nativeView; let nativeAnimation = CABasicAnimation.animationWithKeyPath(args.propertyNameToAnimate); nativeAnimation.fromValue = args.fromValue; nativeAnimation.toValue = args.toValue; @@ -415,7 +415,7 @@ export class Animation extends AnimationBase { private static _createNativeSpringAnimation(propertyAnimations: Array, index: number, playSequentially: boolean, args: AnimationInfo, animation: PropertyAnimationInfo, valueSource: "animation" | "keyframe", finishedCallback: (cancelled?: boolean) => void) { - let nativeView = animation.target._nativeView; + let nativeView = animation.target.nativeView; let callback = undefined; let nextAnimation; @@ -593,7 +593,7 @@ export function _getTransformMismatchErrorMessage(view: View): string { result = CGAffineTransformRotate(result, (view.rotate || 0) * Math.PI / 180); result = CGAffineTransformScale(result, view.scaleX || 1, view.scaleY || 1); let viewTransform = NSStringFromCGAffineTransform(result); - let nativeTransform = NSStringFromCGAffineTransform(view._nativeView.transform); + let nativeTransform = NSStringFromCGAffineTransform(view.nativeView.transform); if (viewTransform !== nativeTransform) { return "View and Native transforms do not match. View: " + viewTransform + "; Native: " + nativeTransform; diff --git a/tns-core-modules/ui/border/border.ts b/tns-core-modules/ui/border/border.ts index 0b550dd47..7fd9feac5 100644 --- a/tns-core-modules/ui/border/border.ts +++ b/tns-core-modules/ui/border/border.ts @@ -40,3 +40,5 @@ export class Border extends ContentView implements BorderDefinition { View.layoutChild(this, this.layoutView, this.effectiveBorderLeftWidth, this.effectiveBorderTopWidth, right - left - horizontalBorderLength, bottom - top - verticalBorderLength); } } + +Border.prototype.recycleNativeView = true; \ No newline at end of file diff --git a/tns-core-modules/ui/button/button-common.ts b/tns-core-modules/ui/button/button-common.ts index 94a5a8af3..b65b28cd0 100644 --- a/tns-core-modules/ui/button/button-common.ts +++ b/tns-core-modules/ui/button/button-common.ts @@ -13,3 +13,5 @@ export abstract class ButtonBase extends TextBase implements ButtonDefinition { this.style.whiteSpace = value ? WhiteSpace.NORMAL : WhiteSpace.NO_WRAP; } } + +ButtonBase.prototype.recycleNativeView = true; \ No newline at end of file diff --git a/tns-core-modules/ui/button/button.android.ts b/tns-core-modules/ui/button/button.android.ts index c4428f020..34a8e1cc9 100644 --- a/tns-core-modules/ui/button/button.android.ts +++ b/tns-core-modules/ui/button/button.android.ts @@ -1,6 +1,6 @@ import { ButtonBase, PseudoClassHandler, - paddingLeftProperty, paddingTopProperty, paddingRightProperty, paddingBottomProperty, + paddingLeftProperty, paddingTopProperty, paddingRightProperty, paddingBottomProperty, Length, zIndexProperty } from "./button-common"; @@ -13,6 +13,7 @@ interface ClickListener { } let ClickListener: ClickListener; +let APILEVEL: number; function initializeClickListener(): void { if (ClickListener) { @@ -32,20 +33,31 @@ function initializeClickListener(): void { } ClickListener = ClickListenerImpl; + APILEVEL = android.os.Build.VERSION.SDK_INT; } export class Button extends ButtonBase { - _button: android.widget.Button; - + nativeView: android.widget.Button; + private _highlightedHandler: (args: TouchGestureEventData) => void; public _createNativeView() { initializeClickListener(); - const button = this._button = new android.widget.Button(this._context); - button.setOnClickListener(new ClickListener(this)); + const button = new android.widget.Button(this._context); + const clickListener = new ClickListener(this); + button.setOnClickListener(clickListener); + (button).clickListener = clickListener; return button; } + public _initNativeView(): void { + (this.nativeView).clickListener.owner = this; + } + + public _disposeNativeView() { + (this.nativeView).clickListener.owner = null; + } + @PseudoClassHandler("normal", "highlighted", "pressed", "active") _updateHandler(subscribe: boolean) { if (subscribe) { @@ -99,8 +111,8 @@ export class Button extends ButtonBase { [zIndexProperty.setNative](value: number) { org.nativescript.widgets.ViewHelper.setZIndex(this.nativeView, value); // API >= 21 - if (this.nativeView.setStateListAnimator) { - this.nativeView.setStateListAnimator(null); + if (APILEVEL >= 21) { + (this.nativeView).setStateListAnimator(null); } } -} +} \ No newline at end of file diff --git a/tns-core-modules/ui/content-view/content-view.ts b/tns-core-modules/ui/content-view/content-view.ts index 739fc4ff8..fe9a74480 100644 --- a/tns-core-modules/ui/content-view/content-view.ts +++ b/tns-core-modules/ui/content-view/content-view.ts @@ -91,3 +91,5 @@ export class ContentView extends CustomLayoutView implements ContentViewDefiniti View.layoutChild(this, this.layoutView, 0, 0, right - left, bottom - top); } } + +ContentView.prototype.recycleNativeView = true; \ No newline at end of file diff --git a/tns-core-modules/ui/core/properties/properties.ts b/tns-core-modules/ui/core/properties/properties.ts index 9b6762c99..b279fe237 100644 --- a/tns-core-modules/ui/core/properties/properties.ts +++ b/tns-core-modules/ui/core/properties/properties.ts @@ -122,8 +122,8 @@ export class Property implements TypedPropertyDescriptor< } if (setNativeValue) { - if (this[getDefault] && !(defaultValueKey in this)) { - this[defaultValueKey] = this[getDefault](); + if (!(defaultValueKey in this)) { + this[defaultValueKey] = this[getDefault] ? this[getDefault]() : defaultValue; } this[setNative](unboxedValue); @@ -158,6 +158,10 @@ export class Property implements TypedPropertyDescriptor< valueChanged(owner, currentValue, value); } + if (owner.nativeView && !(defaultValueKey in owner)) { + owner[defaultValueKey] = owner[getDefault] ? owner[getDefault]() : defaultValue; + } + if (owner.hasListeners(eventName)) { owner.notify({ eventName: eventName, @@ -262,8 +266,8 @@ export class CoercibleProperty extends Property imp } if (setNativeValue) { - if (this[getDefault] && !(defaultValueKey in this)) { - this[defaultValueKey] = this[getDefault](); + if (!(defaultValueKey in this)) { + this[defaultValueKey] = this[getDefault] ? this[getDefault]() : defaultValue; } this[setNative](unboxedValue); @@ -447,8 +451,8 @@ export class CssProperty implements definitions.CssProperty< } if (setNativeValue) { - if (view[getDefault] && !(defaultValueKey in this)) { - this[defaultValueKey] = view[getDefault](); + if (!(defaultValueKey in this)) { + this[defaultValueKey] = view[getDefault] ? view[getDefault]() : defaultValue; } view[setNative](value); @@ -516,8 +520,8 @@ export class CssProperty implements definitions.CssProperty< } if (setNativeValue) { - if (view[getDefault] && !(defaultValueKey in this)) { - this[defaultValueKey] = view[getDefault](); + if (!(defaultValueKey in this)) { + this[defaultValueKey] = view[getDefault] ? view[getDefault]() : defaultValue; } view[setNative](value); @@ -799,8 +803,8 @@ export class InheritedCssProperty extends CssProperty } if (setNativeValue) { - if (view[getDefault] && !(defaultValueKey in this)) { - this[defaultValueKey] = view[getDefault](); + if (!(defaultValueKey in this)) { + this[defaultValueKey] = view[getDefault] ? view[getDefault]() : defaultValue; } view[setNative](newValue); @@ -962,8 +966,8 @@ export function initNativeView(view: ViewBase): void { const getDefault = property.getDefault; if (setNative in view) { const defaultValueKey = property.defaultValueKey; - if (view[getDefault] && !(defaultValueKey in view)) { - view[defaultValueKey] = view[getDefault](); + if (!(defaultValueKey in view)) { + view[defaultValueKey] = view[getDefault] ? view[getDefault]() : property.defaultValue; } const value = view[symbol]; @@ -982,8 +986,8 @@ export function initNativeView(view: ViewBase): void { if (view[property.setNative]) { if (view[property.getDefault]) { const defaultValueKey = property.defaultValueKey; - if (view[property.getDefault] && !(defaultValueKey in style)) { - style[defaultValueKey] = view[property.getDefault](); + if (!(defaultValueKey in style)) { + style[defaultValueKey] = view[property.getDefault] ? view[property.getDefault]() : property.defaultValue; } } @@ -1009,9 +1013,6 @@ export function resetNativeView(view: ViewBase): void { view[property.setNative](property.defaultValue); } } - - // This will not call propertyChange!!! - delete view[property.key]; } const style = view.style; @@ -1031,9 +1032,6 @@ export function resetNativeView(view: ViewBase): void { view[property.setNative](property.defaultValue); } } - - // This will not call propertyChange!!! - delete style[property.key]; } } @@ -1123,4 +1121,4 @@ export function makeParser(isValid: (value: any) => boolean): (value: any) => throw new Error("Invalid value: " + value); } }; -} +} \ No newline at end of file diff --git a/tns-core-modules/ui/core/view-base/view-base.ts b/tns-core-modules/ui/core/view-base/view-base.ts index 4c1abb644..227a2ebbe 100644 --- a/tns-core-modules/ui/core/view-base/view-base.ts +++ b/tns-core-modules/ui/core/view-base/view-base.ts @@ -91,6 +91,41 @@ export function eachDescendant(view: ViewBaseDefinition, callback: (child: ViewB let viewIdCounter = 1; +const contextMap = new Map[]>>(); + +function getNativeView(context: Object, typeName: string): Object { + let typeMap = contextMap.get(context); + if (!typeMap) { + typeMap = new Map[]>(); + contextMap.set(context, typeMap); + return undefined; + } + + const array = typeMap.get(typeName); + if (array) { + let nativeView; + while (array.length > 0) { + const weakRef = array.pop(); + nativeView = weakRef.get(); + if (nativeView) { + return nativeView; + } + } + } + return undefined; +} + +function putNativeView(context: Object, view: ViewBase): void { + const typeMap = contextMap.get(context); + const typeName = view.typeName; + let list = typeMap.get(typeName); + if (!list) { + list = []; + typeMap.set(typeName, list); + } + list.push(new WeakRef(view.nativeView)); +} + export class ViewBase extends Observable implements ViewBaseDefinition { public static loadedEvent = "loaded"; public static unloadedEvent = "unloaded"; @@ -200,11 +235,10 @@ export class ViewBase extends Observable implements ViewBaseDefinition { this.className = v; } - public get inlineStyleSelector(): SelectorCore { + get inlineStyleSelector(): SelectorCore { return this._inlineStyleSelector; } - - public set inlineStyleSelector(value: SelectorCore) { + set inlineStyleSelector(value: SelectorCore) { this._inlineStyleSelector = value; } @@ -567,7 +601,7 @@ export class ViewBase extends Observable implements ViewBaseDefinition { } public _resetNativeView(): void { - if (this.nativeView && this.recycleNativeView) { + if (this.nativeView && this.recycleNativeView && isAndroid) { resetNativeView(this); } } @@ -585,8 +619,18 @@ export class ViewBase extends Observable implements ViewBaseDefinition { this._context = context; traceNotifyEvent(this, "_onContextChanged"); + let currentNativeView = this.nativeView; if (isAndroid) { - const nativeView = this._androidView = this.nativeView = this._createNativeView(); + let nativeView: android.view.View; + if (this.recycleNativeView) { + nativeView = getNativeView(context, this.typeName); + } + + if (!nativeView) { + nativeView = this._createNativeView(); + } + + this._androidView = this.nativeView = nativeView; if (nativeView) { let result: android.graphics.Rect = (nativeView).defaultPaddings; if (result === undefined) { @@ -616,7 +660,10 @@ export class ViewBase extends Observable implements ViewBaseDefinition { } else { // TODO: Implement _createNativeView for iOS this._createNativeView(); - this.nativeView = this._iosView = (this)._nativeView; + if (!currentNativeView) { + console.log(`${this.typeName} doesnt have NativeView !!!!! =================`); + } + // this.nativeView = this._iosView = (this)._nativeView; } this._initNativeView(); @@ -627,7 +674,9 @@ export class ViewBase extends Observable implements ViewBaseDefinition { } if (this.nativeView) { - initNativeView(this); + if (currentNativeView !== this.nativeView) { + initNativeView(this); + } } this.eachChild((child) => { @@ -645,7 +694,7 @@ export class ViewBase extends Observable implements ViewBaseDefinition { if (traceEnabled()) { traceWrite(`${this}._tearDownUI(${force})`, traceCategories.VisualTreeEvents); } - + this._resetNativeView(); this.eachChild((child) => { @@ -657,8 +706,20 @@ export class ViewBase extends Observable implements ViewBaseDefinition { this.parent._removeViewFromNativeVisualTree(this); } + const nativeView = this.nativeView; + if (nativeView && this.recycleNativeView && isAndroid) { + const nativeParent = isAndroid ? (nativeView).getParent() : (nativeView).superview; + if (!nativeParent) { + putNativeView(this._context, this); + } + } + this._disposeNativeView(); + this.nativeView = null; + this._androidView = null; + this._iosView = null; + this._context = null; traceNotifyEvent(this, "_onContextChanged"); traceNotifyEvent(this, "_tearDownUI"); @@ -822,4 +883,4 @@ export function booleanConverter(v: string): boolean { } throw new Error(`Invalid boolean: ${v}`); -} +} \ No newline at end of file diff --git a/tns-core-modules/ui/core/view/view-common.ts b/tns-core-modules/ui/core/view/view-common.ts index 3b1c1e148..dc116b97e 100644 --- a/tns-core-modules/ui/core/view/view-common.ts +++ b/tns-core-modules/ui/core/view/view-common.ts @@ -680,10 +680,6 @@ export abstract class ViewCommon extends ViewBase implements ViewDefinition { // needed for iOS. } - get _nativeView(): any { - return undefined; - } - public focus(): boolean { return undefined; } diff --git a/tns-core-modules/ui/core/view/view.android.ts b/tns-core-modules/ui/core/view/view.android.ts index eddca41b0..005253756 100644 --- a/tns-core-modules/ui/core/view/view.android.ts +++ b/tns-core-modules/ui/core/view/view.android.ts @@ -20,10 +20,6 @@ import { export * from "./view-common"; -const ANDROID = "_android"; -const NATIVE_VIEW = "_nativeView"; -const VIEW_GROUP = "_viewGroup"; - interface TouchListener { new (owner: View): android.view.View.OnTouchListener; } @@ -60,7 +56,7 @@ function initializeTouchListener(): void { }); } - let nativeView = owner._nativeView; + let nativeView = owner.nativeView; if (!nativeView || !nativeView.onTouchEvent) { return false; } @@ -76,7 +72,7 @@ export class View extends ViewCommon { private touchListenerIsSet: boolean; private touchListener: android.view.View.OnTouchListener; - public nativeView: android.view.View; + nativeView: android.view.View; // TODO: Implement unobserve that detach the touchListener. observe(type: GestureTypes, callback: (args: GestureEventData) => void, thisArg?: any): void { @@ -93,7 +89,7 @@ export class View extends ViewCommon { public onUnloaded() { if (this.touchListenerIsSet) { - this._nativeView.setOnTouchListener(null); + this.nativeView.setOnTouchListener(null); this.touchListenerIsSet = false; } @@ -106,61 +102,40 @@ export class View extends ViewCommon { } private setOnTouchListener() { - if (this._nativeView && this.hasGestureObservers()) { + if (this.nativeView && this.hasGestureObservers()) { this.touchListenerIsSet = true; - if (this._nativeView.setClickable) { - this._nativeView.setClickable(true); + if (this.nativeView.setClickable) { + this.nativeView.setClickable(true); } initializeTouchListener(); this.touchListener = this.touchListener || new TouchListener(this); - this._nativeView.setOnTouchListener(this.touchListener); + this.nativeView.setOnTouchListener(this.touchListener); } } - // TODO: revise this method - public _disposeNativeView() { - - // Widgets like buttons and such have reference to their native view in both properties. - if (this[NATIVE_VIEW] === this[ANDROID]) { - (this)[NATIVE_VIEW] = undefined; - } - - // Handle layout and content view - if (this[VIEW_GROUP] === this[ANDROID]) { - this[VIEW_GROUP] = undefined; - } - - this[ANDROID] = undefined; - this.nativeView = undefined; - } - - get _nativeView(): android.view.View { - return this.android; - } - get isLayoutRequired(): boolean { return !this.isLayoutValid; } get isLayoutValid(): boolean { - if (this._nativeView) { - return !this._nativeView.isLayoutRequested(); + if (this.nativeView) { + return !this.nativeView.isLayoutRequested(); } return false; } public layoutNativeView(left: number, top: number, right: number, bottom: number): void { - if (this._nativeView) { - this._nativeView.layout(left, top, right, bottom); + if (this.nativeView) { + this.nativeView.layout(left, top, right, bottom); } } public requestLayout(): void { super.requestLayout(); - if (this._nativeView) { - return this._nativeView.requestLayout(); + if (this.nativeView) { + return this.nativeView.requestLayout(); } } @@ -175,7 +150,7 @@ export class View extends ViewCommon { } public onMeasure(widthMeasureSpec: number, heightMeasureSpec: number): void { - let view = this._nativeView; + let view = this.nativeView; if (view) { view.measure(widthMeasureSpec, heightMeasureSpec); this.setMeasuredDimension(view.getMeasuredWidth(), view.getMeasuredHeight()); @@ -183,56 +158,56 @@ export class View extends ViewCommon { } public onLayout(left: number, top: number, right: number, bottom: number): void { - let view = this._nativeView; + let view = this.nativeView; if (view) { this.layoutNativeView(left, top, right, bottom); } } _getCurrentLayoutBounds(): { left: number; top: number; right: number; bottom: number } { - if (this._nativeView) { + if (this.nativeView && !this.isCollapsed) { return { - left: this._nativeView.getLeft(), - top: this._nativeView.getTop(), - right: this._nativeView.getRight(), - bottom: this._nativeView.getBottom() + left: this.nativeView.getLeft(), + top: this.nativeView.getTop(), + right: this.nativeView.getRight(), + bottom: this.nativeView.getBottom() }; + } else { + return { left: 0, top: 0, right: 0, bottom: 0 }; } - - return super._getCurrentLayoutBounds(); } public getMeasuredWidth(): number { - if (this._nativeView) { - return this._nativeView.getMeasuredWidth(); + if (this.nativeView) { + return this.nativeView.getMeasuredWidth(); } return super.getMeasuredWidth(); } public getMeasuredHeight(): number { - if (this._nativeView) { - return this._nativeView.getMeasuredHeight(); + if (this.nativeView) { + return this.nativeView.getMeasuredHeight(); } return super.getMeasuredHeight(); } public focus(): boolean { - if (this._nativeView) { - return this._nativeView.requestFocus(); + if (this.nativeView) { + return this.nativeView.requestFocus(); } return false; } public getLocationInWindow(): Point { - if (!this._nativeView || !this._nativeView.getWindowToken()) { + if (!this.nativeView || !this.nativeView.getWindowToken()) { return undefined; } let nativeArray = (Array).create("int", 2); - this._nativeView.getLocationInWindow(nativeArray); + this.nativeView.getLocationInWindow(nativeArray); return { x: layout.toDeviceIndependentPixels(nativeArray[0]), y: layout.toDeviceIndependentPixels(nativeArray[1]), @@ -240,12 +215,12 @@ export class View extends ViewCommon { } public getLocationOnScreen(): Point { - if (!this._nativeView || !this._nativeView.getWindowToken()) { + if (!this.nativeView || !this.nativeView.getWindowToken()) { return undefined; } let nativeArray = (Array).create("int", 2); - this._nativeView.getLocationOnScreen(nativeArray); + this.nativeView.getLocationOnScreen(nativeArray); return { x: layout.toDeviceIndependentPixels(nativeArray[0]), y: layout.toDeviceIndependentPixels(nativeArray[1]), @@ -253,16 +228,16 @@ export class View extends ViewCommon { } public getLocationRelativeTo(otherView: ViewCommon): Point { - if (!this._nativeView || !this._nativeView.getWindowToken() || - !otherView._nativeView || !otherView._nativeView.getWindowToken() || - this._nativeView.getWindowToken() !== otherView._nativeView.getWindowToken()) { + if (!this.nativeView || !this.nativeView.getWindowToken() || + !otherView.nativeView || !otherView.nativeView.getWindowToken() || + this.nativeView.getWindowToken() !== otherView.nativeView.getWindowToken()) { return undefined; } let myArray = (Array).create("int", 2); - this._nativeView.getLocationOnScreen(myArray); + this.nativeView.getLocationOnScreen(myArray); let otherArray = (Array).create("int", 2); - otherView._nativeView.getLocationOnScreen(otherArray); + otherView.nativeView.getLocationOnScreen(otherArray); return { x: layout.toDeviceIndependentPixels(myArray[0] - otherArray[0]), y: layout.toDeviceIndependentPixels(myArray[1] - otherArray[1]), @@ -325,7 +300,7 @@ export class View extends ViewCommon { if (!value) { initializeDisabledListener(); // User interaction is disabled -- we stop it and we do not care whether someone wants to listen for gestures. - this._nativeView.setOnTouchListener(disableUserInteractionListener); + this.nativeView.setOnTouchListener(disableUserInteractionListener); } else { this.setOnTouchListener(); } @@ -494,29 +469,20 @@ export class View extends ViewCommon { } export class CustomLayoutView extends View implements CustomLayoutViewDefinition { - private _viewGroup: android.view.ViewGroup; - - get android(): android.view.ViewGroup { - return this._viewGroup; - } - - get _nativeView(): android.view.ViewGroup { - return this._viewGroup; - } + nativeView: android.view.ViewGroup; public _createNativeView() { - const viewGroup = this._viewGroup = new org.nativescript.widgets.ContentLayout(this._context); - return viewGroup; + return new org.nativescript.widgets.ContentLayout(this._context); } public _addViewToNativeVisualTree(child: ViewCommon, atIndex: number = -1): boolean { super._addViewToNativeVisualTree(child); - if (this._nativeView && child.nativeView) { + if (this.nativeView && child.nativeView) { if (traceEnabled()) { traceWrite(`${this}.nativeView.addView(${child}.nativeView, ${atIndex})`, traceCategories.VisualTreeEvents); } - this._nativeView.addView(child.nativeView, atIndex); + this.nativeView.addView(child.nativeView, atIndex); if (child instanceof View) { this._updateNativeLayoutParams(child); } @@ -542,10 +508,10 @@ export class CustomLayoutView extends View implements CustomLayoutViewDefinition public _removeViewFromNativeVisualTree(child: ViewCommon): void { super._removeViewFromNativeVisualTree(child); - if (this._nativeView && child._nativeView) { - this._nativeView.removeView(child._nativeView); + if (this.nativeView && child.nativeView) { + this.nativeView.removeView(child.nativeView); if (traceEnabled()) { - traceWrite(`${this}._nativeView.removeView(${child}._nativeView)`, traceCategories.VisualTreeEvents); + traceWrite(`${this}.nativeView.removeView(${child}.nativeView)`, traceCategories.VisualTreeEvents); traceNotifyEvent(child, "childInLayoutRemovedFromNativeVisualTree"); } } diff --git a/tns-core-modules/ui/core/view/view.d.ts b/tns-core-modules/ui/core/view/view.d.ts index b7448143c..a45bbd362 100644 --- a/tns-core-modules/ui/core/view/view.d.ts +++ b/tns-core-modules/ui/core/view/view.d.ts @@ -494,7 +494,6 @@ export abstract class View extends ViewBase implements ApplyXmlAttributes { _getCurrentLayoutBounds(): { left: number; top: number; right: number; bottom: number }; _goToVisualState(state: string); - _nativeView: any; _setNativeViewFrame(nativeView: any, frame: any): void; // _onStylePropertyChanged(property: dependencyObservable.Property): void; diff --git a/tns-core-modules/ui/core/view/view.ios.ts b/tns-core-modules/ui/core/view/view.ios.ts index a642ce0a7..c7e8fb0e9 100644 --- a/tns-core-modules/ui/core/view/view.ios.ts +++ b/tns-core-modules/ui/core/view/view.ios.ts @@ -27,9 +27,9 @@ export class View extends ViewCommon { private _cachedFrame: CGRect; private _suspendCATransaction = false; - get _nativeView(): UIView { - return this.ios; - } + // get nativeView(): UIView { + // return this.ios; + // } public _addViewCore(view: ViewCommon, atIndex?: number) { super._addViewCore(view, atIndex); @@ -411,15 +411,15 @@ export class View extends ViewCommon { export class CustomLayoutView extends View { - private _view: UIView; + nativeView: UIView; constructor() { super(); - this._view = UIView.new(); + this.nativeView = UIView.new(); } get ios(): UIView { - return this._view; + return this.nativeView; } public onMeasure(widthMeasureSpec: number, heightMeasureSpec: number): void { @@ -452,4 +452,4 @@ export class CustomLayoutView extends View { child.nativeView.removeFromSuperview(); } } -} +} \ No newline at end of file diff --git a/tns-core-modules/ui/date-picker/date-picker-common.ts b/tns-core-modules/ui/date-picker/date-picker-common.ts index dd93e31c7..81382e20d 100644 --- a/tns-core-modules/ui/date-picker/date-picker-common.ts +++ b/tns-core-modules/ui/date-picker/date-picker-common.ts @@ -12,6 +12,8 @@ export class DatePickerBase extends View implements DatePickerDefinition { public date: Date; } +DatePickerBase.prototype.recycleNativeView = true; + export const yearProperty = new Property({ name: "year", valueConverter: (v) => parseInt(v) }); yearProperty.register(DatePickerBase); @@ -33,4 +35,4 @@ export const minDateProperty = new Property({ name: "minDa minDateProperty.register(DatePickerBase); export const dateProperty = new Property({ name: "date", equalityComparer: dateComparer, valueConverter: (v) => new Date(v) }); -dateProperty.register(DatePickerBase); +dateProperty.register(DatePickerBase); \ No newline at end of file diff --git a/tns-core-modules/ui/date-picker/date-picker.android.ts b/tns-core-modules/ui/date-picker/date-picker.android.ts index 6017c2f58..5758c6e5e 100644 --- a/tns-core-modules/ui/date-picker/date-picker.android.ts +++ b/tns-core-modules/ui/date-picker/date-picker.android.ts @@ -25,23 +25,23 @@ function initializeDateChangedListener(): void { onDateChanged(picker: android.widget.DatePicker, year: number, month: number, day: number) { const owner = this.owner; - let dateIsChanged = false; + let dateChanged = false; if (year !== owner.year) { yearProperty.nativeValueChange(owner, year); - dateIsChanged = true; + dateChanged = true; } if ((month + 1) !== owner.month) { monthProperty.nativeValueChange(owner, month + 1); - dateIsChanged = true; + dateChanged = true; } if (day !== owner.day) { dayProperty.nativeValueChange(owner, day); - dateIsChanged = true; + dateChanged = true; } - if (dateIsChanged) { + if (dateChanged) { dateProperty.nativeValueChange(owner, new Date(year, month, day)); } } @@ -51,85 +51,87 @@ function initializeDateChangedListener(): void { } export class DatePicker extends DatePickerBase { - private _android: android.widget.DatePicker; - public _listener: android.widget.DatePicker.OnDateChangedListener; - - get android(): android.widget.DatePicker { - return this._android; - } + nativeView: android.widget.DatePicker; public _createNativeView() { initializeDateChangedListener(); - const picker = this._android = new android.widget.DatePicker(this._context); + const picker = new android.widget.DatePicker(this._context); picker.setCalendarViewShown(false); - this._listener = this._listener = new DateChangedListener(this); - picker.init(0, 0, 0, this._listener); + const listener = new DateChangedListener(this); + picker.init(0, 0, 0, listener); + (picker).listener = listener; return picker; } + public _initNativeView(): void { + (this.nativeView).listener.owner = this; + } + + public _disposeNativeView() { + (this.nativeView).listener.owner = null; + } + private updateNativeDate(): void { - const year = typeof this.year === "number" ? this.year : this.android.getYear(); - const month = typeof this.month === "number" ? (this.month - 1) : this.android.getMonth(); - const day = typeof this.day === "number" ? this.day : this.android.getDayOfMonth(); + const nativeView = this.nativeView; + const year = typeof this.year === "number" ? this.year : nativeView.getYear(); + const month = typeof this.month === "number" ? (this.month - 1) : nativeView.getMonth(); + const day = typeof this.day === "number" ? this.day : nativeView.getDayOfMonth(); this.date = new Date(year, month, day); } [yearProperty.getDefault](): number { - return this.android.getYear(); + return this.nativeView.getYear(); } [yearProperty.setNative](value: number) { - if (this.android.getYear() !== value) { + if (this.nativeView.getYear() !== value) { this.updateNativeDate(); } } [monthProperty.getDefault](): number { - return this.android.getMonth(); + return this.nativeView.getMonth(); } [monthProperty.setNative](value: number) { - if (this.android.getMonth() !== (value - 1)) { + if (this.nativeView.getMonth() !== (value - 1)) { this.updateNativeDate(); } } [dayProperty.getDefault](): number { - return this.android.getDayOfMonth(); + return this.nativeView.getDayOfMonth(); } [dayProperty.setNative](value: number) { - if (this.android.getDayOfMonth() !== value) { + if (this.nativeView.getDayOfMonth() !== value) { this.updateNativeDate(); } } [dateProperty.getDefault](): Date { - return new Date(this.android.getYear(), this.android.getMonth(), this.android.getDayOfMonth()); + const nativeView = this.nativeView; + return new Date(nativeView.getYear(), nativeView.getMonth(), nativeView.getDayOfMonth()); } [dateProperty.setNative](value: Date) { - if (this.android.getDayOfMonth() !== value.getDay() - || this.android.getMonth() !== value.getMonth() - || this.android.getYear() !== value.getFullYear()) { - this.android.updateDate(value.getFullYear(), value.getMonth(), value.getDate()); + const nativeView = this.nativeView; + if (nativeView.getDayOfMonth() !== value.getDay() + || nativeView.getMonth() !== value.getMonth() + || nativeView.getYear() !== value.getFullYear()) { + nativeView.updateDate(value.getFullYear(), value.getMonth(), value.getDate()); } } [maxDateProperty.getDefault](): Date { - return this.android.getMaxDate(); + return this.nativeView.getMaxDate(); } [maxDateProperty.setNative](value: Date) { - let newValue = value.getTime(); - if (this.android.getMaxDate() !== newValue) { - this.android.setMaxDate(newValue); - } + const newValue = value instanceof Date ? value.getTime() : value; + this.nativeView.setMaxDate(newValue); } [minDateProperty.getDefault](): Date { - return this.android.getMinDate(); + return this.nativeView.getMinDate(); } [minDateProperty.setNative](value: Date) { - let picker = this.android; - let newValue = value.getTime(); - if (picker.getMinDate() !== newValue) { - picker.setMinDate(newValue); - } + const newValue = value instanceof Date ? value.getTime() : value; + this.nativeView.setMinDate(newValue); } -} \ No newline at end of file +} diff --git a/tns-core-modules/ui/editable-text-base/editable-text-base.android.ts b/tns-core-modules/ui/editable-text-base/editable-text-base.android.ts index a2fe9a648..358f34634 100644 --- a/tns-core-modules/ui/editable-text-base/editable-text-base.android.ts +++ b/tns-core-modules/ui/editable-text-base/editable-text-base.android.ts @@ -48,6 +48,10 @@ function initializeEditTextListeners(): void { public afterTextChanged(editable: android.text.IEditable) { const owner = this.owner; + if (!owner) { + return; + } + switch (owner.updateTextTrigger) { case "focusLost": owner._dirtyTextAccumulator = editable.toString(); @@ -62,6 +66,9 @@ function initializeEditTextListeners(): void { public onFocusChange(view: android.view.View, hasFocus: boolean) { const owner = this.owner; + if (!owner) { + return; + } if (hasFocus) { if (dismissKeyboardTimeoutId) { @@ -88,6 +95,9 @@ function initializeEditTextListeners(): void { public onEditorAction(textView: android.widget.TextView, actionId: number, event: android.view.KeyEvent): boolean { const owner = this.owner; + if (!owner) { + return; + } if (actionId === android.view.inputmethod.EditorInfo.IME_ACTION_DONE || actionId === android.view.inputmethod.EditorInfo.IME_ACTION_GO || @@ -118,66 +128,70 @@ export abstract class EditableTextBase extends EditableTextBaseCommon { /* tslint:disable */ _dirtyTextAccumulator: string; /* tslint:enable */ - - _editTextListeners: EditTextListeners; - private _android: android.widget.EditText; + nativeView: android.widget.EditText; private _keyListenerCache: android.text.method.KeyListener; - get android(): android.widget.EditText { - return this._android; - } - - public abstract _configureEditText(): void; + public abstract _configureEditText(editText: android.widget.EditText): void; public abstract _onReturnPress(): void; public _createNativeView() { initializeEditTextListeners(); - const editText = this._android = new android.widget.EditText(this._context); - this._configureEditText(); - this._keyListenerCache = editText.getKeyListener(); + const editText = new android.widget.EditText(this._context); + this._configureEditText(editText); - this._editTextListeners = this._editTextListeners || new EditTextListeners(this); - editText.addTextChangedListener(this._editTextListeners); - editText.setOnFocusChangeListener(this._editTextListeners); - editText.setOnEditorActionListener(this._editTextListeners); + const listeners = new EditTextListeners(this); + editText.addTextChangedListener(listeners); + editText.setOnFocusChangeListener(listeners); + editText.setOnEditorActionListener(listeners); + (editText).listener = listeners; return editText; } - public _resetNativeView(force?: boolean) { - if (this._android) { - this._android.setOnFocusChangeListener(null); - this._android.setOnEditorActionListener(null); - - if (this._editTextListeners) { - this._android.removeTextChangedListener(this._editTextListeners); - } - } - super._resetNativeView(); + public _initNativeView(): void { + const nativeView = this.nativeView; + (nativeView).listener.owner = this; + this._keyListenerCache = nativeView.getKeyListener(); } public _disposeNativeView(force?: boolean) { - this._android = undefined; - super._disposeNativeView(); + (this.nativeView).listener.owner = null; } + // public _resetNativeView(force?: boolean) { + // if (this._android) { + // this._android.setOnFocusChangeListener(null); + // this._android.setOnEditorActionListener(null); + + // if (this._editTextListeners) { + // this._android.removeTextChangedListener(this._editTextListeners); + // } + // } + // super._resetNativeView(); + // } + + // public _disposeNativeView(force?: boolean) { + // this._android = undefined; + // super._disposeNativeView(); + // } + public dismissSoftInput() { - ad.dismissSoftInput(this._android); + ad.dismissSoftInput(this.nativeView); } public focus(): boolean { let result = super.focus(); if (result) { - ad.showSoftInput(this._android); + ad.showSoftInput(this.nativeView); } return result; } private _setInputType(inputType): void { - let nativeView = this._android; + let nativeView = this.nativeView; nativeView.setInputType(inputType); // setInputType will change the keyListener so we should cache it again @@ -193,15 +207,15 @@ export abstract class EditableTextBase extends EditableTextBaseCommon { } [textProperty.getDefault](): string { - return this._android.getText(); + return this.nativeView.getText(); } [textProperty.setNative](value: string) { const text = (value === null || value === undefined) ? '' : value.toString(); - this._android.setText(text, android.widget.TextView.BufferType.EDITABLE); + this.nativeView.setText(text, android.widget.TextView.BufferType.EDITABLE); } [keyboardTypeProperty.getDefault](): "datetime" | "phone" | "number" | "url" | "email" | string { - let inputType = this._android.getInputType(); + let inputType = this.nativeView.getInputType(); switch (inputType) { case android.text.InputType.TYPE_CLASS_DATETIME | android.text.InputType.TYPE_DATETIME_VARIATION_NORMAL: return "datetime"; @@ -259,7 +273,7 @@ export abstract class EditableTextBase extends EditableTextBaseCommon { } [returnKeyTypeProperty.getDefault](): "done" | "next" | "go" | "search" | "send" | string { - let ime = this._android.getImeOptions(); + let ime = this.nativeView.getImeOptions(); switch (ime) { case android.view.inputmethod.EditorInfo.IME_ACTION_DONE: return "done"; @@ -308,23 +322,23 @@ export abstract class EditableTextBase extends EditableTextBaseCommon { break; } - this._android.setImeOptions(newImeOptions); + this.nativeView.setImeOptions(newImeOptions); } [editableProperty.getDefault](): boolean { - return !!this._android.getKeyListener(); + return true; } [editableProperty.setNative](value: boolean) { if (value) { - this._android.setKeyListener(this._keyListenerCache); + this.nativeView.setKeyListener(this._keyListenerCache); } else { - this._android.setKeyListener(null); + this.nativeView.setKeyListener(null); } } [autocapitalizationTypeProperty.getDefault](): "none" | "words" | "sentences" | "allCharacters" | string { - let inputType = this._android.getInputType(); + let inputType = this.nativeView.getInputType(); if ((inputType & android.text.InputType.TYPE_TEXT_FLAG_CAP_WORDS) === android.text.InputType.TYPE_TEXT_FLAG_CAP_WORDS) { return "words"; } else if ((inputType & android.text.InputType.TYPE_TEXT_FLAG_CAP_SENTENCES) === android.text.InputType.TYPE_TEXT_FLAG_CAP_SENTENCES) { @@ -336,7 +350,7 @@ export abstract class EditableTextBase extends EditableTextBaseCommon { } } [autocapitalizationTypeProperty.setNative](value: string) { - let inputType = this._android.getInputType(); + let inputType = this.nativeView.getInputType(); inputType = inputType & ~28672; //28672 (0x00070000) 13,14,15bits (111 0000 0000 0000) switch (value) { @@ -367,7 +381,7 @@ export abstract class EditableTextBase extends EditableTextBaseCommon { } [autocorrectProperty.getDefault](): boolean { - let autocorrect = this._android.getInputType(); + let autocorrect = this.nativeView.getInputType(); if ((autocorrect & android.text.InputType.TYPE_TEXT_FLAG_AUTO_CORRECT) === android.text.InputType.TYPE_TEXT_FLAG_AUTO_CORRECT) { return true; } @@ -375,7 +389,7 @@ export abstract class EditableTextBase extends EditableTextBaseCommon { return false; } [autocorrectProperty.setNative](value: boolean) { - let inputType = this._android.getInputType(); + let inputType = this.nativeView.getInputType(); switch (value) { case true: inputType = inputType | android.text.InputType.TYPE_TEXT_FLAG_AUTO_COMPLETE; @@ -396,20 +410,20 @@ export abstract class EditableTextBase extends EditableTextBaseCommon { } [hintProperty.getDefault](): string { - return this._android.getHint(); + return this.nativeView.getHint(); } [hintProperty.setNative](value: string) { - this._android.setHint(value + ''); + this.nativeView.setHint(value + ''); } [placeholderColorProperty.getDefault](): android.content.res.ColorStateList { - return this._android.getHintTextColors(); + return this.nativeView.getHintTextColors(); } [placeholderColorProperty.setNative](value: Color | android.content.res.ColorStateList) { if (value instanceof Color) { - this._android.setHintTextColor(value.android); + this.nativeView.setHintTextColor(value.android); } else { - this._android.setHintTextColor(value); + this.nativeView.setHintTextColor(value); } } } diff --git a/tns-core-modules/ui/frame/activity.android.ts b/tns-core-modules/ui/frame/activity.android.ts index cb54ec50d..b1db531f0 100644 --- a/tns-core-modules/ui/frame/activity.android.ts +++ b/tns-core-modules/ui/frame/activity.android.ts @@ -1,4 +1,5 @@ import { setActivityCallbacks, AndroidActivityCallbacks } from "./frame"; +import * as appModule from "../../application"; @JavaProxy("com.tns.NativeScriptActivity") class NativeScriptActivity extends android.app.Activity { @@ -10,6 +11,8 @@ class NativeScriptActivity extends android.app.Activity { } protected onCreate(savedInstanceState: android.os.Bundle): void { + appModule.android.init(this.getApplication()); + // Set isNativeScriptActivity in onCreate. // The JS construcotr might not be called beacuse the activity is created from Andoird. this.isNativeScriptActivity = true; diff --git a/tns-core-modules/ui/frame/frame.android.ts b/tns-core-modules/ui/frame/frame.android.ts index 446d9a8de..5b01d2344 100644 --- a/tns-core-modules/ui/frame/frame.android.ts +++ b/tns-core-modules/ui/frame/frame.android.ts @@ -84,14 +84,11 @@ export class Frame extends FrameBase { private _android: AndroidFrame; private _delayedNavigationEntry: BackstackEntry; private _containerViewId: number = -1; - private _listener: android.view.View.OnAttachStateChangeListener; + // private _listener: android.view.View.OnAttachStateChangeListener; + constructor() { super(); this._android = new AndroidFrame(this); - this._listener = new android.view.View.OnAttachStateChangeListener({ - onViewAttachedToWindow: this.onNativeViewAttachedToWindow.bind(this), - onViewDetachedFromWindow: this.onNativeViewDetachedToWindow.bind(this) - }); } public static get defaultAnimatedNavigation(): boolean { @@ -116,10 +113,6 @@ export class Frame extends FrameBase { return this._android; } - get _nativeView(): any { - return this._android.rootViewGroup; - } - public _navigateCore(backstackEntry: BackstackEntry) { super._navigateCore(backstackEntry); @@ -297,28 +290,45 @@ export class Frame extends FrameBase { } public _createNativeView() { + // TODO: probably memory leak. + // this._listener = new android.view.View.OnAttachStateChangeListener({ + // onViewAttachedToWindow: this.onNativeViewAttachedToWindow.bind(this), + // onViewDetachedFromWindow: this.onNativeViewDetachedToWindow.bind(this) + // }); + const root = new org.nativescript.widgets.ContentLayout(this._context); if (this._containerViewId < 0) { this._containerViewId = android.view.View.generateViewId(); } - - this._android.rootViewGroup = root; - this._android.rootViewGroup.setId(this._containerViewId); - this._android.rootViewGroup.addOnAttachStateChangeListener(this._listener); return root; } - private onNativeViewAttachedToWindow(view: android.view.View): void { - if (this._delayedNavigationEntry) { - this._navigateCore(this._delayedNavigationEntry); - this._delayedNavigationEntry = undefined; - } + public _initNativeView(): void { + this._android.rootViewGroup = this.nativeView; + this._android.rootViewGroup.setId(this._containerViewId); + // this._android.rootViewGroup.addOnAttachStateChangeListener(this._listener); } - private onNativeViewDetachedToWindow(view: android.view.View): void { - // unused for the moment. + // public _resetNativeView() { + // this._android.rootViewGroup.removeOnAttachStateChangeListener(this._listener); + // } + + public _disposeNativeView() { + // we should keep the reference to underlying native object, since frame can contain many pages. + this._android.rootViewGroup = null; } + // private onNativeViewAttachedToWindow(view: android.view.View): void { + // if (this._delayedNavigationEntry) { + // this._navigateCore(this._delayedNavigationEntry); + // this._delayedNavigationEntry = undefined; + // } + // } + + // private onNativeViewDetachedToWindow(view: android.view.View): void { + // // unused for the moment. + // } + public _popFromFrameStack() { if (!this._isInFrameStack) { return; @@ -330,15 +340,6 @@ export class Frame extends FrameBase { } } - public _resetNativeView() { - this._android.rootViewGroup.removeOnAttachStateChangeListener(this._listener); - } - - public _disposeNativeView() { - // we should keep the reference to underlying native object, since frame can contain many pages. - this._android.rootViewGroup = null; - } - public _printNativeBackStack() { if (!this._android.activity) { return; @@ -721,7 +722,7 @@ class FragmentCallbacksImplementation implements AndroidFragmentCallbacks { return label; } - return page._nativeView; + return page.nativeView; } public onSaveInstanceState(fragment: android.app.Fragment, outState: android.os.Bundle, superFunc: Function): void { @@ -821,7 +822,7 @@ class ActivityCallbacksImplementation implements AndroidActivityCallbacks { // Initialize native visual tree; rootView._setupUI(activity); - activity.setContentView(rootView._nativeView, new org.nativescript.widgets.CommonLayoutParams()); + activity.setContentView(rootView.nativeView, new org.nativescript.widgets.CommonLayoutParams()); // frameId is negative w if (frame) { frame.navigate(navParam); diff --git a/tns-core-modules/ui/frame/frame.ios.ts b/tns-core-modules/ui/frame/frame.ios.ts index 646f24f09..067eb19ec 100644 --- a/tns-core-modules/ui/frame/frame.ios.ts +++ b/tns-core-modules/ui/frame/frame.ios.ts @@ -44,7 +44,8 @@ export class Frame extends FrameBase { constructor() { super(); this._ios = new iOSFrame(this); - + this.nativeView = this._ios.controller.view; + // When there is a 40px high "in-call" status bar, nobody moves the navigationBar top from 20 to 40 and it remains underneath the status bar. let frameRef = new WeakRef(this); application.ios.addNotificationObserver(UIApplicationDidChangeStatusBarFrameNotification, (notification: NSNotification) => { @@ -247,9 +248,9 @@ export class Frame extends FrameBase { return this._ios; } - get _nativeView(): any { - return this._ios.controller.view; - } + // get nativeView(): any { + // return this._ios.controller.view; + // } public static get defaultAnimatedNavigation(): boolean { return FrameBase.defaultAnimatedNavigation; @@ -268,7 +269,7 @@ export class Frame extends FrameBase { public requestLayout(): void { super.requestLayout(); // Invalidate our Window so that layout is triggered again. - let window = this._nativeView.window; + let window = this.nativeView.window; if (window) { window.setNeedsLayout(); } @@ -356,7 +357,7 @@ export class Frame extends FrameBase { public remeasureFrame(): void { this.requestLayout(); - let window: UIWindow = this._nativeView.window; + let window: UIWindow = this.nativeView.window; if (window) { window.layoutIfNeeded(); } diff --git a/tns-core-modules/ui/gestures/gestures.ios.ts b/tns-core-modules/ui/gestures/gestures.ios.ts index 9208db0dc..9bf15a520 100644 --- a/tns-core-modules/ui/gestures/gestures.ios.ts +++ b/tns-core-modules/ui/gestures/gestures.ios.ts @@ -107,8 +107,8 @@ export class GesturesObserver extends GesturesObserverBase { private _attach(target: View, type: GestureTypes) { this._detach(); - if (target && target._nativeView && target._nativeView.addGestureRecognizer) { - let nativeView = target._nativeView; + if (target && target.nativeView && target.nativeView.addGestureRecognizer) { + let nativeView = target.nativeView; if (type & GestureTypes.tap) { nativeView.addGestureRecognizer(this._createRecognizer(GestureTypes.tap)); @@ -129,7 +129,7 @@ export class GesturesObserver extends GesturesObserverBase { if (type & GestureTypes.pan) { nativeView.addGestureRecognizer(this._createRecognizer(GestureTypes.pan, args => { - this._executeCallback(_getPanData(args, target._nativeView)); + this._executeCallback(_getPanData(args, target.nativeView)); })); } @@ -168,11 +168,11 @@ export class GesturesObserver extends GesturesObserverBase { } private _detach() { - if (this.target && this.target._nativeView) { + if (this.target && this.target.nativeView) { for (let name in this._recognizers) { if (this._recognizers.hasOwnProperty(name)) { let item = this._recognizers[name]; - this.target._nativeView.removeGestureRecognizer(item.recognizer); + this.target.nativeView.removeGestureRecognizer(item.recognizer); item.recognizer = null; item.target = null; @@ -288,7 +288,7 @@ function _getSwipeDirection(direction: UISwipeGestureRecognizerDirection): Swipe function _getPinchData(args: GestureEventData): PinchGestureEventData { let recognizer = args.ios; - let center = recognizer.locationInView(args.view._nativeView); + let center = recognizer.locationInView(args.view.nativeView); return { type: args.type, @@ -386,7 +386,7 @@ class Pointer implements Pointer { private _location: CGPoint; private get location(): CGPoint { if (!this._location) { - this._location = this.ios.locationInView(this._view._nativeView); + this._location = this.ios.locationInView(this._view.nativeView); } return this._location; @@ -470,10 +470,10 @@ class TouchGestureEventData implements TouchGestureEventData { } getX(): number { - return this.getMainPointer().locationInView(this.view._nativeView).x; + return this.getMainPointer().locationInView(this.view.nativeView).x; } getY(): number { - return this.getMainPointer().locationInView(this.view._nativeView).y + return this.getMainPointer().locationInView(this.view.nativeView).y } -} +} \ No newline at end of file diff --git a/tns-core-modules/ui/html-view/html-view-common.ts b/tns-core-modules/ui/html-view/html-view-common.ts index c7a0d8b49..5f1b9ae20 100644 --- a/tns-core-modules/ui/html-view/html-view-common.ts +++ b/tns-core-modules/ui/html-view/html-view-common.ts @@ -7,6 +7,8 @@ export class HtmlViewBase extends View implements HtmlViewDefinition { public html: string; } +HtmlViewBase.prototype.recycleNativeView = true; + // TODO: Can we use Label.ios optimization for affectsLayout??? export const htmlProperty = new Property({ name: "html", defaultValue: "", affectsLayout: true }); -htmlProperty.register(HtmlViewBase); +htmlProperty.register(HtmlViewBase); \ No newline at end of file diff --git a/tns-core-modules/ui/html-view/html-view.android.ts b/tns-core-modules/ui/html-view/html-view.android.ts index f7d50fb24..3c83257d8 100644 --- a/tns-core-modules/ui/html-view/html-view.android.ts +++ b/tns-core-modules/ui/html-view/html-view.android.ts @@ -5,14 +5,10 @@ export * from "./html-view-common"; export class HtmlView extends HtmlViewBase { - private _android: android.widget.TextView; - - get android(): android.widget.TextView { - return this._android; - } + nativeView: android.widget.TextView; public _createNativeView() { - const textView = this._android = new android.widget.TextView(this._context); + const textView = new android.widget.TextView(this._context); // This makes the html work textView.setLinksClickable(true); textView.setMovementMethod(android.text.method.LinkMovementMethod.getInstance()); @@ -30,7 +26,7 @@ export class HtmlView extends HtmlViewBase { if (value.search(/= 0) { mask = 0; } - this._android.setAutoLinkMask(mask); - this._android.setText(android.text.Html.fromHtml(value)); + this.nativeView.setAutoLinkMask(mask); + this.nativeView.setText(android.text.Html.fromHtml(value)); } } \ No newline at end of file diff --git a/tns-core-modules/ui/html-view/html-view.ios.ts b/tns-core-modules/ui/html-view/html-view.ios.ts index af592c314..2439a2309 100644 --- a/tns-core-modules/ui/html-view/html-view.ios.ts +++ b/tns-core-modules/ui/html-view/html-view.ios.ts @@ -9,7 +9,7 @@ export class HtmlView extends HtmlViewBase { constructor() { super(); - this._ios = UITextView.new(); + this.nativeView = this._ios = UITextView.new(); this._ios.scrollEnabled = false; this._ios.editable = false; @@ -22,12 +22,12 @@ export class HtmlView extends HtmlViewBase { return this._ios; } - get _nativeView(): UITextView { - return this._ios; - } + // get nativeView(): UITextView { + // return this._ios; + // } public onMeasure(widthMeasureSpec: number, heightMeasureSpec: number): void { - var nativeView = this._nativeView; + var nativeView = this._ios; if (nativeView) { const width = layout.getMeasureSpecSize(widthMeasureSpec); const widthMode = layout.getMeasureSpecMode(widthMeasureSpec); diff --git a/tns-core-modules/ui/image/image-common.ts b/tns-core-modules/ui/image/image-common.ts index 29f391165..c88295e1c 100644 --- a/tns-core-modules/ui/image/image-common.ts +++ b/tns-core-modules/ui/image/image-common.ts @@ -103,6 +103,8 @@ export abstract class ImageBase extends View implements ImageDefinition { } } +ImageBase.prototype.recycleNativeView = true; + export const imageSourceProperty = new Property({ name: "imageSource" }); imageSourceProperty.register(ImageBase); diff --git a/tns-core-modules/ui/image/image.android.ts b/tns-core-modules/ui/image/image.android.ts index 5bfcfcc5c..e9934fb13 100644 --- a/tns-core-modules/ui/image/image.android.ts +++ b/tns-core-modules/ui/image/image.android.ts @@ -47,124 +47,6 @@ export function initImageCache(context: android.content.Context, mode = CacheMod imageFetcher.initCache(); } -export class Image extends ImageBase { - private _android: org.nativescript.widgets.ImageView; - private _imageLoadedListener: org.nativescript.widgets.image.Worker.OnImageLoadedListener; - - public decodeWidth = 0; - public decodeHeight = 0; - public useCache = true; - - get android(): org.nativescript.widgets.ImageView { - return this._android; - } - - public _createNativeView() { - initializeImageLoadedListener(); - if (!imageFetcher) { - initImageCache(this._context); - } - - const imageView = this._android = new org.nativescript.widgets.ImageView(this._context); - return imageView; - } - - public _createImageSourceFromSrc() { - let imageView = this._android; - this.imageSource = unsetValue; - - if (!imageView || !this.src) { - return; - } - - let value = this.src; - let async = this.loadMode === ASYNC; - this._imageLoadedListener = this._imageLoadedListener || new ImageLoadedListener(this); - - if (typeof value === "string") { - value = value.trim(); - this.isLoading = true; - - if (isDataURI(value)) { - // TODO: Check with runtime what should we do in case of base64 string. - super._createImageSourceFromSrc(); - } - else if (isFileOrResourcePath(value)) { - if (value.indexOf(RESOURCE_PREFIX) === 0) { - imageView.setUri(value, this.decodeWidth, this.decodeHeight, this.useCache, async, this._imageLoadedListener); - } - else { - let fileName = value; - if (fileName.indexOf("~/") === 0) { - fileName = path.join(knownFolders.currentApp().path, fileName.replace("~/", "")); - } - - imageView.setUri(FILE_PREFIX + fileName, this.decodeWidth, this.decodeHeight, this.useCache, async, this._imageLoadedListener); - } - } - else { - // For backwards compatibility http always use async loading. - imageView.setUri(value, this.decodeWidth, this.decodeHeight, this.useCache, true, this._imageLoadedListener); - } - } else { - super._createImageSourceFromSrc(); - } - } - - [stretchProperty.getDefault](): "aspectFit" { - return "aspectFit"; - } - [stretchProperty.setNative](value: "none" | "aspectFill" | "aspectFit" | "fill") { - switch (value) { - case "aspectFit": - this.android.setScaleType(android.widget.ImageView.ScaleType.FIT_CENTER); - break; - case "aspectFill": - this.android.setScaleType(android.widget.ImageView.ScaleType.CENTER_CROP); - break; - case "fill": - this.android.setScaleType(android.widget.ImageView.ScaleType.FIT_XY); - break; - case "none": - default: - this.android.setScaleType(android.widget.ImageView.ScaleType.MATRIX); - break; - } - } - - [tintColorProperty.getDefault](): Color { - return undefined; - } - [tintColorProperty.setNative](value: Color) { - if (value === undefined) { - this._android.clearColorFilter(); - } else { - this._android.setColorFilter(value.android); - } - } - - [imageSourceProperty.getDefault](): ImageSource { - return undefined; - } - [imageSourceProperty.setNative](value: ImageSource) { - if (value && value.android) { - let rotation = value.rotationAngle ? value.rotationAngle : 0; - this.android.setRotationAngle(rotation); - this.android.setImageBitmap(value.android); - } else { - this.android.setRotationAngle(0); - this.android.setImageBitmap(null); - } - } - - [srcProperty.getDefault](): any { - return undefined; - } - [srcProperty.setNative](value: any) { - this._createImageSourceFromSrc(); - } -} - interface ImageLoadedListener { new (owner: Image): org.nativescript.widgets.image.Worker.OnImageLoadedListener; } @@ -177,15 +59,143 @@ function initializeImageLoadedListener() { @Interfaces([org.nativescript.widgets.image.Worker.OnImageLoadedListener]) class ImageLoadedListenerImpl extends java.lang.Object implements org.nativescript.widgets.image.Worker.OnImageLoadedListener { - constructor(private owner: Image) { + constructor(public owner: Image) { super(); return global.__native(this); } onImageLoaded(success: boolean): void { - this.owner.isLoading = false; + const owner = this.owner; + if (owner) { + owner.isLoading = false; + } } } ImageLoadedListener = ImageLoadedListenerImpl; } + +export class Image extends ImageBase { + nativeView: org.nativescript.widgets.ImageView; + + public decodeWidth = 0; + public decodeHeight = 0; + public useCache = true; + + public _createNativeView() { + initializeImageLoadedListener(); + if (!imageFetcher) { + initImageCache(this._context); + } + + const imageView = new org.nativescript.widgets.ImageView(this._context); + const listener = new ImageLoadedListener(this); + imageView.setImageLoadedListener(listener); + (imageView).listener = listener; + + return imageView; + } + + public _initNativeView(): void { + (this.nativeView).listener.owner = this; + } + + public _disposeNativeView() { + (this.nativeView).listener.owner = null; + } + + public _createImageSourceFromSrc() { + let imageView = this.nativeView; + this.imageSource = unsetValue; + + if (!imageView || !this.src) { + return; + } + + let value = this.src; + let async = this.loadMode === ASYNC; + + if (typeof value === "string") { + value = value.trim(); + this.isLoading = true; + + if (isDataURI(value)) { + // TODO: Check with runtime what should we do in case of base64 string. + super._createImageSourceFromSrc(); + } + else if (isFileOrResourcePath(value)) { + if (value.indexOf(RESOURCE_PREFIX) === 0) { + imageView.setUri(value, this.decodeWidth, this.decodeHeight, this.useCache, async); + } + else { + let fileName = value; + if (fileName.indexOf("~/") === 0) { + fileName = path.join(knownFolders.currentApp().path, fileName.replace("~/", "")); + } + + imageView.setUri(FILE_PREFIX + fileName, this.decodeWidth, this.decodeHeight, this.useCache, async); + } + } + else { + // For backwards compatibility http always use async loading. + imageView.setUri(value, this.decodeWidth, this.decodeHeight, this.useCache, true); + } + } else { + super._createImageSourceFromSrc(); + } + } + + [stretchProperty.getDefault](): "aspectFit" { + return "aspectFit"; + } + [stretchProperty.setNative](value: "none" | "aspectFill" | "aspectFit" | "fill") { + switch (value) { + case "aspectFit": + this.nativeView.setScaleType(android.widget.ImageView.ScaleType.FIT_CENTER); + break; + case "aspectFill": + this.nativeView.setScaleType(android.widget.ImageView.ScaleType.CENTER_CROP); + break; + case "fill": + this.nativeView.setScaleType(android.widget.ImageView.ScaleType.FIT_XY); + break; + case "none": + default: + this.nativeView.setScaleType(android.widget.ImageView.ScaleType.MATRIX); + break; + } + } + + [tintColorProperty.getDefault](): Color { + return undefined; + } + [tintColorProperty.setNative](value: Color) { + if (value === undefined) { + this.nativeView.clearColorFilter(); + } else { + this.nativeView.setColorFilter(value.android); + } + } + + [imageSourceProperty.getDefault](): ImageSource { + return undefined; + } + [imageSourceProperty.setNative](value: ImageSource) { + const nativeView = this.nativeView; + if (value && value.android) { + const rotation = value.rotationAngle ? value.rotationAngle : 0; + nativeView.setRotationAngle(rotation); + nativeView.setImageBitmap(value.android); + } else { + nativeView.setRotationAngle(0); + nativeView.setImageBitmap(null); + } + } + + [srcProperty.getDefault](): any { + return undefined; + } + [srcProperty.setNative](value: any) { + this._createImageSourceFromSrc(); + } +} diff --git a/tns-core-modules/ui/image/image.ios.ts b/tns-core-modules/ui/image/image.ios.ts index 4ce45bf07..d07289dc9 100644 --- a/tns-core-modules/ui/image/image.ios.ts +++ b/tns-core-modules/ui/image/image.ios.ts @@ -14,7 +14,7 @@ export class Image extends ImageBase { super(); //TODO: Think of unified way of setting all the default values. - this._ios = UIImageView.new(); + this.nativeView = this._ios = UIImageView.new(); this._ios.contentMode = UIViewContentMode.ScaleAspectFit; this._ios.userInteractionEnabled = true; this._setNativeClipToBounds(); diff --git a/tns-core-modules/ui/label/label.android.ts b/tns-core-modules/ui/label/label.android.ts index d1696e172..c09514537 100644 --- a/tns-core-modules/ui/label/label.android.ts +++ b/tns-core-modules/ui/label/label.android.ts @@ -4,7 +4,7 @@ import { TextBase, WhiteSpace } from "../text-base"; export * from "../text-base"; export class Label extends TextBase implements LabelDefinition { - private _android: android.widget.TextView; + nativeView: android.widget.TextView; get textWrap(): boolean { return this.style.whiteSpace === WhiteSpace.NORMAL; @@ -13,14 +13,18 @@ export class Label extends TextBase implements LabelDefinition { this.style.whiteSpace = value ? WhiteSpace.NORMAL : WhiteSpace.NO_WRAP; } - get android(): android.widget.TextView { - return this._android; - } - public _createNativeView() { - const textView = this._android = new android.widget.TextView(this._context); + const textView = new android.widget.TextView(this._context); textView.setSingleLine(true); textView.setEllipsize(android.text.TextUtils.TruncateAt.END); return textView; } + + public _initNativeView(): void { + const textView = this.nativeView; + textView.setSingleLine(true); + // textView.setEllipsize(android.text.TextUtils.TruncateAt.END); + } } + +Label.prototype.recycleNativeView = true; \ No newline at end of file diff --git a/tns-core-modules/ui/label/label.ios.ts b/tns-core-modules/ui/label/label.ios.ts index a26725895..30eaf2a22 100644 --- a/tns-core-modules/ui/label/label.ios.ts +++ b/tns-core-modules/ui/label/label.ios.ts @@ -38,10 +38,6 @@ export class Label extends TextBase implements LabelDefinition { return this.nativeView; } - get _nativeView(): TNSLabel { - return this.nativeView; - } - get textWrap(): boolean { return this.style.whiteSpace === WhiteSpace.NORMAL; } @@ -237,3 +233,5 @@ export class Label extends TextBase implements LabelDefinition { }; } } + +Label.prototype.recycleNativeView = true; \ No newline at end of file diff --git a/tns-core-modules/ui/layouts/absolute-layout/absolute-layout-common.ts b/tns-core-modules/ui/layouts/absolute-layout/absolute-layout-common.ts index 634849164..aad2dc96c 100644 --- a/tns-core-modules/ui/layouts/absolute-layout/absolute-layout-common.ts +++ b/tns-core-modules/ui/layouts/absolute-layout/absolute-layout-common.ts @@ -43,6 +43,8 @@ export class AbsoluteLayoutBase extends LayoutBase implements AbsoluteLayoutDefi } } +AbsoluteLayoutBase.prototype.recycleNativeView = true; + export const leftProperty = new Property({ name: "left", defaultValue: zeroLength, valueChanged: (target, oldValue, newValue) => { @@ -65,4 +67,4 @@ export const topProperty = new Property({ } }, valueConverter: (v) => Length.parse(v) }); -topProperty.register(View); +topProperty.register(View); \ No newline at end of file diff --git a/tns-core-modules/ui/layouts/absolute-layout/absolute-layout.android.ts b/tns-core-modules/ui/layouts/absolute-layout/absolute-layout.android.ts index 00990ca9c..67e9ee8e7 100644 --- a/tns-core-modules/ui/layouts/absolute-layout/absolute-layout.android.ts +++ b/tns-core-modules/ui/layouts/absolute-layout/absolute-layout.android.ts @@ -1,10 +1,10 @@ -import { AbsoluteLayoutBase, View, leftProperty, topProperty } from "./absolute-layout-common"; +import { AbsoluteLayoutBase, View, leftProperty, topProperty, Length } from "./absolute-layout-common"; export * from "./absolute-layout-common"; function makeNativeSetter(setter: (this: View, lp: org.nativescript.widgets.CommonLayoutParams, value: T) => void) { return function(this: View, value: T) { - const nativeView: android.view.View = this._nativeView; + const nativeView: android.view.View = this.nativeView; const lp = nativeView.getLayoutParams() || new org.nativescript.widgets.CommonLayoutParams(); if (lp instanceof org.nativescript.widgets.CommonLayoutParams) { setter.call(this, lp, value); @@ -13,23 +13,13 @@ function makeNativeSetter(setter: (this: View, lp: org.nativescript.widgets.C } } -View.prototype[topProperty.setNative] = makeNativeSetter(function(this: View, lp, value) { lp.top = this.effectiveTop }); -View.prototype[leftProperty.setNative] = makeNativeSetter(function(this: View, lp, value) { lp.left = this.effectiveLeft }); +View.prototype[topProperty.setNative] = makeNativeSetter(function(this: View, lp, value) { lp.top = Length.toDevicePixels(value, 0) }); +View.prototype[leftProperty.setNative] = makeNativeSetter(function(this: View, lp, value) { lp.left = Length.toDevicePixels(value, 0) }); export class AbsoluteLayout extends AbsoluteLayoutBase { - - private _layout: org.nativescript.widgets.AbsoluteLayout; - - get android(): org.nativescript.widgets.AbsoluteLayout { - return this._layout; - } - - get _nativeView(): org.nativescript.widgets.AbsoluteLayout { - return this._layout; - } + nativeView: org.nativescript.widgets.AbsoluteLayout; public _createNativeView() { - const layout = this._layout = new org.nativescript.widgets.AbsoluteLayout(this._context); - return layout; + return new org.nativescript.widgets.AbsoluteLayout(this._context); } -} \ No newline at end of file +} diff --git a/tns-core-modules/ui/layouts/dock-layout/dock-layout-common.ts b/tns-core-modules/ui/layouts/dock-layout/dock-layout-common.ts index 136cdfdb7..d90ce6c30 100644 --- a/tns-core-modules/ui/layouts/dock-layout/dock-layout-common.ts +++ b/tns-core-modules/ui/layouts/dock-layout/dock-layout-common.ts @@ -27,6 +27,8 @@ export class DockLayoutBase extends LayoutBase implements DockLayoutDefinition { } } +DockLayoutBase.prototype.recycleNativeView = true; + export const dockProperty = new Property({ name: "dock", defaultValue: "left", valueChanged: (target, oldValue, newValue) => { if (target instanceof View) { diff --git a/tns-core-modules/ui/layouts/dock-layout/dock-layout.android.ts b/tns-core-modules/ui/layouts/dock-layout/dock-layout.android.ts index d11a2b067..3a6efaed3 100644 --- a/tns-core-modules/ui/layouts/dock-layout/dock-layout.android.ts +++ b/tns-core-modules/ui/layouts/dock-layout/dock-layout.android.ts @@ -3,7 +3,7 @@ export * from "./dock-layout-common"; View.prototype[dockProperty.setNative] = function(this: View, value: "left" | "top" | "right" | "bottom") { - const nativeView: android.view.View = this._nativeView; + const nativeView: android.view.View = this.nativeView; const lp = nativeView.getLayoutParams() || new org.nativescript.widgets.CommonLayoutParams(); if (lp instanceof org.nativescript.widgets.CommonLayoutParams) { switch (value) { @@ -32,26 +32,16 @@ View.prototype[dockProperty.setNative] = function(this: View, value: "left" | "t } export class DockLayout extends DockLayoutBase { - - private _layout: org.nativescript.widgets.DockLayout; - - get android(): org.nativescript.widgets.DockLayout { - return this._layout; - } - - get _nativeView(): org.nativescript.widgets.DockLayout { - return this._layout; - } + nativeView: org.nativescript.widgets.DockLayout; public _createNativeView() { - const layout = this._layout = new org.nativescript.widgets.DockLayout(this._context); - return layout; + return new org.nativescript.widgets.DockLayout(this._context); } [stretchLastChildProperty.getDefault](): boolean { - return false; + return true; } [stretchLastChildProperty.setNative](value: boolean) { - this._layout.setStretchLastChild(value); + this.nativeView.setStretchLastChild(value); } -} \ No newline at end of file +} diff --git a/tns-core-modules/ui/layouts/flexbox-layout/flexbox-layout-common.ts b/tns-core-modules/ui/layouts/flexbox-layout/flexbox-layout-common.ts index 4fd067520..f912d7aab 100644 --- a/tns-core-modules/ui/layouts/flexbox-layout/flexbox-layout-common.ts +++ b/tns-core-modules/ui/layouts/flexbox-layout/flexbox-layout-common.ts @@ -209,6 +209,8 @@ export abstract class FlexboxLayoutBase extends LayoutBase { } } +FlexboxLayoutBase.prototype.recycleNativeView = true; + export const flexDirectionProperty = new CssProperty({ name: "flexDirection", cssName: "flex-direction", defaultValue: FlexDirection.ROW, affectsLayout: isIOS, valueConverter: FlexDirection.parse }); flexDirectionProperty.register(Style); diff --git a/tns-core-modules/ui/layouts/flexbox-layout/flexbox-layout.android.ts b/tns-core-modules/ui/layouts/flexbox-layout/flexbox-layout.android.ts index 485b9b4f1..143502dca 100644 --- a/tns-core-modules/ui/layouts/flexbox-layout/flexbox-layout.android.ts +++ b/tns-core-modules/ui/layouts/flexbox-layout/flexbox-layout.android.ts @@ -14,7 +14,7 @@ export * from "./flexbox-layout-common"; function makeNativeSetter(setter: (lp: org.nativescript.widgets.FlexboxLayout.LayoutParams, value: T) => void) { return function(this: View, value: T) { - const nativeView: android.view.View = this._nativeView; + const nativeView: android.view.View = this.nativeView; const lp = nativeView.getLayoutParams() || new org.nativescript.widgets.FlexboxLayout.LayoutParams(); if (lp instanceof org.nativescript.widgets.FlexboxLayout.LayoutParams) { setter(lp, value); @@ -77,53 +77,49 @@ const alignSelfMap = { } export class FlexboxLayout extends FlexboxLayoutBase { - private _layout: org.nativescript.widgets.FlexboxLayout; - - constructor() { - super(); - } - - get android(): org.nativescript.widgets.FlexboxLayout { return this._layout; } - get _nativeView(): org.nativescript.widgets.FlexboxLayout { return this._layout; } + nativeView: org.nativescript.widgets.FlexboxLayout; public _createNativeView() { - const layout = this._layout = new org.nativescript.widgets.FlexboxLayout(this._context); - return layout; + return new org.nativescript.widgets.FlexboxLayout(this._context); + } + + public _disposeNativeView() { + (this.nativeView).invalidateOrdersCache(); } [flexDirectionProperty.getDefault](): FlexDirection { return flexDirectionProperty.defaultValue; } [flexDirectionProperty.setNative](flexDirection: FlexDirection) { - this.android.setFlexDirection(flexDirectionMap[flexDirection]); + this.nativeView.setFlexDirection(flexDirectionMap[flexDirection]); } [flexWrapProperty.getDefault](): FlexWrap { return flexWrapProperty.defaultValue; } [flexWrapProperty.setNative](flexWrap: FlexWrap) { - this.android.setFlexWrap(flexWrapMap[flexWrap]); + this.nativeView.setFlexWrap(flexWrapMap[flexWrap]); } [justifyContentProperty.getDefault](): JustifyContent { return justifyContentProperty.defaultValue; } [justifyContentProperty.setNative](justifyContent: JustifyContent) { - this.android.setJustifyContent(justifyContentMap[justifyContent]); + this.nativeView.setJustifyContent(justifyContentMap[justifyContent]); } [alignItemsProperty.getDefault](): AlignItems { return alignItemsProperty.defaultValue; } [alignItemsProperty.setNative](alignItems: AlignItems) { - this.android.setAlignItems(alignItemsMap[alignItems]); + this.nativeView.setAlignItems(alignItemsMap[alignItems]); } [alignContentProperty.getDefault](): AlignContent { return alignContentProperty.defaultValue; } [alignContentProperty.setNative](alignContent: AlignContent) { - this.android.setAlignContent(alignContentMap[alignContent]); + this.nativeView.setAlignContent(alignContentMap[alignContent]); } public _updateNativeLayoutParams(child: View): void { diff --git a/tns-core-modules/ui/layouts/grid-layout/grid-layout-common.ts b/tns-core-modules/ui/layouts/grid-layout/grid-layout-common.ts index 6de9ea9bd..ce22a9726 100644 --- a/tns-core-modules/ui/layouts/grid-layout/grid-layout-common.ts +++ b/tns-core-modules/ui/layouts/grid-layout/grid-layout-common.ts @@ -305,6 +305,8 @@ export class GridLayoutBase extends LayoutBase implements GridLayoutDefinition { } } +GridLayoutBase.prototype.recycleNativeView = true; + export const columnProperty = new Property({ name: "col", defaultValue: 0, valueChanged: (target, oldValue, newValue) => { diff --git a/tns-core-modules/ui/layouts/grid-layout/grid-layout.android.ts b/tns-core-modules/ui/layouts/grid-layout/grid-layout.android.ts index 06409f663..396d326e8 100644 --- a/tns-core-modules/ui/layouts/grid-layout/grid-layout.android.ts +++ b/tns-core-modules/ui/layouts/grid-layout/grid-layout.android.ts @@ -7,7 +7,7 @@ export * from "./grid-layout-common"; function makeNativeSetter(setter: (lp: org.nativescript.widgets.CommonLayoutParams, value: T) => void) { return function(this: View, value: T) { - const nativeView: android.view.View = this._nativeView; + const nativeView: android.view.View = this.nativeView; const lp = nativeView.getLayoutParams() || new org.nativescript.widgets.CommonLayoutParams(); if (lp instanceof org.nativescript.widgets.CommonLayoutParams) { setter(lp, value); @@ -50,57 +50,62 @@ export class ItemSpec extends ItemSpecBase { } export class GridLayout extends GridLayoutBase { - - private _layout: org.nativescript.widgets.GridLayout; - - get android(): org.nativescript.widgets.GridLayout { - return this._layout; - } - - get _nativeView(): org.nativescript.widgets.GridLayout { - return this._layout; - } + nativeView: org.nativescript.widgets.GridLayout; public _createNativeView() { - const layout = this._layout = new org.nativescript.widgets.GridLayout(this._context); + return new org.nativescript.widgets.GridLayout(this._context); + } + public _initNativeView(): void { // Update native GridLayout - this.getRows().forEach((itemSpec: ItemSpec, index, rows) => { this._onRowAdded(itemSpec); }, this); - this.getColumns().forEach((itemSpec: ItemSpec, index, rows) => { this._onColumnAdded(itemSpec); }, this); - return layout; + this.rowsInternal.forEach((itemSpec: ItemSpec, index, rows) => { this._onRowAdded(itemSpec); }, this); + this.columnsInternal.forEach((itemSpec: ItemSpec, index, rows) => { this._onColumnAdded(itemSpec); }, this); + } + + public _disposeNativeView() { + // 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); + } } public _onRowAdded(itemSpec: ItemSpec) { - if (this._layout) { + if (this.nativeView) { const nativeSpec = createNativeSpec(itemSpec); itemSpec.nativeSpec = nativeSpec; - this._layout.addRow(nativeSpec); + this.nativeView.addRow(nativeSpec); } } public _onColumnAdded(itemSpec: ItemSpec) { - if (this._layout) { + if (this.nativeView) { const nativeSpec = createNativeSpec(itemSpec); itemSpec.nativeSpec = nativeSpec; - this._layout.addColumn(nativeSpec); + this.nativeView.addColumn(nativeSpec); } } public _onRowRemoved(itemSpec: ItemSpec, index: number) { itemSpec.nativeSpec = null; - if (this._layout) { - this._layout.removeRowAt(index); + if (this.nativeView) { + this.nativeView.removeRowAt(index); } } public _onColumnRemoved(itemSpec: ItemSpec, index: number) { itemSpec.nativeSpec = null; - if (this._layout) { - this._layout.removeColumnAt(index); + if (this.nativeView) { + this.nativeView.removeColumnAt(index); } } protected invalidate(): void { // No need to request layout for android because it will be done in the native call. } -} \ No newline at end of file +} diff --git a/tns-core-modules/ui/layouts/layout-base.ios.ts b/tns-core-modules/ui/layouts/layout-base.ios.ts index d15dbdcce..9f7f7bf07 100644 --- a/tns-core-modules/ui/layouts/layout-base.ios.ts +++ b/tns-core-modules/ui/layouts/layout-base.ios.ts @@ -3,6 +3,7 @@ export * from "./layout-base-common"; export class LayoutBase extends LayoutBaseCommon { + nativeView: UIView; [clipToBoundsProperty.getDefault](): boolean { return false; @@ -13,7 +14,7 @@ export class LayoutBase extends LayoutBaseCommon { _setNativeClipToBounds() { if (this.clipToBounds) { - this._nativeView.clipsToBounds = true; + this.nativeView.clipsToBounds = true; } else { super._setNativeClipToBounds(); } diff --git a/tns-core-modules/ui/layouts/layout.android.ts b/tns-core-modules/ui/layouts/layout.android.ts index d98470342..46e5bfb42 100644 --- a/tns-core-modules/ui/layouts/layout.android.ts +++ b/tns-core-modules/ui/layouts/layout.android.ts @@ -37,34 +37,28 @@ function initializeNativeViewGroup() { } export class Layout extends LayoutBase implements LayoutDefinition { - private _viewGroup: android.view.ViewGroup; + nativeView: android.view.ViewGroup; _measuredWidth: number; _measuredHeight: number; - get android(): android.view.ViewGroup { - return this._viewGroup; - } - - get _nativeView(): android.view.ViewGroup { - return this._viewGroup; - } - public _createNativeView() { initializeNativeViewGroup(); - const layout = this._viewGroup = new NativeViewGroup(this._context); - this._viewGroup[OWNER] = this; - return layout; + return new NativeViewGroup(this._context); + } + + public _initNativeView(): void { + (this.nativeView)[OWNER] = this; } public _disposeNativeView() { - delete this._viewGroup[OWNER]; + (this.nativeView)[OWNER] = undefined; super._disposeNativeView(); } public measure(widthMeasureSpec: number, heightMeasureSpec: number): void { this._setCurrentMeasureSpecs(widthMeasureSpec, heightMeasureSpec); - const view = this._nativeView; + const view = this.nativeView; if (view) { if (traceEnabled()) { traceWrite(`${this} :measure: ${layout.measureSpecToString(widthMeasureSpec)}, ${layout.measureSpecToString(heightMeasureSpec)}`, traceCategories.Layout); @@ -76,7 +70,7 @@ export class Layout extends LayoutBase implements LayoutDefinition { public layout(left: number, top: number, right: number, bottom: number): void { this._setCurrentLayoutBounds(left, top, right, bottom); - var view = this._nativeView; + var view = this.nativeView; if (view) { this.layoutNativeView(left, top, right, bottom); if (traceEnabled()) { @@ -111,4 +105,4 @@ export class Layout extends LayoutBase implements LayoutDefinition { public getMeasuredHeight(): number { return this._measuredHeight; } -} +} \ No newline at end of file diff --git a/tns-core-modules/ui/layouts/layout.ios.ts b/tns-core-modules/ui/layouts/layout.ios.ts index 650579f32..fac50746b 100644 --- a/tns-core-modules/ui/layouts/layout.ios.ts +++ b/tns-core-modules/ui/layouts/layout.ios.ts @@ -3,19 +3,15 @@ import { LayoutBase } from "./layout-base"; export * from "./layout-base"; export class Layout extends LayoutBase implements LayoutDefinition { - private _view: UIView; + nativeView: UIView; constructor() { super(); - this._view = UIView.new(); + this.nativeView = UIView.new(); } get ios(): UIView { - return this._view; - } - - get _nativeView(): UIView { - return this._view; + return this.nativeView; } public onMeasure(widthMeasureSpec: number, heightMeasureSpec: number): void { diff --git a/tns-core-modules/ui/layouts/stack-layout/stack-layout-common.ts b/tns-core-modules/ui/layouts/stack-layout/stack-layout-common.ts index a49649ebf..8cdd6e9bb 100644 --- a/tns-core-modules/ui/layouts/stack-layout/stack-layout-common.ts +++ b/tns-core-modules/ui/layouts/stack-layout/stack-layout-common.ts @@ -7,6 +7,8 @@ export class StackLayoutBase extends LayoutBase implements StackLayoutDefinition public orientation: "horizontal" | "vertical"; } +StackLayoutBase.prototype.recycleNativeView = true; + export const orientationProperty = new Property({ name: "orientation", defaultValue: "vertical", affectsLayout: isIOS, valueConverter: (v) => { diff --git a/tns-core-modules/ui/layouts/stack-layout/stack-layout.android.ts b/tns-core-modules/ui/layouts/stack-layout/stack-layout.android.ts index a421bf4bc..0663743a6 100644 --- a/tns-core-modules/ui/layouts/stack-layout/stack-layout.android.ts +++ b/tns-core-modules/ui/layouts/stack-layout/stack-layout.android.ts @@ -3,25 +3,13 @@ export * from "./stack-layout-common"; export class StackLayout extends StackLayoutBase { - private _layout: org.nativescript.widgets.StackLayout; - - get android(): org.nativescript.widgets.StackLayout { - return this._layout; - } - - get _nativeView(): org.nativescript.widgets.StackLayout { - return this._layout; - } + nativeView: org.nativescript.widgets.StackLayout; public _createNativeView() { - const layout = this._layout = new org.nativescript.widgets.StackLayout(this._context); - return layout; + return new org.nativescript.widgets.StackLayout(this._context); } - [orientationProperty.getDefault](): "horizontal" | "vertical" { - return "vertical"; - } [orientationProperty.setNative](value: "horizontal" | "vertical") { - this._layout.setOrientation(value === "vertical" ? org.nativescript.widgets.Orientation.vertical : org.nativescript.widgets.Orientation.horizontal) + this.nativeView.setOrientation(value === "vertical" ? org.nativescript.widgets.Orientation.vertical : org.nativescript.widgets.Orientation.horizontal) } } \ No newline at end of file diff --git a/tns-core-modules/ui/layouts/wrap-layout/wrap-layout-common.ts b/tns-core-modules/ui/layouts/wrap-layout/wrap-layout-common.ts index e17646561..c052c29a3 100644 --- a/tns-core-modules/ui/layouts/wrap-layout/wrap-layout-common.ts +++ b/tns-core-modules/ui/layouts/wrap-layout/wrap-layout-common.ts @@ -1,5 +1,5 @@ import { WrapLayout as WrapLayoutDefinition } from "."; -import { LayoutBase, Property, isIOS, Length, zeroLength } from "../layout-base"; +import { LayoutBase, Property, isIOS, Length } from "../layout-base"; export * from "../layout-base"; @@ -11,14 +11,16 @@ export class WrapLayoutBase extends LayoutBase implements WrapLayoutDefinition { public effectiveItemHeight: number; } +WrapLayoutBase.prototype.recycleNativeView = true; + export const itemWidthProperty = new Property({ - name: "itemWidth", defaultValue: zeroLength, affectsLayout: isIOS, valueConverter: (v) => Length.parse(v), + name: "itemWidth", defaultValue: "auto", affectsLayout: isIOS, valueConverter: (v) => Length.parse(v), valueChanged: (target, oldValue, newValue) => target.effectiveItemWidth = Length.toDevicePixels(newValue, -1) }); itemWidthProperty.register(WrapLayoutBase); export const itemHeightProperty = new Property({ - name: "itemHeight", defaultValue: zeroLength, affectsLayout: isIOS, valueConverter: (v) => Length.parse(v), + name: "itemHeight", defaultValue: "auto", affectsLayout: isIOS, valueConverter: (v) => Length.parse(v), valueChanged: (target, oldValue, newValue) => target.effectiveItemHeight = Length.toDevicePixels(newValue, -1) }); itemHeightProperty.register(WrapLayoutBase); diff --git a/tns-core-modules/ui/layouts/wrap-layout/wrap-layout.android.ts b/tns-core-modules/ui/layouts/wrap-layout/wrap-layout.android.ts index 6f5330495..503ff8861 100644 --- a/tns-core-modules/ui/layouts/wrap-layout/wrap-layout.android.ts +++ b/tns-core-modules/ui/layouts/wrap-layout/wrap-layout.android.ts @@ -3,39 +3,21 @@ export * from "./wrap-layout-common"; export class WrapLayout extends WrapLayoutBase { - private _layout: org.nativescript.widgets.WrapLayout; - - get android(): org.nativescript.widgets.WrapLayout { - return this._layout; - } - - get _nativeView(): org.nativescript.widgets.WrapLayout { - return this._layout; - } + nativeView: org.nativescript.widgets.WrapLayout; public _createNativeView() { - const layout = this._layout = new org.nativescript.widgets.WrapLayout(this._context); - return layout; + return new org.nativescript.widgets.WrapLayout(this._context); } - [orientationProperty.getDefault](): "horizontal" | "vertical" { - return "vertical"; - } [orientationProperty.setNative](value: "horizontal" | "vertical") { - this._layout.setOrientation(value === "vertical" ? org.nativescript.widgets.Orientation.vertical : org.nativescript.widgets.Orientation.horizontal) + this.nativeView.setOrientation(value === "vertical" ? org.nativescript.widgets.Orientation.vertical : org.nativescript.widgets.Orientation.horizontal) } - [itemWidthProperty.getDefault](): Length { - return "auto"; - } [itemWidthProperty.setNative](value: Length) { - this._layout.setItemWidth(Length.toDevicePixels(value, -1)); + this.nativeView.setItemWidth(Length.toDevicePixels(value, -1)); } - [itemHeightProperty.getDefault](): Length { - return "auto"; - } [itemHeightProperty.setNative](value: Length) { - this._layout.setItemHeight(Length.toDevicePixels(value, -1)); + this.nativeView.setItemHeight(Length.toDevicePixels(value, -1)); } } \ No newline at end of file diff --git a/tns-core-modules/ui/list-picker/list-picker-common.ts b/tns-core-modules/ui/list-picker/list-picker-common.ts index f6e4b1c77..0bec40935 100644 --- a/tns-core-modules/ui/list-picker/list-picker-common.ts +++ b/tns-core-modules/ui/list-picker/list-picker-common.ts @@ -20,6 +20,8 @@ export class ListPickerBase extends View implements ListPickerDefinition { } } +ListPickerBase.prototype.recycleNativeView = true; + export const selectedIndexProperty = new CoercibleProperty({ name: "selectedIndex", defaultValue: -1, valueConverter: (v) => parseInt(v), diff --git a/tns-core-modules/ui/list-picker/list-picker.android.ts b/tns-core-modules/ui/list-picker/list-picker.android.ts index 670e52b44..a3428fc0a 100644 --- a/tns-core-modules/ui/list-picker/list-picker.android.ts +++ b/tns-core-modules/ui/list-picker/list-picker.android.ts @@ -69,33 +69,41 @@ function getSelectorWheelPaint(picker: android.widget.NumberPicker): android.gra } export class ListPicker extends ListPickerBase { - private _android: android.widget.NumberPicker; - private _valueChangedListener: android.widget.NumberPicker.OnValueChangeListener; - private _formatter: android.widget.NumberPicker.Formatter; - private _editText: android.widget.EditText; + nativeView: android.widget.NumberPicker; private _selectorWheelPaint: android.graphics.Paint; - get android(): android.widget.NumberPicker { - return this._android; - } - public _createNativeView() { initializeNativeClasses(); - const picker = this._android = new android.widget.NumberPicker(this._context); - let editText = getEditText(this._android); - this._editText = editText; - this._selectorWheelPaint = getSelectorWheelPaint(picker); - + const picker = new android.widget.NumberPicker(this._context); + picker.setDescendantFocusability(android.widget.NumberPicker.FOCUS_BLOCK_DESCENDANTS); picker.setMinValue(0); picker.setMaxValue(0); picker.setValue(0); - this._formatter = this._formatter || new Formatter(this); - picker.setFormatter(this._formatter); + const formatter = new Formatter(this); + picker.setFormatter(formatter); + (picker).formatter = formatter; - this._valueChangedListener = this._valueChangedListener || new ValueChangeListener(this); - picker.setOnValueChangedListener(this._valueChangedListener); + const valueChangedListener = new ValueChangeListener(this); + picker.setOnValueChangedListener(valueChangedListener); + (picker).valueChangedListener = valueChangedListener; + + const editText = getEditText(picker); + if (editText) { + (picker).editText = editText; + } + + picker.setWrapSelectorWheel(false); + return picker; + } + + public _initNativeView(): void { + const nativeView = this.nativeView; + this._selectorWheelPaint = getSelectorWheelPaint(nativeView); + (nativeView).formatter.owner = this; + (nativeView).valueChangedListener.owner = this; + const editText = (nativeView).editText; if (editText) { //Fix the disappearing selected item. @@ -105,20 +113,26 @@ export class ListPicker extends ListPickerBase { //Since the Android NumberPicker has to always have at least one item, i.e. minValue=maxValue=value=0, we don't want this zero showing up when this.items is empty. editText.setText(" ", android.widget.TextView.BufferType.NORMAL); } + } - picker.setWrapSelectorWheel(false); - return picker; + public _disposeNativeView() { + const nativeView = this.nativeView; + (nativeView).formatter.owner = null; + (nativeView).valueChangedListener.owner = null; } private _fixNumberPickerRendering() { + const nativeView = this.nativeView; //HACK: Force the stubborn NumberPicker to render correctly when we have 0 or 1 items. - this._android.setFormatter(null); - this._android.setFormatter(this._formatter); //Force the NumberPicker to call our Formatter - if (this._editText) { - this._editText.setFilters([]); - this._editText.invalidate(); //Force the EditText to redraw + nativeView.setFormatter(null); + nativeView.setFormatter((nativeView).formatter); //Force the NumberPicker to call our Formatter + + const editText = (nativeView).editText; + if (editText) { + editText.setFilters([]); + editText.invalidate(); //Force the EditText to redraw } - this._android.invalidate(); + nativeView.invalidate(); } [selectedIndexProperty.getDefault](): number { @@ -126,7 +140,7 @@ export class ListPicker extends ListPickerBase { } [selectedIndexProperty.setNative](value: number) { if (value >= 0) { - this.android.setValue(value); + this.nativeView.setValue(value); } } @@ -135,7 +149,7 @@ export class ListPicker extends ListPickerBase { } [itemsProperty.setNative](value: any[] | ItemsSource) { let maxValue = value && value.length > 0 ? value.length - 1 : 0; - this.android.setMaxValue(maxValue); + this.nativeView.setMaxValue(maxValue); this._fixNumberPickerRendering(); // Coerce selected index after we have set items to native view. @@ -143,9 +157,10 @@ export class ListPicker extends ListPickerBase { } [colorProperty.getDefault](): { wheelColor: number, textColor: number } { + const editText = (this.nativeView).editText; return { wheelColor: this._selectorWheelPaint.getColor(), - textColor: this._editText ? this._editText.getTextColors().getDefaultColor() : -1 + textColor: editText ? editText.getTextColors().getDefaultColor() : -1 } } [colorProperty.setNative](value: { wheelColor: number, textColor: number } | Color) { @@ -159,8 +174,9 @@ export class ListPicker extends ListPickerBase { } this._selectorWheelPaint.setColor(wheelColor); - if (this._editText) { - this._editText.setTextColor(color); + const editText = (this.nativeView).editText; + if (editText) { + editText.setTextColor(color); } } } diff --git a/tns-core-modules/ui/list-picker/list-picker.ios.ts b/tns-core-modules/ui/list-picker/list-picker.ios.ts index d513de1b6..7b34fe3fa 100644 --- a/tns-core-modules/ui/list-picker/list-picker.ios.ts +++ b/tns-core-modules/ui/list-picker/list-picker.ios.ts @@ -11,7 +11,7 @@ export class ListPicker extends ListPickerBase { constructor() { super(); - this._ios = UIPickerView.new(); + this.nativeView = this._ios = UIPickerView.new(); this._ios.dataSource = this._dataSource = ListPickerDataSource.initWithOwner(new WeakRef(this)); this._delegate = ListPickerDelegateImpl.initWithOwner(new WeakRef(this)); diff --git a/tns-core-modules/ui/list-view/list-view-common.ts b/tns-core-modules/ui/list-view/list-view-common.ts index 5c7a0f0e7..7256f63bb 100644 --- a/tns-core-modules/ui/list-view/list-view-common.ts +++ b/tns-core-modules/ui/list-view/list-view-common.ts @@ -124,6 +124,8 @@ export abstract class ListViewBase extends View implements ListViewDefinition { } } +ListViewBase.prototype.recycleNativeView = true; + /** * Represents the property backing the items property of each ListView instance. */ @@ -174,7 +176,7 @@ export const rowHeightProperty = new CoercibleProperty({ name: "rowHeight", defaultValue: defaultRowHeight, equalityComparer: Length.equals, coerceValue: (target, value) => { // We coerce to default value if we don't have display density. - return target._nativeView ? value : defaultRowHeight; + return target.nativeView ? value : defaultRowHeight; }, valueChanged: (target, oldValue, newValue) => { target._effectiveRowHeight = Length.toDevicePixels(newValue, autoEffectiveRowHeight); diff --git a/tns-core-modules/ui/list-view/list-view.android.ts b/tns-core-modules/ui/list-view/list-view.android.ts index 9802da8f8..b04528464 100644 --- a/tns-core-modules/ui/list-view/list-view.android.ts +++ b/tns-core-modules/ui/list-view/list-view.android.ts @@ -26,7 +26,7 @@ function initializeItemClickListener(): void { @Interfaces([android.widget.AdapterView.OnItemClickListener]) class ItemClickListenerImpl extends java.lang.Object implements android.widget.AdapterView.OnItemClickListener { - constructor(private owner: ListView) { + constructor(public owner: ListView) { super(); return global.__native(this); } @@ -42,39 +42,61 @@ function initializeItemClickListener(): void { } export class ListView extends ListViewBase { + nativeView: android.widget.ListView; + private _androidViewId: number = -1; - private _android: android.widget.ListView; - private _itemClickListener: android.widget.AdapterView.OnItemClickListener; + public _realizedItems = new Map(); public _realizedTemplates = new Map>(); public _createNativeView() { initializeItemClickListener(); - const listView = this._android = new android.widget.ListView(this._context); - this._android.setDescendantFocusability(android.view.ViewGroup.FOCUS_AFTER_DESCENDANTS); + + const listView = new android.widget.ListView(this._context); + listView.setDescendantFocusability(android.view.ViewGroup.FOCUS_AFTER_DESCENDANTS); this.updateEffectiveRowHeight(); // Fixes issue with black random black items when scrolling - this._android.setCacheColorHint(android.graphics.Color.TRANSPARENT); - if (this._androidViewId < 0) { - this._androidViewId = android.view.View.generateViewId(); - } - this._android.setId(this._androidViewId); + listView.setCacheColorHint(android.graphics.Color.TRANSPARENT); + + // listView.setId(this._androidViewId); ensureListViewAdapterClass(); - this._android.setAdapter(new ListViewAdapterClass(this)); + const adapter = new ListViewAdapterClass(this); + listView.setAdapter(adapter); + (listView).adapter = adapter; + + const itemClickListener = new ItemClickListener(this); + listView.setOnItemClickListener(itemClickListener); + (listView).itemClickListener = itemClickListener; - this._itemClickListener = this._itemClickListener || new ItemClickListener(this); - this.android.setOnItemClickListener(this._itemClickListener); return listView; } - get android(): android.widget.ListView { - return this._android; + public _initNativeView(): void { + const nativeView: any = this.nativeView; + (nativeView).itemClickListener.owner = this; + const adapter = (nativeView).adapter; + adapter.owner = this; + nativeView.setAdapter(adapter); + if (this._androidViewId < 0) { + this._androidViewId = android.view.View.generateViewId(); + } + nativeView.setId(this._androidViewId); + } + + public _disposeNativeView() { + const nativeView = this.nativeView; + nativeView.setAdapter(null); + (nativeView).itemClickListener.owner = null; + (nativeView).adapter.owner = null; + this.clearRealizedCells(); + super._disposeNativeView(); } public refresh() { - if (!this._android || !this._android.getAdapter()) { + const nativeView = this.nativeView; + if (!nativeView || !nativeView.getAdapter()) { return; } @@ -85,20 +107,16 @@ export class ListView extends ListViewBase { } }); - (this._android.getAdapter()).notifyDataSetChanged(); + (nativeView.getAdapter()).notifyDataSetChanged(); } public scrollToIndex(index: number) { - if (this._android) { - this._android.setSelection(index); + const nativeView = this.nativeView; + if (nativeView) { + nativeView.setSelection(index); } } - public _disposeNativeView() { - super._disposeNativeView(); - this.clearRealizedCells(); - } - get _childrenCount(): number { return this._realizedItems.size; } @@ -145,14 +163,14 @@ export class ListView extends ListViewBase { } [separatorColorProperty.getDefault](): { dividerHeight: number, divider: android.graphics.drawable.Drawable } { - let nativeView = this._android; + let nativeView = this.nativeView; return { dividerHeight: nativeView.getDividerHeight(), divider: nativeView.getDivider() }; } [separatorColorProperty.setNative](value: Color | { dividerHeight: number, divider: android.graphics.drawable.Drawable }) { - let nativeView = this._android; + let nativeView = this.nativeView; if (value instanceof Color) { nativeView.setDivider(new android.graphics.drawable.ColorDrawable(value.android)); nativeView.setDividerHeight(1); @@ -171,7 +189,7 @@ export class ListView extends ListViewBase { this._itemTemplatesInternal = this._itemTemplatesInternal.concat(value); } - this.android.setAdapter(new ListViewAdapterClass(this)); + this.nativeView.setAdapter(new ListViewAdapterClass(this)); this.refresh(); } } @@ -183,22 +201,19 @@ function ensureListViewAdapterClass() { } class ListViewAdapter extends android.widget.BaseAdapter { - private _listView: ListView; - - constructor(listView: ListView) { + constructor(public owner: ListView) { super(); - this._listView = listView; return global.__native(this); } public getCount() { - return this._listView && this._listView.items && this._listView.items.length ? this._listView.items.length : 0; + return this.owner && this.owner.items && this.owner.items.length ? this.owner.items.length : 0; } public getItem(i: number) { - if (this._listView && this._listView.items && i < this._listView.items.length) { - let getItem = (this._listView.items).getItem; - return getItem ? getItem(i) : this._listView.items[i]; + if (this.owner && this.owner.items && i < this.owner.items.length) { + let getItem = (this.owner.items).getItem; + return getItem ? getItem(i) : this.owner.items[i]; } return null; @@ -213,32 +228,32 @@ function ensureListViewAdapterClass() { } public getViewTypeCount() { - return this._listView._itemTemplatesInternal.length; + return this.owner._itemTemplatesInternal.length; } public getItemViewType(index: number) { - let template = this._listView._getItemTemplate(index); - let itemViewType = this._listView._itemTemplatesInternal.indexOf(template); + let template = this.owner._getItemTemplate(index); + let itemViewType = this.owner._itemTemplatesInternal.indexOf(template); return itemViewType; } public getView(index: number, convertView: android.view.View, parent: android.view.ViewGroup): android.view.View { - //this._listView._dumpRealizedTemplates(); + //this.owner._dumpRealizedTemplates(); - if (!this._listView) { + if (!this.owner) { return null; } - let totalItemCount = this._listView.items ? this._listView.items.length : 0; + let totalItemCount = this.owner.items ? this.owner.items.length : 0; if (index === (totalItemCount - 1)) { - this._listView.notify({ eventName: LOADMOREITEMS, object: this._listView }); + this.owner.notify({ eventName: LOADMOREITEMS, object: this.owner }); } // Recycle an existing view or create a new one if needed. - let template = this._listView._getItemTemplate(index); + let template = this.owner._getItemTemplate(index); let view: View; if (convertView) { - view = this._listView._realizedTemplates.get(template.key).get(convertView); + view = this.owner._realizedTemplates.get(template.key).get(convertView); if (!view) { throw new Error(`There is no entry with key '${convertView}' in the realized views cache for template with key'${template.key}'.`); } @@ -248,50 +263,50 @@ function ensureListViewAdapterClass() { } let args: ItemEventData = { - eventName: ITEMLOADING, object: this._listView, index: index, view: view, + eventName: ITEMLOADING, object: this.owner, index: index, view: view, android: parent, ios: undefined }; - this._listView.notify(args); + this.owner.notify(args); if (!args.view) { - args.view = this._listView._getDefaultItemContent(index); + args.view = this.owner._getDefaultItemContent(index); } if (args.view) { - if (this._listView._effectiveRowHeight > -1) { - args.view.height = this._listView.rowHeight; + if (this.owner._effectiveRowHeight > -1) { + args.view.height = this.owner.rowHeight; } else { args.view.height = unsetValue; } - this._listView._prepareItem(args.view, index); + this.owner._prepareItem(args.view, index); if (!args.view.parent) { // Proxy containers should not get treated as layouts. // Wrap them in a real layout as well. if (args.view instanceof LayoutBase && !(args.view instanceof ProxyViewContainer)) { - this._listView._addView(args.view); - convertView = args.view.android; + this.owner._addView(args.view); + convertView = args.view.nativeView; } else { let sp = new StackLayout(); sp.addChild(args.view); - this._listView._addView(sp); + this.owner._addView(sp); - convertView = sp.android; + convertView = sp.nativeView; } } // Cache the view for recycling - let realizedItemsForTemplateKey = this._listView._realizedTemplates.get(template.key); + let realizedItemsForTemplateKey = this.owner._realizedTemplates.get(template.key); if (!realizedItemsForTemplateKey) { realizedItemsForTemplateKey = new Map(); - this._listView._realizedTemplates.set(template.key, realizedItemsForTemplateKey); + this.owner._realizedTemplates.set(template.key, realizedItemsForTemplateKey); } realizedItemsForTemplateKey.set(convertView, args.view); - this._listView._realizedItems.set(convertView, args.view); + this.owner._realizedItems.set(convertView, args.view); } return convertView; diff --git a/tns-core-modules/ui/list-view/list-view.ios.ts b/tns-core-modules/ui/list-view/list-view.ios.ts index bf4ce5605..1b3ad4e0f 100644 --- a/tns-core-modules/ui/list-view/list-view.ios.ts +++ b/tns-core-modules/ui/list-view/list-view.ios.ts @@ -208,7 +208,7 @@ export class ListView extends ListViewBase { constructor() { super(); - this._ios = UITableView.new(); + this.nativeView = this._ios = UITableView.new(); this._ios.registerClassForCellReuseIdentifier(ListViewCell.class(), this._defaultTemplate.key); this._ios.autoresizingMask = UIViewAutoresizing.None; this._ios.estimatedRowHeight = DEFAULT_HEIGHT; @@ -359,15 +359,15 @@ export class ListView extends ListViewBase { cell.owner = new WeakRef(view); } else if (cell.view !== view) { this._removeContainer(cell); - (cell.view._nativeView).removeFromSuperview(); + (cell.view.nativeView).removeFromSuperview(); cell.owner = new WeakRef(view); } this._prepareItem(view, indexPath.row); this._map.set(cell, view); // We expect that views returned from itemLoading are new (e.g. not reused). - if (view && !view.parent && view._nativeView) { - cell.contentView.addSubview(view._nativeView); + if (view && !view.parent && view.nativeView) { + cell.contentView.addSubview(view.nativeView); this._addView(view); } diff --git a/tns-core-modules/ui/page/page.android.ts b/tns-core-modules/ui/page/page.android.ts index fc6281846..925335b3f 100644 --- a/tns-core-modules/ui/page/page.android.ts +++ b/tns-core-modules/ui/page/page.android.ts @@ -39,13 +39,13 @@ function initializeDialogFragment() { this._owner.verticalAlignment = this._fullscreen ? "stretch" : "middle"; this._owner.actionBarHidden = true; - const nativeView = this._owner._nativeView; + const nativeView = this._owner.nativeView; let layoutParams = nativeView.getLayoutParams(); if (!layoutParams) { layoutParams = new org.nativescript.widgets.CommonLayoutParams(); nativeView.setLayoutParams(layoutParams); } - dialog.setContentView(this._owner._nativeView, layoutParams); + dialog.setContentView(this._owner.nativeView, layoutParams); const window = dialog.getWindow(); window.setBackgroundDrawable(new android.graphics.drawable.ColorDrawable(android.graphics.Color.TRANSPARENT)); @@ -87,32 +87,24 @@ function initializeDialogFragment() { } export class Page extends PageBase { + nativeView: org.nativescript.widgets.GridLayout; private _isBackNavigation = false; - private _grid: org.nativescript.widgets.GridLayout; - - get android(): android.view.ViewGroup { - return this._grid; - } - - get _nativeView(): android.view.ViewGroup { - return this._grid; - } - - get nativeView(): android.view.ViewGroup { - return this._grid; - } public _createNativeView() { - const layout = this._grid = new org.nativescript.widgets.GridLayout(this._context); - this._grid.addRow(new org.nativescript.widgets.ItemSpec(1, org.nativescript.widgets.GridUnitType.auto)); - this._grid.addRow(new org.nativescript.widgets.ItemSpec(1, org.nativescript.widgets.GridUnitType.star)); - this.nativeView.setBackgroundColor(new Color("white").android); + 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.setBackgroundColor(-1); return layout; } + public _initNativeView(): void { + this.nativeView.setBackgroundColor(-1); // White color. + } + public _addViewToNativeVisualTree(child: View, atIndex?: number): boolean { // Set the row property for the child - if (this._nativeView && child._nativeView) { + if (this.nativeView && child.nativeView) { if (child instanceof ActionBar) { GridLayout.setRow(child, 0); child.horizontalAlignment = "stretch"; @@ -235,4 +227,4 @@ export class Page extends PageBase { (window).setStatusBarColor(color); } } -} +} \ No newline at end of file diff --git a/tns-core-modules/ui/page/page.ios.ts b/tns-core-modules/ui/page/page.ios.ts index 4c7591747..a5429c85e 100644 --- a/tns-core-modules/ui/page/page.ios.ts +++ b/tns-core-modules/ui/page/page.ios.ts @@ -325,7 +325,7 @@ class UIViewControllerImpl extends UIViewController { } export class Page extends PageBase { - public nativeView: UIView; + nativeView: UIView; private _ios: UIViewControllerImpl; public _enableLoadedEvents: boolean; @@ -343,8 +343,8 @@ export class Page extends PageBase { public requestLayout(): void { super.requestLayout(); - if (!this.parent && this.ios && this._nativeView) { - this._nativeView.setNeedsLayout(); + if (!this.parent && this.ios && this.nativeView) { + this.nativeView.setNeedsLayout(); } } @@ -402,10 +402,6 @@ export class Page extends PageBase { return this._ios; } - get _nativeView(): UIView { - return this.ios.view; - } - protected _showNativeModalView(parent: Page, context: any, closeCallback: Function, fullscreen?: boolean) { super._showNativeModalView(parent, context, closeCallback, fullscreen); this._modalParent = parent; diff --git a/tns-core-modules/ui/placeholder/placeholder.android.ts b/tns-core-modules/ui/placeholder/placeholder.android.ts index 7fc2b4ac3..952e629b6 100644 --- a/tns-core-modules/ui/placeholder/placeholder.android.ts +++ b/tns-core-modules/ui/placeholder/placeholder.android.ts @@ -4,20 +4,9 @@ import { View } from "../core/view" export class Placeholder extends View implements PlaceholderDefinition { public static creatingViewEvent = "creatingView"; - private _android: android.view.View; - public _createNativeView() { let args = { eventName: Placeholder.creatingViewEvent, object: this, view: undefined, context: this._context }; this.notify(args); - const view = this._android = args.view; - return view; + return args.view; } - - get android(): android.view.View { - return this._android; - } - - get _nativeView(): android.view.View { - return this._android; - } -} +} \ No newline at end of file diff --git a/tns-core-modules/ui/placeholder/placeholder.ios.ts b/tns-core-modules/ui/placeholder/placeholder.ios.ts index 5f42a0fa0..3e1989e54 100644 --- a/tns-core-modules/ui/placeholder/placeholder.ios.ts +++ b/tns-core-modules/ui/placeholder/placeholder.ios.ts @@ -10,12 +10,8 @@ export class Placeholder extends View implements PlaceholderDefinition { if (!this._ios) { var args = { eventName: Placeholder.creatingViewEvent, object: this, view: undefined, context: undefined }; super.notify(args); - this._ios = args.view; + this.nativeView = this._ios = args.view; } return this._ios; } - - get _nativeView(): UIView { - return this.ios; - } -} +} \ No newline at end of file diff --git a/tns-core-modules/ui/progress/progress-common.ts b/tns-core-modules/ui/progress/progress-common.ts index eaa675c13..2d7627f20 100644 --- a/tns-core-modules/ui/progress/progress-common.ts +++ b/tns-core-modules/ui/progress/progress-common.ts @@ -27,6 +27,8 @@ export class ProgressBase extends View implements ProgressDefinition { // } } +ProgressBase.prototype.recycleNativeView = true; + /** * Represents the observable property backing the value property of each Progress instance. */ diff --git a/tns-core-modules/ui/progress/progress.android.ts b/tns-core-modules/ui/progress/progress.android.ts index 40aa747c6..7e3a6569c 100644 --- a/tns-core-modules/ui/progress/progress.android.ts +++ b/tns-core-modules/ui/progress/progress.android.ts @@ -8,40 +8,31 @@ export * from "./progress-common"; const R_ATTR_PROGRESS_BAR_STYLE_HORIZONTAL = 0x01010078; export class Progress extends ProgressBase { - private _android: android.widget.ProgressBar; + nativeView: android.widget.ProgressBar; public _createNativeView() { - const progressBar = this._android = new android.widget.ProgressBar(this._context, null, R_ATTR_PROGRESS_BAR_STYLE_HORIZONTAL); - return progressBar; - } - - get android(): android.widget.ProgressBar { - return this._android; - } - - get nativeView(): android.widget.ProgressBar { - return this._android; + return new android.widget.ProgressBar(this._context, null, R_ATTR_PROGRESS_BAR_STYLE_HORIZONTAL); } [valueProperty.getDefault](): number { return 0; } [valueProperty.setNative](value: number) { - this._android.setProgress(value); + this.nativeView.setProgress(value); } [maxValueProperty.getDefault](): number { return 100; } [maxValueProperty.setNative](value: number) { - this._android.setMax(value); + this.nativeView.setMax(value); } [colorProperty.getDefault](): android.graphics.drawable.Drawable { return null; } [colorProperty.setNative](value: Color) { - let progressDrawable = this._android.getProgressDrawable(); + let progressDrawable = this.nativeView.getProgressDrawable(); if (!progressDrawable) { return; } @@ -57,7 +48,7 @@ export class Progress extends ProgressBase { return null; } [backgroundColorProperty.setNative](value: Color) { - let progressDrawable = this._android.getProgressDrawable(); + let progressDrawable = this.nativeView.getProgressDrawable(); if (!progressDrawable) { return; } @@ -80,4 +71,4 @@ export class Progress extends ProgressBase { [backgroundInternalProperty.setNative](value: Color) { // } -} \ No newline at end of file +} diff --git a/tns-core-modules/ui/progress/progress.ios.ts b/tns-core-modules/ui/progress/progress.ios.ts index 3a9b59687..9016f58ae 100644 --- a/tns-core-modules/ui/progress/progress.ios.ts +++ b/tns-core-modules/ui/progress/progress.ios.ts @@ -6,19 +6,20 @@ export * from "./progress-common"; export class Progress extends ProgressBase { - private _ios = UIProgressView.new(); + private _ios: UIProgressView; + constructor() { + super(); + this.nativeView = this._ios = UIProgressView.new(); + } + get ios(): UIProgressView { return this._ios; } - get nativeView(): UIProgressView { - return this._ios; - } - - get _nativeView(): UIProgressView { - return this._ios; - } + // get nativeView(): UIProgressView { + // return this._ios; + // } [valueProperty.getDefault](): number { return 0; diff --git a/tns-core-modules/ui/proxy-view-container/proxy-view-container.ts b/tns-core-modules/ui/proxy-view-container/proxy-view-container.ts index 2588763b9..6357cfb56 100644 --- a/tns-core-modules/ui/proxy-view-container/proxy-view-container.ts +++ b/tns-core-modules/ui/proxy-view-container/proxy-view-container.ts @@ -10,6 +10,12 @@ import { LayoutBase, View, traceEnabled, traceWrite, traceCategories } from "../ // * Child is removed from attached proxy. Handled in _removeViewFromNativeVisualTree. // * Proxy (with children) is removed form the DOM. In _removeViewFromNativeVisualTree recursively when the proxy is removed from its parent. export class ProxyViewContainer extends LayoutBase implements ProxyViewContainerDefinition { + + constructor() { + super(); + this.nativeView = undefined; + } + // No native view for proxy container. get ios(): any { return null; @@ -19,9 +25,9 @@ export class ProxyViewContainer extends LayoutBase implements ProxyViewContainer return null; } - get _nativeView(): any { - return null; - } + // get nativeView(): any { + // return null; + // } get isLayoutRequested(): boolean { // Always return false so all layout requests from children bubble up. diff --git a/tns-core-modules/ui/repeater/repeater.ts b/tns-core-modules/ui/repeater/repeater.ts index 4c6034c46..3a6b0a68c 100644 --- a/tns-core-modules/ui/repeater/repeater.ts +++ b/tns-core-modules/ui/repeater/repeater.ts @@ -118,6 +118,8 @@ export class Repeater extends CustomLayoutView implements RepeaterDefinition { } +Repeater.prototype.recycleNativeView = true; + /** * Represents the item template property of each ListView instance. */ diff --git a/tns-core-modules/ui/scroll-view/scroll-view-common.ts b/tns-core-modules/ui/scroll-view/scroll-view-common.ts index 7238a2286..ef7521b76 100644 --- a/tns-core-modules/ui/scroll-view/scroll-view-common.ts +++ b/tns-core-modules/ui/scroll-view/scroll-view-common.ts @@ -95,4 +95,4 @@ export const orientationProperty = new Property{ object: this, @@ -136,7 +127,9 @@ export class ScrollView extends ScrollViewBase { } protected dettachNative() { - this._android.getViewTreeObserver().removeOnScrollChangedListener(this.handler); + this.nativeView.getViewTreeObserver().removeOnScrollChangedListener(this.handler); this.handler = null; } } + +ScrollView.prototype.recycleNativeView = false; \ No newline at end of file diff --git a/tns-core-modules/ui/scroll-view/scroll-view.ios.ts b/tns-core-modules/ui/scroll-view/scroll-view.ios.ts index e744f9f40..17b376d15 100644 --- a/tns-core-modules/ui/scroll-view/scroll-view.ios.ts +++ b/tns-core-modules/ui/scroll-view/scroll-view.ios.ts @@ -81,10 +81,6 @@ export class ScrollView extends ScrollViewBase { return this.nativeView; } - get _nativeView(): UIView { - return this.nativeView; - } - public scrollToVerticalOffset(value: number, animated: boolean) { if (this.orientation === "vertical") { const bounds = this.nativeView.bounds.size; @@ -153,3 +149,5 @@ export class ScrollView extends ScrollViewBase { // NOOP } } + +ScrollView.prototype.recycleNativeView = true; \ No newline at end of file diff --git a/tns-core-modules/ui/search-bar/search-bar-common.ts b/tns-core-modules/ui/search-bar/search-bar-common.ts index 83873f116..0e25abf37 100644 --- a/tns-core-modules/ui/search-bar/search-bar-common.ts +++ b/tns-core-modules/ui/search-bar/search-bar-common.ts @@ -14,6 +14,8 @@ export abstract class SearchBarBase extends View implements SearchBarDefinition public abstract dismissSoftInput(); } +SearchBarBase.prototype.recycleNativeView = true; + export const textProperty = new Property({ name: "text", defaultValue: "", affectsLayout: isIOS }); textProperty.register(SearchBarBase); diff --git a/tns-core-modules/ui/search-bar/search-bar.android.ts b/tns-core-modules/ui/search-bar/search-bar.android.ts index c5ea996d8..a2647787f 100644 --- a/tns-core-modules/ui/search-bar/search-bar.android.ts +++ b/tns-core-modules/ui/search-bar/search-bar.android.ts @@ -76,18 +76,16 @@ function initializeNativeClasses(): void { } export class SearchBar extends SearchBarBase { - private _android: android.widget.SearchView; - private _closeListener: android.widget.SearchView.OnCloseListener; - private _queryTextListener: android.widget.SearchView.OnQueryTextListener; + nativeView: android.widget.SearchView; public dismissSoftInput() { - ad.dismissSoftInput(this._nativeView); + ad.dismissSoftInput(this.nativeView); } public focus(): boolean { let result = super.focus(); if (result) { - ad.showSoftInput(this._nativeView); + ad.showSoftInput(this.nativeView); } return result; @@ -95,25 +93,35 @@ export class SearchBar extends SearchBarBase { public _createNativeView() { initializeNativeClasses(); - this._android = new android.widget.SearchView(this._context); + const nativeView = new android.widget.SearchView(this._context); + nativeView.setIconified(false); - this._android.setIconified(false); + const queryTextListener = new QueryTextListener(this); + nativeView.setOnQueryTextListener(queryTextListener); + (nativeView).queryTextListener = queryTextListener; - this._queryTextListener = this._queryTextListener || new QueryTextListener(this); - this._android.setOnQueryTextListener(this._queryTextListener); + const closeListener = new CloseListener(this); + nativeView.setOnCloseListener(closeListener); + (nativeView).closeListener = closeListener; - this._closeListener = this._closeListener || new CloseListener(this); - this._android.setOnCloseListener(this._closeListener); - return this._android; + return nativeView; } - get android(): android.widget.SearchView { - return this._android; + public _initNativeView(): void { + const nativeView: any = this.nativeView; + nativeView.closeListener.owner = this; + nativeView.queryTextListener.owner = this; + } + + public _disposeNativeView() { + const nativeView: any = this.nativeView; + nativeView.closeListener.owner = null; + nativeView.queryTextListener.owner = null; } [backgroundColorProperty.getDefault](): number { // TODO: Why do we get DrawingCacheBackgroundColor but set backgroundColor????? - let result = this._android.getDrawingCacheBackgroundColor(); + const result = this.nativeView.getDrawingCacheBackgroundColor(); return result; } [backgroundColorProperty.setNative](value: Color) { @@ -124,24 +132,18 @@ export class SearchBar extends SearchBarBase { color = value.android; } - this._android.setBackgroundColor(color); - let searchPlate = this._getSearchPlate(); + this.nativeView.setBackgroundColor(color); + const searchPlate = this._getSearchPlate(); searchPlate.setBackgroundColor(color); } [colorProperty.getDefault](): number { - let textView = this._getTextView(); + const textView = this._getTextView(); return textView.getCurrentTextColor(); } [colorProperty.setNative](value: Color) { - let color: number; - if (typeof value === "number") { - color = value; - } else { - color = value.android; - } - - let textView = this._getTextView(); + const color: number = (typeof value === "number") ? value : value.android; + const textView = this._getTextView(); textView.setTextColor(color); } @@ -175,41 +177,41 @@ export class SearchBar extends SearchBarBase { } [textProperty.setNative](value: string) { const text = (value === null || value === undefined) ? '' : value.toString(); - this._android.setQuery(text, false); + this.nativeView.setQuery(text, false); } [hintProperty.getDefault](): string { return ""; } [hintProperty.setNative](value: string) { const text = (value === null || value === undefined) ? '' : value.toString(); - this._android.setQueryHint(text); + this.nativeView.setQueryHint(text); } [textFieldBackgroundColorProperty.getDefault](): number { - let textView = this._getTextView(); + const textView = this._getTextView(); return textView.getCurrentTextColor(); } [textFieldBackgroundColorProperty.setNative](value: Color) { - let textView = this._getTextView(); - let color = value instanceof Color ? value.android : value; + const textView = this._getTextView(); + const color = value instanceof Color ? value.android : value; textView.setBackgroundColor(color); } [textFieldHintColorProperty.getDefault](): number { - let textView = this._getTextView(); + const textView = this._getTextView(); return textView.getCurrentTextColor(); } [textFieldHintColorProperty.setNative](value: Color) { - let textView = this._getTextView(); - let color = value instanceof Color ? value.android : value; + const textView = this._getTextView(); + const color = value instanceof Color ? value.android : value; textView.setHintTextColor(color); } private _getTextView(): android.widget.TextView { - let id = this._android.getContext().getResources().getIdentifier("android:id/search_src_text", null, null); - return this._android.findViewById(id); + const id = this.nativeView.getContext().getResources().getIdentifier("android:id/search_src_text", null, null); + return this.nativeView.findViewById(id); } private _getSearchPlate(): android.widget.LinearLayout { - let id = this._android.getContext().getResources().getIdentifier("android:id/search_plate", null, null); - return this._android.findViewById(id); + const id = this.nativeView.getContext().getResources().getIdentifier("android:id/search_plate", null, null); + return this.nativeView.findViewById(id); } } diff --git a/tns-core-modules/ui/search-bar/search-bar.ios.ts b/tns-core-modules/ui/search-bar/search-bar.ios.ts index 46c069941..45a23cb14 100644 --- a/tns-core-modules/ui/search-bar/search-bar.ios.ts +++ b/tns-core-modules/ui/search-bar/search-bar.ios.ts @@ -61,7 +61,7 @@ export class SearchBar extends SearchBarBase { constructor() { super(); - this._ios = UISearchBar.new(); + this.nativeView = this._ios = UISearchBar.new(); this._delegate = UISearchBarDelegateImpl.initWithOwner(new WeakRef(this)); } diff --git a/tns-core-modules/ui/segmented-bar/segmented-bar-common.ts b/tns-core-modules/ui/segmented-bar/segmented-bar-common.ts index 3b12bab7f..0ad0ac11c 100644 --- a/tns-core-modules/ui/segmented-bar/segmented-bar-common.ts +++ b/tns-core-modules/ui/segmented-bar/segmented-bar-common.ts @@ -42,13 +42,20 @@ export abstract class SegmentedBarBase extends View implements SegmentedBarDefin public _addChildFromBuilder(name: string, value: any): void { if (name === "SegmentedBarItem") { - if (!this.items) { - this.items = new Array(); + const item = value; + let items = this.items; + if (!items) { + items = new Array(); + items.push(item); + this.items = items; + } else { + items.push(item); + this._addView(item); + } + + if (this.nativeView) { + this[itemsProperty.setNative](items); } - let item = value; - this.items.push(item); - this._addView(item); - selectedIndexProperty.coerce(this); } } @@ -77,6 +84,8 @@ export abstract class SegmentedBarBase extends View implements SegmentedBarDefin } } +SegmentedBarBase.prototype.recycleNativeView = true; + /** * Gets or sets the selected index dependency property of the SegmentedBar. */ @@ -108,7 +117,6 @@ selectedIndexProperty.register(SegmentedBarBase); export const itemsProperty = new Property({ name: "items", valueChanged: (target, oldValue, newValue) => { target.onItemsChanged(oldValue, newValue); - selectedIndexProperty.coerce(target); } }); itemsProperty.register(SegmentedBarBase); diff --git a/tns-core-modules/ui/segmented-bar/segmented-bar.android.ts b/tns-core-modules/ui/segmented-bar/segmented-bar.android.ts index f46c5a4b0..0ade8e383 100644 --- a/tns-core-modules/ui/segmented-bar/segmented-bar.android.ts +++ b/tns-core-modules/ui/segmented-bar/segmented-bar.android.ts @@ -41,7 +41,7 @@ function initializeNativeClasses(): void { @Interfaces([android.widget.TabHost.OnTabChangeListener]) class TabChangeListenerImpl extends java.lang.Object implements android.widget.TabHost.OnTabChangeListener { - constructor(private owner: SegmentedBar) { + constructor(public owner: SegmentedBar) { super(); return global.__native(this); } @@ -56,7 +56,7 @@ function initializeNativeClasses(): void { @Interfaces([android.widget.TabHost.TabContentFactory]) class TabContentFactoryImpl extends java.lang.Object implements android.widget.TabHost.TabContentFactory { - constructor(private owner: SegmentedBar) { + constructor(public owner: SegmentedBar) { super(); return global.__native(this); } @@ -90,22 +90,14 @@ function initializeNativeClasses(): void { } export class SegmentedBarItem extends SegmentedBarItemBase { - private _textView: android.widget.TextView; - - get nativeView(): android.widget.TextView { - return this._textView; - } - - get android(): android.widget.TextView { - return this._textView; - } + nativeView: android.widget.TextView; public setupNativeView(tabIndex: number): void { // TabHost.TabSpec.setIndicator DOES NOT WORK once the title has been set. // http://stackoverflow.com/questions/2935781/modify-tab-indicator-dynamically-in-android - const titleTextView = this.parent.android.getTabWidget().getChildAt(tabIndex).findViewById(TITLE_TEXT_VIEW_ID); + const titleTextView = this.parent.nativeView.getTabWidget().getChildAt(tabIndex).findViewById(TITLE_TEXT_VIEW_ID); - this._textView = titleTextView; + this.nativeView = titleTextView; if (titleTextView) { initNativeView(this); if (this.titleDirty) { @@ -116,7 +108,7 @@ export class SegmentedBarItem extends SegmentedBarItemBase { private titleDirty: boolean; public _update(): void { - const tv = this._textView; + const tv = this.nativeView; if (tv) { let title = this.title; title = (title === null || title === undefined) ? "" : title; @@ -128,39 +120,39 @@ export class SegmentedBarItem extends SegmentedBarItemBase { } [colorProperty.getDefault](): number { - return this._textView.getCurrentTextColor(); + return this.nativeView.getCurrentTextColor(); } [colorProperty.setNative](value: Color | number) { - let color = value instanceof Color ? value.android : value; - this._textView.setTextColor(color); + const color = value instanceof Color ? value.android : value; + this.nativeView.setTextColor(color); } [fontSizeProperty.getDefault](): { nativeSize: number } { - return { nativeSize: this._textView.getTextSize() }; + return { nativeSize: this.nativeView.getTextSize() }; } [fontSizeProperty.setNative](value: number | { nativeSize: number }) { if (typeof value === "number") { - this._textView.setTextSize(value); + this.nativeView.setTextSize(value); } else { - this._textView.setTextSize(android.util.TypedValue.COMPLEX_UNIT_PX, value.nativeSize); + this.nativeView.setTextSize(android.util.TypedValue.COMPLEX_UNIT_PX, value.nativeSize); } } [fontInternalProperty.getDefault](): android.graphics.Typeface { - return this._textView.getTypeface(); + return this.nativeView.getTypeface(); } [fontInternalProperty.setNative](value: Font | android.graphics.Typeface) { - this._textView.setTypeface(value instanceof Font ? value.getAndroidTypeface() : value); + this.nativeView.setTypeface(value instanceof Font ? value.getAndroidTypeface() : value); } [selectedBackgroundColorProperty.getDefault](): android.graphics.drawable.Drawable.ConstantState { - let viewGroup = this._textView.getParent(); + const viewGroup = this.nativeView.getParent(); return viewGroup.getBackground().getConstantState(); } [selectedBackgroundColorProperty.setNative](value: Color | android.graphics.drawable.Drawable.ConstantState) { - let viewGroup = this._textView.getParent(); + const viewGroup = this.nativeView.getParent(); if (value instanceof Color) { - let color = value.android; + const color = value.android; const backgroundDrawable = viewGroup.getBackground(); if (apiLevel > 21 && backgroundDrawable && typeof backgroundDrawable.setColorFilter === "function") { const newDrawable = backgroundDrawable.getConstantState().newDrawable(); @@ -168,8 +160,8 @@ export class SegmentedBarItem extends SegmentedBarItemBase { org.nativescript.widgets.ViewHelper.setBackground(viewGroup, newDrawable); } else { const stateDrawable = new android.graphics.drawable.StateListDrawable(); - let colorDrawable: android.graphics.drawable.ColorDrawable = new org.nativescript.widgets.SegmentedBarColorDrawable(color, selectedIndicatorThickness); - let arr = Array.create("int", 1); + const colorDrawable: android.graphics.drawable.ColorDrawable = new org.nativescript.widgets.SegmentedBarColorDrawable(color, selectedIndicatorThickness); + const arr = Array.create("int", 1); arr[0] = R_ATTR_STATE_SELECTED; stateDrawable.addState(arr, colorDrawable); stateDrawable.setBounds(0, 15, viewGroup.getRight(), viewGroup.getBottom()); @@ -183,9 +175,8 @@ export class SegmentedBarItem extends SegmentedBarItemBase { } export class SegmentedBar extends SegmentedBarBase { - private _android: android.widget.TabHost; - private listener: android.widget.TabHost.OnTabChangeListener; - private tabContentFactory: android.widget.TabHost.TabContentFactory; + nativeView: android.widget.TabHost; + private _tabContentFactory: android.widget.TabHost.TabContentFactory; private _addingTab: boolean; public shouldChangeSelectedIndex(): boolean { @@ -195,39 +186,47 @@ export class SegmentedBar extends SegmentedBarBase { public _createNativeView() { initializeNativeClasses(); - this._android = new TabHost(this._context, null); + const context: android.content.Context = this._context; + const nativeView = new TabHost(context, null); - this.listener = this.listener || new TabChangeListener(this); - this.tabContentFactory = this.tabContentFactory || new TabContentFactory(this); - - const tabHostLayout = new android.widget.LinearLayout(this._context); + const tabHostLayout = new android.widget.LinearLayout(context); tabHostLayout.setOrientation(android.widget.LinearLayout.VERTICAL); - const tabWidget = new android.widget.TabWidget(this._context); + const tabWidget = new android.widget.TabWidget(context); tabWidget.setId(R_ID_TABS); tabHostLayout.addView(tabWidget); - const frame = new android.widget.FrameLayout(this._context); + const frame = new android.widget.FrameLayout(context); frame.setId(R_ID_TABCONTENT); frame.setVisibility(android.view.View.GONE); tabHostLayout.addView(frame); - this._android.addView(tabHostLayout); - this._android.setup(); - this._android.setOnTabChangedListener(this.listener); - return this._android; + nativeView.addView(tabHostLayout); + + const listener = new TabChangeListener(this); + nativeView.setOnTabChangedListener(listener); + (nativeView).listener = listener; + nativeView.setup(); + return nativeView; } - get android(): android.widget.TabHost { - return this._android; + public _initNativeView(): void { + const nativeView: any = this.nativeView; + nativeView.listener.owner = this; + this._tabContentFactory = this._tabContentFactory || new TabContentFactory(this); + } + + public _disposeNativeView() { + const nativeView: any = this.nativeView; + nativeView.listener.owner = null; } private insertTab(tabItem: SegmentedBarItem, index: number): void { - const tab = this.android.newTabSpec(index + ""); + const tabHost = this.nativeView; + const tab = tabHost.newTabSpec(index + ""); tab.setIndicator(tabItem.title + ""); - tab.setContent(this.tabContentFactory); + tab.setContent(this._tabContentFactory); - let tabHost = this.android; this._addingTab = true; tabHost.addTab(tab); tabItem.setupNativeView(index); @@ -238,18 +237,20 @@ export class SegmentedBar extends SegmentedBarBase { return -1; } [selectedIndexProperty.setNative](value: number) { - this._android.setCurrentTab(value); + this.nativeView.setCurrentTab(value); } [itemsProperty.getDefault](): SegmentedBarItem[] { return null; } [itemsProperty.setNative](value: SegmentedBarItem[]) { - this._android.clearAllTabs(); + this.nativeView.clearAllTabs(); const newItems = value; if (newItems) { newItems.forEach((item, i, arr) => this.insertTab(item, i)); } + + selectedIndexProperty.coerce(this); } -} +} \ No newline at end of file diff --git a/tns-core-modules/ui/segmented-bar/segmented-bar.ios.ts b/tns-core-modules/ui/segmented-bar/segmented-bar.ios.ts index fe0669111..e0ce4562c 100644 --- a/tns-core-modules/ui/segmented-bar/segmented-bar.ios.ts +++ b/tns-core-modules/ui/segmented-bar/segmented-bar.ios.ts @@ -26,7 +26,7 @@ export class SegmentedBar extends SegmentedBarBase { constructor() { super(); - this._ios = UISegmentedControl.new(); + this.nativeView = this._ios = UISegmentedControl.new(); this._selectionHandler = SelectionHandlerImpl.initWithOwner(new WeakRef(this)); this._ios.addTargetActionForControlEvents(this._selectionHandler, "selected", UIControlEvents.ValueChanged); @@ -58,6 +58,8 @@ export class SegmentedBar extends SegmentedBarBase { segmentedControl.insertSegmentWithTitleAtIndexAnimated(title, index, false); }) } + + selectedIndexProperty.coerce(this); } [selectedBackgroundColorProperty.getDefault](): UIColor { diff --git a/tns-core-modules/ui/slider/slider-common.ts b/tns-core-modules/ui/slider/slider-common.ts index be7dfa29c..ce5617be5 100644 --- a/tns-core-modules/ui/slider/slider-common.ts +++ b/tns-core-modules/ui/slider/slider-common.ts @@ -10,6 +10,8 @@ export class SliderBase extends View implements SliderDefinition { public maxValue: number; } +SliderBase.prototype.recycleNativeView = true; + /** * Represents the observable property backing the value property of each Slider instance. */ diff --git a/tns-core-modules/ui/slider/slider.android.ts b/tns-core-modules/ui/slider/slider.android.ts index 0c292ff01..7940c55ab 100644 --- a/tns-core-modules/ui/slider/slider.android.ts +++ b/tns-core-modules/ui/slider/slider.android.ts @@ -19,7 +19,7 @@ function initializeSeekBarChangeListener(): void { @Interfaces([android.widget.SeekBar.OnSeekBarChangeListener]) class SeekBarChangeListenerImpl extends java.lang.Object implements android.widget.SeekBar.OnSeekBarChangeListener { - constructor(private owner: Slider) { + constructor(public owner: Slider) { super(); return global.__native(this); } @@ -46,19 +46,25 @@ function initializeSeekBarChangeListener(): void { export class Slider extends SliderBase { _supressNativeValue: boolean; - private _android: android.widget.SeekBar; - private changeListener: android.widget.SeekBar.OnSeekBarChangeListener; + nativeView: android.widget.SeekBar; public _createNativeView() { initializeSeekBarChangeListener(); - this.changeListener = this.changeListener || new SeekBarChangeListener(this); - this._android = new android.widget.SeekBar(this._context); - this._android.setOnSeekBarChangeListener(this.changeListener); - return this._android; + const listener = new SeekBarChangeListener(this); + const nativeView = new android.widget.SeekBar(this._context); + nativeView.setOnSeekBarChangeListener(listener); + (nativeView).listener = listener; + return nativeView; } - get android(): android.widget.SeekBar { - return this._android; + public _initNativeView(): void { + const nativeView: any = this.nativeView; + nativeView.listener.owner = this; + } + + public _disposeNativeView() { + const nativeView: any = this.nativeView; + nativeView.listener.owner = null; } /** @@ -68,9 +74,10 @@ export class Slider extends SliderBase { */ private setNativeValuesSilently(newValue: number, newMaxValue: number) { this._supressNativeValue = true; + const nativeView = this.nativeView; try { - this.android.setMax(newMaxValue); - this.android.setProgress(newValue); + nativeView.setMax(newMaxValue); + nativeView.setProgress(newValue); } finally { this._supressNativeValue = false; @@ -93,7 +100,7 @@ export class Slider extends SliderBase { return 100; } [maxValueProperty.setNative](value: number) { - this._android.setMax(value - this.minValue); + this.nativeView.setMax(value - this.minValue); } [colorProperty.getDefault](): number { @@ -101,9 +108,9 @@ export class Slider extends SliderBase { } [colorProperty.setNative](value: number | Color) { if (value instanceof Color) { - this._android.getThumb().setColorFilter(value.android, android.graphics.PorterDuff.Mode.SRC_IN); + this.nativeView.getThumb().setColorFilter(value.android, android.graphics.PorterDuff.Mode.SRC_IN); } else { - this._android.getThumb().clearColorFilter(); + this.nativeView.getThumb().clearColorFilter(); } } @@ -112,9 +119,9 @@ export class Slider extends SliderBase { } [backgroundColorProperty.setNative](value: number | Color) { if (value instanceof Color) { - this._android.getProgressDrawable().setColorFilter(value.android, android.graphics.PorterDuff.Mode.SRC_IN); + this.nativeView.getProgressDrawable().setColorFilter(value.android, android.graphics.PorterDuff.Mode.SRC_IN); } else { - this._android.getProgressDrawable().clearColorFilter(); + this.nativeView.getProgressDrawable().clearColorFilter(); } } diff --git a/tns-core-modules/ui/slider/slider.ios.ts b/tns-core-modules/ui/slider/slider.ios.ts index 556f1e982..81294c841 100644 --- a/tns-core-modules/ui/slider/slider.ios.ts +++ b/tns-core-modules/ui/slider/slider.ios.ts @@ -36,7 +36,7 @@ export class Slider extends SliderBase { constructor() { super(); - this._ios = UISlider.new(); + this.nativeView = this._ios = UISlider.new(); // default values this._ios.minimumValue = 0; diff --git a/tns-core-modules/ui/styling/background.android.ts b/tns-core-modules/ui/styling/background.android.ts index 1f5e3f0c1..785e3128d 100644 --- a/tns-core-modules/ui/styling/background.android.ts +++ b/tns-core-modules/ui/styling/background.android.ts @@ -29,14 +29,14 @@ export module ad { } export function onBackgroundOrBorderPropertyChanged(view: View) { - let nativeView = view._nativeView; + let nativeView = view.nativeView; if (!nativeView) { return; } let background = view.style.backgroundInternal; let backgroundDrawable = nativeView.getBackground(); - let cache = view._nativeView; + let cache = view.nativeView; let viewClass = getClass(view); // always cache the default background constant state. diff --git a/tns-core-modules/ui/styling/background.ios.ts b/tns-core-modules/ui/styling/background.ios.ts index eb5794e55..9345a6f40 100644 --- a/tns-core-modules/ui/styling/background.ios.ts +++ b/tns-core-modules/ui/styling/background.ios.ts @@ -354,9 +354,9 @@ function drawClipPath(nativeView: UIView, background: Background) { function rectPath(value: string, bounds: Rect): UIBezierPath { const arr = value.split(/[\s]+/); const top = cssValueToDeviceIndependentPixels(arr[0], bounds.top); - const left = cssValueToDeviceIndependentPixels(arr[1], bounds.left); + const right = cssValueToDeviceIndependentPixels(arr[1], bounds.right); const bottom = cssValueToDeviceIndependentPixels(arr[2], bounds.bottom); - const right = cssValueToDeviceIndependentPixels(arr[3], bounds.right); + const left = cssValueToDeviceIndependentPixels(arr[3], bounds.left); return UIBezierPath.bezierPathWithRect(CGRectMake(left, top, right - left, bottom - top)).CGPath; } diff --git a/tns-core-modules/ui/switch/switch-common.ts b/tns-core-modules/ui/switch/switch-common.ts index 5d4890493..7044b660b 100644 --- a/tns-core-modules/ui/switch/switch-common.ts +++ b/tns-core-modules/ui/switch/switch-common.ts @@ -7,5 +7,7 @@ export class SwitchBase extends View implements SwitchDefinition { public checked: boolean; } +SwitchBase.prototype.recycleNativeView = true; + export const checkedProperty = new Property({ name: "checked", defaultValue: false, valueConverter: booleanConverter }); -checkedProperty.register(SwitchBase); +checkedProperty.register(SwitchBase); \ No newline at end of file diff --git a/tns-core-modules/ui/switch/switch.android.ts b/tns-core-modules/ui/switch/switch.android.ts index e2354bd7b..0d2fcc9ad 100644 --- a/tns-core-modules/ui/switch/switch.android.ts +++ b/tns-core-modules/ui/switch/switch.android.ts @@ -32,27 +32,33 @@ function initializeCheckedChangeListener(): void { } export class Switch extends SwitchBase { - private _android: android.widget.Switch; - private listener: android.widget.CompoundButton.OnCheckedChangeListener; + nativeView: android.widget.Switch; public checked: boolean; - get android(): android.widget.Switch { - return this._android; - } - public _createNativeView() { initializeCheckedChangeListener(); - this._android = new android.widget.Switch(this._context); - this.listener = this.listener || new CheckedChangeListener(this); - this._android.setOnCheckedChangeListener(this.listener); - return this._android; + const nativeView = new android.widget.Switch(this._context); + const listener = new CheckedChangeListener(this); + nativeView.setOnCheckedChangeListener(listener); + (nativeView).listener = listener; + return nativeView; + } + + public _initNativeView(): void { + const nativeView: any = this.nativeView; + nativeView.listener.owner = this; + } + + public _disposeNativeView() { + const nativeView: any = this.nativeView; + nativeView.listener.owner = null; } [checkedProperty.getDefault](): boolean { return false; } [checkedProperty.setNative](value: boolean) { - this._android.setChecked(value); + this.nativeView.setChecked(value); } [colorProperty.getDefault](): number { @@ -60,9 +66,9 @@ export class Switch extends SwitchBase { } [colorProperty.setNative](value: number | Color) { if (value instanceof Color) { - this._android.getThumbDrawable().setColorFilter(value.android, android.graphics.PorterDuff.Mode.SRC_IN); + this.nativeView.getThumbDrawable().setColorFilter(value.android, android.graphics.PorterDuff.Mode.SRC_IN); } else { - this._android.getThumbDrawable().clearColorFilter(); + this.nativeView.getThumbDrawable().clearColorFilter(); } } @@ -71,9 +77,9 @@ export class Switch extends SwitchBase { } [backgroundColorProperty.setNative](value: number | Color) { if (value instanceof Color) { - this._android.getTrackDrawable().setColorFilter(value.android, android.graphics.PorterDuff.Mode.SRC_IN); + this.nativeView.getTrackDrawable().setColorFilter(value.android, android.graphics.PorterDuff.Mode.SRC_IN); } else { - this._android.getTrackDrawable().clearColorFilter(); + this.nativeView.getTrackDrawable().clearColorFilter(); } } @@ -83,4 +89,4 @@ export class Switch extends SwitchBase { [backgroundInternalProperty.setNative](value: any) { // } -} \ No newline at end of file +} diff --git a/tns-core-modules/ui/switch/switch.ios.ts b/tns-core-modules/ui/switch/switch.ios.ts index 07f6ee511..3fc227f47 100644 --- a/tns-core-modules/ui/switch/switch.ios.ts +++ b/tns-core-modules/ui/switch/switch.ios.ts @@ -28,32 +28,24 @@ class SwitchChangeHandlerImpl extends NSObject { const zeroSize = { width: 0, height: 0 }; export class Switch extends SwitchBase { - private _ios: UISwitch; + nativeView: UISwitch; private _handler: NSObject; constructor() { super(); - this._ios = UISwitch.new(); - + const nativeView = UISwitch.new(); this._handler = SwitchChangeHandlerImpl.initWithOwner(new WeakRef(this)); - this._ios.addTargetActionForControlEvents(this._handler, "valueChanged", UIControlEvents.ValueChanged); + nativeView.addTargetActionForControlEvents(this._handler, "valueChanged", UIControlEvents.ValueChanged); + this.nativeView = nativeView; } get ios(): UISwitch { - return this._ios; - } - - get _nativeView(): UISwitch { - return this._ios; - } - - get nativeView(): UISwitch { - return this._ios; + return this.nativeView; } public onMeasure(widthMeasureSpec: number, heightMeasureSpec: number): void { // It can't be anything different from 51x31 - let nativeSize = this._nativeView.sizeThatFits(zeroSize); + let nativeSize = this.nativeView.sizeThatFits(zeroSize); this.width = nativeSize.width; this.height = nativeSize.height; @@ -66,21 +58,21 @@ export class Switch extends SwitchBase { return false; } [checkedProperty.setNative](value: boolean) { - this._ios.on = value; + this.nativeView.on = value; } [colorProperty.getDefault](): UIColor { - return this._ios.thumbTintColor; + return this.nativeView.thumbTintColor; } [colorProperty.setNative](value: UIColor | Color) { - this._ios.thumbTintColor = value instanceof Color ? value.ios : value; + this.nativeView.thumbTintColor = value instanceof Color ? value.ios : value; } [backgroundColorProperty.getDefault](): UIColor { - return this._ios.onTintColor; + return this.nativeView.onTintColor; } [backgroundColorProperty.setNative](value: UIColor | Color) { - this._ios.onTintColor = value instanceof Color ? value.ios : value; + this.nativeView.onTintColor = value instanceof Color ? value.ios : value; } [backgroundInternalProperty.getDefault](): any { diff --git a/tns-core-modules/ui/tab-view/tab-view.android.ts b/tns-core-modules/ui/tab-view/tab-view.android.ts index 6aeae8221..18704a9f9 100644 --- a/tns-core-modules/ui/tab-view/tab-view.android.ts +++ b/tns-core-modules/ui/tab-view/tab-view.android.ts @@ -35,15 +35,8 @@ function initializeNativeClasses() { } class PagerAdapterImpl extends android.support.v4.view.PagerAdapter { - private owner: TabView; - private items: Array; - - constructor(owner: TabView, items: Array) { + constructor(public owner: TabView, public items: Array) { super(); - - this.owner = owner; - this.items = items; - return global.__native(this); } @@ -73,13 +66,13 @@ function initializeNativeClasses() { if (traceEnabled()) { traceWrite("TabView.PagerAdapter.instantiateItem; restoreHierarchyState: " + item.view, traceCategory); } - item.view._nativeView.restoreHierarchyState(this[VIEWS_STATES]); + item.view.nativeView.restoreHierarchyState(this[VIEWS_STATES]); } - if (item.view._nativeView) { - container.addView(item.view._nativeView); + if (item.view.nativeView) { + container.addView(item.view.nativeView); } - return item.view._nativeView; + return item.view.nativeView; } destroyItem(container: android.view.ViewGroup, index: number, _object: any) { @@ -87,7 +80,7 @@ function initializeNativeClasses() { traceWrite("TabView.PagerAdapter.destroyItem; container: " + container + "; index: " + index + "; _object: " + _object, traceCategory); } let item = this.items[index]; - let nativeView = item.view._nativeView; + let nativeView = item.view.nativeView; if (!nativeView || !_object) { return; @@ -99,7 +92,7 @@ function initializeNativeClasses() { container.removeView(nativeView); - // Note: this.owner._removeView will clear item.view._nativeView. + // Note: this.owner._removeView will clear item.view.nativeView. // So call this after the native instance is removed form the container. // if (item.view.parent === this.owner) { // this.owner._removeView(item.view); @@ -125,7 +118,7 @@ function initializeNativeClasses() { } let viewStates = this[VIEWS_STATES]; let childCallback = function (view: View): boolean { - let nativeView: android.view.View = view._nativeView; + let nativeView: android.view.View = view.nativeView; if (nativeView && nativeView.isSaveFromParentEnabled && nativeView.isSaveFromParentEnabled()) { nativeView.saveHierarchyState(viewStates); } @@ -195,7 +188,7 @@ function getDefaultAccentColor(context: android.content.Context): number { } export class TabViewItem extends TabViewItemBase { - public nativeView: android.widget.TextView; + nativeView: android.widget.TextView; public tabItemSpec: org.nativescript.widgets.TabItemSpec; public index: number; @@ -242,19 +235,22 @@ export class TabViewItem extends TabViewItemBase { } } +function setElevation(grid: org.nativescript.widgets.GridLayout, tabLayout: org.nativescript.widgets.TabLayout) { + const compat = android.support.v4.view.ViewCompat; + if (compat.setElevation) { + const val = DEFAULT_ELEVATION * layout.getDisplayDensity(); + compat.setElevation(grid, val); + compat.setElevation(tabLayout, val); + } +} + export class TabView extends TabViewBase { - private _grid: org.nativescript.widgets.GridLayout; + private _tabLayout: org.nativescript.widgets.TabLayout; private _viewPager: android.support.v4.view.ViewPager; private _pagerAdapter: android.support.v4.view.PagerAdapter; private _androidViewId: number = -1; - private _pageChagedListener: android.support.v4.view.ViewPager.SimpleOnPageChangeListener; - - get android(): android.view.View { - return this._grid; - } - public onItemsChanged(oldItems: TabViewItem[], newItems: TabViewItem[]): void { super.onItemsChanged(oldItems, newItems); @@ -273,57 +269,81 @@ export class TabView extends TabViewBase { traceWrite("TabView._createUI(" + this + ");", traceCategory); } - this._grid = new org.nativescript.widgets.GridLayout(this._context); - this._grid.addRow(new org.nativescript.widgets.ItemSpec(1, org.nativescript.widgets.GridUnitType.auto)); - this._grid.addRow(new org.nativescript.widgets.ItemSpec(1, org.nativescript.widgets.GridUnitType.star)); + const context: android.content.Context = this._context; + const nativeView = new org.nativescript.widgets.GridLayout(context); + 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)); - this._tabLayout = new org.nativescript.widgets.TabLayout(this._context); - this._grid.addView(this._tabLayout); + const tabLayout = new org.nativescript.widgets.TabLayout(context); + nativeView.addView(tabLayout); + (nativeView).tabLayout = tabLayout; - this.setElevation(); + setElevation(nativeView, tabLayout); - const accentColor = getDefaultAccentColor(this._context); + const accentColor = getDefaultAccentColor(context); if (accentColor) { - this._tabLayout.setSelectedIndicatorColors([accentColor]); + tabLayout.setSelectedIndicatorColors([accentColor]); } - const primaryColor = ad.resources.getPalleteColor(PRIMARY_COLOR, this._context); + const primaryColor = ad.resources.getPalleteColor(PRIMARY_COLOR, context); if (primaryColor) { - this._tabLayout.setBackgroundColor(primaryColor); + tabLayout.setBackgroundColor(primaryColor); } + const viewPager = new android.support.v4.view.ViewPager(context); + const lp = new org.nativescript.widgets.CommonLayoutParams(); + lp.row = 1; + viewPager.setLayoutParams(lp); + nativeView.addView(viewPager); + (nativeView).viewPager = viewPager; + + const listener = new PageChangedListener(this); + (viewPager).addOnPageChangeListener(listener); + (viewPager).listener = listener; + + const adapter = new PagerAdapter(this, null); + viewPager.setAdapter(adapter); + (viewPager).adapter = adapter; + + return nativeView; + } + + public _initNativeView(): void { if (this._androidViewId < 0) { this._androidViewId = android.view.View.generateViewId(); } - this._viewPager = new android.support.v4.view.ViewPager(this._context); - this._viewPager.setId(this._androidViewId); - const lp = new org.nativescript.widgets.CommonLayoutParams(); - lp.row = 1; - this._viewPager.setLayoutParams(lp); - this._grid.addView(this._viewPager); + const nativeView: any = this.nativeView; + this._tabLayout = (nativeView).tabLayout; - this._pageChagedListener = new PageChangedListener(this); - (this._viewPager).addOnPageChangeListener(this._pageChagedListener); - this.nativeView = this._viewPager; - this._nativeView = this._viewPager; - return this._grid; + const viewPager = (nativeView).viewPager; + viewPager.setId(this._androidViewId); + this._viewPager = viewPager; + (viewPager).listener.owner = this; + + this._pagerAdapter = (viewPager).adapter; + (this._pagerAdapter).owner = this; } - private setElevation() { - const compat = android.support.v4.view.ViewCompat; - if (compat.setElevation) { - let val = DEFAULT_ELEVATION * layout.getDisplayDensity(); - compat.setElevation(this._grid, val); - compat.setElevation(this._tabLayout, val); - } + public _disposeNativeView() { + // this._tabLayout.setItems(null, null); + this._pagerAdapter.notifyDataSetChanged(); + (this._pagerAdapter).owner = null; + this._pagerAdapter = null; + + // this._viewPager.setAdapter(null); + this._tabLayout = null; + (this._viewPager).listener.owner = null; + this._viewPager = null; } private setAdapter(items: Array) { + (this._pagerAdapter).items = items; + const length = items ? items.length : 0; if (length === 0) { - this._viewPager.setAdapter(null); - this._pagerAdapter = null; + // this._viewPager.setAdapter(null); + // this._pagerAdapter = null; this._tabLayout.setItems(null, null); return; } @@ -336,9 +356,9 @@ export class TabView extends TabViewBase { tabItems.push(tabItemSpec); }); - // TODO: optimize by reusing the adapter and calling setAdapter(null) then the same adapter. - this._pagerAdapter = new PagerAdapter(this, items); - this._viewPager.setAdapter(this._pagerAdapter); + // // TODO: optimize by reusing the adapter and calling setAdapter(null) then the same adapter. + // this._pagerAdapter = new PagerAdapter(this, items); + // this._viewPager.setAdapter(this._pagerAdapter); const tabLayout = this._tabLayout; tabLayout.setItems(tabItems, this._viewPager); @@ -346,6 +366,8 @@ export class TabView extends TabViewBase { const tv = tabLayout.getTextViewForItemAt(i); item.setNativeView(tv); }); + + this._pagerAdapter.notifyDataSetChanged(); } [androidOffscreenTabLimitProperty.getDefault](): number { @@ -407,4 +429,4 @@ export class TabView extends TabViewBase { const color = value instanceof Color ? value.android : value; tabLayout.setSelectedIndicatorColors([color]); } -} +} \ No newline at end of file diff --git a/tns-core-modules/ui/tab-view/tab-view.ios.ts b/tns-core-modules/ui/tab-view/tab-view.ios.ts index 03e837572..a847e7a7f 100644 --- a/tns-core-modules/ui/tab-view/tab-view.ios.ts +++ b/tns-core-modules/ui/tab-view/tab-view.ios.ts @@ -154,6 +154,7 @@ export class TabView extends TabViewBase { super(); this._ios = UITabBarControllerImpl.initWithOwner(new WeakRef(this)); + this.nativeView = this._ios.view; this._delegate = UITabBarControllerDelegateImpl.initWithOwner(new WeakRef(this)); this._moreNavigationControllerDelegate = UINavigationControllerDelegateImpl.initWithOwner(new WeakRef(this)); //This delegate is set on the last line of _addTabs method. @@ -174,13 +175,9 @@ export class TabView extends TabViewBase { return this._ios; } - get nativeView(): UIView { - return this._ios.view; - } - - get _nativeView(): UIView { - return this._ios.view; - } + // get nativeView(): UIView { + // return this._ios.view; + // } public _onViewControllerShown(viewController: UIViewController) { // This method could be called with the moreNavigationController or its list controller, so we have to check. @@ -302,7 +299,7 @@ export class TabView extends TabViewBase { } public onMeasure(widthMeasureSpec: number, heightMeasureSpec: number): void { - const nativeView = this._nativeView; + const nativeView = this.nativeView; if (nativeView) { const width = layout.getMeasureSpecSize(widthMeasureSpec); diff --git a/tns-core-modules/ui/text-base/text-base-common.ts b/tns-core-modules/ui/text-base/text-base-common.ts index 038f3e3c3..2f6f8231e 100644 --- a/tns-core-modules/ui/text-base/text-base-common.ts +++ b/tns-core-modules/ui/text-base/text-base-common.ts @@ -118,7 +118,7 @@ export abstract class TextBaseCommon extends View implements TextBaseDefinition } public _onFormattedTextContentsChanged(data: PropertyChangeData) { - if (this._nativeView) { + if (this.nativeView) { // Notifications from the FormattedString start arriving before the Android view is even created. this[formattedTextProperty.setNative](data.value); } diff --git a/tns-core-modules/ui/text-base/text-base.android.ts b/tns-core-modules/ui/text-base/text-base.android.ts index d847932f7..0c5ca4bbb 100644 --- a/tns-core-modules/ui/text-base/text-base.android.ts +++ b/tns-core-modules/ui/text-base/text-base.android.ts @@ -414,4 +414,4 @@ function setSpanModifiers(ssb: android.text.SpannableStringBuilder, span: Span, // if (letterSpacing > 0) { // ssb.setSpan(new android.text.style.ScaleXSpan((letterSpacing + 1) / 10), start, end, android.text.Spannable.SPAN_EXCLUSIVE_EXCLUSIVE); // } -} +} \ No newline at end of file diff --git a/tns-core-modules/ui/text-field/text-field-common.ts b/tns-core-modules/ui/text-field/text-field-common.ts index 3488ec841..6366b738e 100644 --- a/tns-core-modules/ui/text-field/text-field-common.ts +++ b/tns-core-modules/ui/text-field/text-field-common.ts @@ -8,5 +8,7 @@ export class TextFieldBase extends EditableTextBase implements TextFieldDefiniti public secure: boolean; } +TextFieldBase.prototype.recycleNativeView = true; + export const secureProperty = new Property({ name: "secure", defaultValue: false, valueConverter: booleanConverter }); -secureProperty.register(TextFieldBase); +secureProperty.register(TextFieldBase); \ No newline at end of file diff --git a/tns-core-modules/ui/text-field/text-field.android.ts b/tns-core-modules/ui/text-field/text-field.android.ts index a3c790657..adb0f58a0 100644 --- a/tns-core-modules/ui/text-field/text-field.android.ts +++ b/tns-core-modules/ui/text-field/text-field.android.ts @@ -3,12 +3,17 @@ export * from "./text-field-common"; export class TextField extends TextFieldBase { - public _configureEditText() { - let nativeView = this.android; - nativeView.setInputType(android.text.InputType.TYPE_CLASS_TEXT | android.text.InputType.TYPE_TEXT_VARIATION_NORMAL | android.text.InputType.TYPE_TEXT_FLAG_CAP_SENTENCES); - nativeView.setLines(1); - nativeView.setMaxLines(1); - nativeView.setHorizontallyScrolling(true); + public _configureEditText(editText: android.widget.EditText) { + editText.setInputType(android.text.InputType.TYPE_CLASS_TEXT | android.text.InputType.TYPE_TEXT_VARIATION_NORMAL | android.text.InputType.TYPE_TEXT_FLAG_CAP_SENTENCES); + editText.setLines(1); + editText.setMaxLines(1); + editText.setHorizontallyScrolling(true); + } + + public _initNativeView(): void { + // TODO: We should be able to reset it using only our properties. Check it first. + super._initNativeView(); + this.nativeView.setInputType(android.text.InputType.TYPE_CLASS_TEXT | android.text.InputType.TYPE_TEXT_VARIATION_NORMAL | android.text.InputType.TYPE_TEXT_FLAG_CAP_SENTENCES); } public _onReturnPress() { @@ -29,19 +34,16 @@ export class TextField extends TextFieldBase { if (value) { if (currentClass === android.text.InputType.TYPE_CLASS_TEXT) { newInputType = currentClass | currentFlags | android.text.InputType.TYPE_TEXT_VARIATION_PASSWORD; - } - else if (currentClass === android.text.InputType.TYPE_CLASS_NUMBER) { + } else if (currentClass === android.text.InputType.TYPE_CLASS_NUMBER) { newInputType = currentClass | currentFlags | android.text.InputType.TYPE_NUMBER_VARIATION_PASSWORD; } // Lower all autocapitalization bits, because password bits don't like them and we will receive "Unsupported input type: 16513" error for example. newInputType = newInputType & ~28672; //28672 (0x0070000) 13,14,15 bits (111 0000 0000 0000) - } - else { + } else { if (currentClass === android.text.InputType.TYPE_CLASS_TEXT) { newInputType = currentClass | currentFlags | android.text.InputType.TYPE_TEXT_VARIATION_NORMAL; - } - else if (currentClass === android.text.InputType.TYPE_CLASS_NUMBER) { + } else if (currentClass === android.text.InputType.TYPE_CLASS_NUMBER) { newInputType = currentClass | currentFlags | android.text.InputType.TYPE_NUMBER_VARIATION_NORMAL; } } diff --git a/tns-core-modules/ui/text-view/text-view.android.ts b/tns-core-modules/ui/text-view/text-view.android.ts index bfb157d24..474211afe 100644 --- a/tns-core-modules/ui/text-view/text-view.android.ts +++ b/tns-core-modules/ui/text-view/text-view.android.ts @@ -4,8 +4,16 @@ import { EditableTextBase } from "../editable-text-base"; export * from "../text-base"; export class TextView extends EditableTextBase implements TextViewDefinition { - public _configureEditText() { - this.android.setInputType(android.text.InputType.TYPE_CLASS_TEXT | android.text.InputType.TYPE_TEXT_VARIATION_NORMAL | android.text.InputType.TYPE_TEXT_FLAG_CAP_SENTENCES | android.text.InputType.TYPE_TEXT_FLAG_MULTI_LINE); - this.android.setGravity(android.view.Gravity.TOP | android.view.Gravity.LEFT); + public _configureEditText(editText: android.widget.EditText) { + editText.setInputType(android.text.InputType.TYPE_CLASS_TEXT | android.text.InputType.TYPE_TEXT_VARIATION_NORMAL | android.text.InputType.TYPE_TEXT_FLAG_CAP_SENTENCES | android.text.InputType.TYPE_TEXT_FLAG_MULTI_LINE); + editText.setGravity(android.view.Gravity.TOP | android.view.Gravity.LEFT); + } + + public _initNativeView(): void { + // TODO: We should be able to reset it using only our properties. Check it first. + super._initNativeView(); + this.nativeView.setInputType(android.text.InputType.TYPE_CLASS_TEXT | android.text.InputType.TYPE_TEXT_VARIATION_NORMAL | android.text.InputType.TYPE_TEXT_FLAG_CAP_SENTENCES | android.text.InputType.TYPE_TEXT_FLAG_MULTI_LINE); } } + +TextView.prototype.recycleNativeView = true; \ No newline at end of file diff --git a/tns-core-modules/ui/text-view/text-view.ios.ts b/tns-core-modules/ui/text-view/text-view.ios.ts index 9b571f1e9..3d5e653b1 100644 --- a/tns-core-modules/ui/text-view/text-view.ios.ts +++ b/tns-core-modules/ui/text-view/text-view.ios.ts @@ -67,7 +67,7 @@ export class TextView extends EditableTextBase implements TextViewDefinition { constructor() { super(); - this._ios = UITextView.new(); + this.nativeView = this._ios = UITextView.new(); if (!this._ios.font) { this._ios.font = UIFont.systemFontOfSize(12); } @@ -88,9 +88,9 @@ export class TextView extends EditableTextBase implements TextViewDefinition { return this._ios; } - get nativeView(): UITextView { - return this._ios; - } + // get nativeView(): UITextView { + // return this._ios; + // } public _refreshHintState(hint: string, text: string) { if (this.formattedText) { @@ -269,3 +269,5 @@ export class TextView extends EditableTextBase implements TextViewDefinition { this.nativeView.textContainerInset = { top: inset.top, left: left, bottom: inset.bottom, right: inset.right }; } } + +TextView.prototype.recycleNativeView = true; \ No newline at end of file diff --git a/tns-core-modules/ui/time-picker/time-picker-common.ts b/tns-core-modules/ui/time-picker/time-picker-common.ts index 733aa08eb..428c27b75 100644 --- a/tns-core-modules/ui/time-picker/time-picker-common.ts +++ b/tns-core-modules/ui/time-picker/time-picker-common.ts @@ -20,15 +20,14 @@ export function getValidTime(picker: TimePickerDefinition, hour: number, minute: } let time = { hour: hour, minute: minute }; + if (!isLessThanMaxTime(picker, hour, minute)) { + time = { hour: picker.maxHour, minute: picker.maxMinute }; + } if (!isGreaterThanMinTime(picker, hour, minute)) { time = { hour: picker.minHour, minute: picker.minMinute }; } - if (!isLessThanMaxTime(picker, hour, minute)) { - time = { hour: picker.maxHour, minute: picker.maxMinute }; - } - return time; } @@ -96,101 +95,41 @@ export abstract class TimePickerBase extends View implements TimePickerDefinitio public maxMinute: number; } -export var hourProperty = new Property({ - name: "hour", defaultValue: 0, valueChanged: (picker, oldValue, newValue) => { - if (!isHourValid(newValue)) { - throw new Error(getErrorMessage(picker, "Hour", newValue)); - } - - if (isValidTime(picker)) { - // picker._setNativeTime(); - if (picker.time) { - picker.time.setHours(picker.hour); - } else { - picker.time = new Date(0, 0, 0, picker.hour, picker.minute); - } - } else { - throw new Error(getErrorMessage(picker, "Hour", newValue)); - } - }, valueConverter: (v) => parseInt(v) -}); -hourProperty.register(TimePickerBase); +TimePickerBase.prototype.recycleNativeView = true; export var minHourProperty = new Property({ name: "minHour", defaultValue: 0, valueChanged: (picker, oldValue, newValue) => { - if (!isHourValid(newValue)) { + if (!isHourValid(newValue) || !isValidTime(picker)) { throw new Error(getErrorMessage(picker, "minHour", newValue)); } - if (isValidTime(picker)) { - // picker._setNativeMinTime(); - } else { - throw new Error(getErrorMessage(picker, "Hour", newValue)); - } }, valueConverter: (v) => parseInt(v) }); minHourProperty.register(TimePickerBase); export var maxHourProperty = new Property({ name: "maxHour", defaultValue: 23, valueChanged: (picker, oldValue, newValue) => { - if (!isHourValid(newValue)) { + if (!isHourValid(newValue) || !isValidTime(picker)) { throw new Error(getErrorMessage(picker, "maxHour", newValue)); } - if (isValidTime(picker)) { - // picker._setNativeMaxTime(); - } else { - throw new Error(getErrorMessage(picker, "Hour", newValue)); - } }, valueConverter: (v) => parseInt(v) }); maxHourProperty.register(TimePickerBase); -export var minuteProperty = new Property({ - name: "minute", defaultValue: 0, valueChanged: (picker, oldValue, newValue) => { - if (!isMinuteValid(newValue)) { - throw new Error(getErrorMessage(picker, "minute", newValue)); - } - - if (isValidTime(picker)) { - // picker._setNativeTime(); - if (picker.time) { - picker.time.setMinutes(picker.minute); - } else { - picker.time = new Date(0, 0, 0, picker.hour, picker.minute); - } - } else { - throw new Error(getErrorMessage(picker, "Minute", newValue)); - } - }, valueConverter: (v) => parseInt(v) -}); -minuteProperty.register(TimePickerBase); - export var minMinuteProperty = new Property({ name: "minMinute", defaultValue: 0, valueChanged: (picker, oldValue, newValue) => { - if (!isMinuteValid(newValue)) { + if (!isMinuteValid(newValue) || !isValidTime(picker)) { throw new Error(getErrorMessage(picker, "minMinute", newValue)); } - - if (isValidTime(picker)) { - // picker._setNativeMinTime(); - } else { - throw new Error(getErrorMessage(picker, "Minute", newValue)); - } }, valueConverter: (v) => parseInt(v) }); minMinuteProperty.register(TimePickerBase); export var maxMinuteProperty = new Property({ name: "maxMinute", defaultValue: 59, valueChanged: (picker, oldValue, newValue) => { - if (!isMinuteValid(newValue)) { + if (!isMinuteValid(newValue) || !isValidTime(picker)) { throw new Error(getErrorMessage(picker, "maxMinute", newValue)); } - - if (isValidTime(picker)) { - // picker._setNativeMaxTime(); - } else { - throw new Error(getErrorMessage(picker, "Minute", newValue)); - } }, valueConverter: (v) => parseInt(v) }); maxMinuteProperty.register(TimePickerBase); @@ -208,6 +147,29 @@ function dateComparer(x: Date, y: Date): boolean { return (x <= y && x >= y) ? true : false; } +export var minuteProperty = new Property({ + name: "minute", defaultValue: 0, valueChanged: (picker, oldValue, newValue) => { + if (!isMinuteValid(newValue) || !isValidTime(picker)) { + throw new Error(getErrorMessage(picker, "minute", newValue)); + } + + picker.time = new Date(0, 0, 0, picker.hour, picker.minute); + }, valueConverter: (v) => parseInt(v) +}); +minuteProperty.register(TimePickerBase); + +export var hourProperty = new Property({ + name: "hour", defaultValue: 0, valueChanged: (picker, oldValue, newValue) => { + if (!isHourValid(newValue) || !isValidTime(picker)) { + throw new Error(getErrorMessage(picker, "Hour", newValue)); + } + + picker.time = new Date(0, 0, 0, picker.hour, picker.minute); + + }, valueConverter: (v) => parseInt(v) +}); +hourProperty.register(TimePickerBase); + export var timeProperty = new Property({ name: "time", equalityComparer: dateComparer, valueChanged: (picker, oldValue, newValue) => { if (!isValidTime(picker)) { @@ -216,7 +178,6 @@ export var timeProperty = new Property({ picker.hour = newValue.getHours(); picker.minute = newValue.getMinutes(); - // picker._set } }); -timeProperty.register(TimePickerBase); +timeProperty.register(TimePickerBase); \ No newline at end of file diff --git a/tns-core-modules/ui/time-picker/time-picker.android.ts b/tns-core-modules/ui/time-picker/time-picker.android.ts index fe219fd50..f7d161bf1 100644 --- a/tns-core-modules/ui/time-picker/time-picker.android.ts +++ b/tns-core-modules/ui/time-picker/time-picker.android.ts @@ -13,6 +13,8 @@ function initializeTimeChangedListener(): void { return; } + apiLevel = android.os.Build.VERSION.SDK_INT; + @Interfaces([android.widget.TimePicker.OnTimeChangedListener]) class TimeChangedListenerImpl extends java.lang.Object implements android.widget.TimePicker.OnTimeChangedListener { constructor(public owner: TimePicker) { @@ -22,8 +24,13 @@ function initializeTimeChangedListener(): void { onTimeChanged(picker: android.widget.TimePicker, hour: number, minute: number): void { const timePicker = this.owner; - let validTime = getValidTime(timePicker, hour, minute); - timePicker._setNativeValueSilently(validTime.hour, validTime.minute); + if (timePicker.updatingNativeValue) { + return; + } + + const validTime = getValidTime(timePicker, hour, minute); + hourProperty.nativeValueChange(timePicker, validTime.hour); + minuteProperty.nativeValueChange(timePicker, validTime.minute); timeProperty.nativeValueChange(timePicker, new Date(0, 0, 0, validTime.hour, validTime.minute)); } } @@ -31,71 +38,59 @@ function initializeTimeChangedListener(): void { TimeChangedListener = TimeChangedListenerImpl; } +let apiLevel: number; + export class TimePicker extends TimePickerBase { - private _android: android.widget.TimePicker; - private _listener: android.widget.TimePicker.OnTimeChangedListener; + nativeView: android.widget.TimePicker; + updatingNativeValue: boolean; public _createNativeView() { initializeTimeChangedListener(); - this._android = new android.widget.TimePicker(this._context); - this._listener = this._listener || new TimeChangedListener(this); - this._android.setOnTimeChangedListener(this._listener); - - let c = java.util.Calendar.getInstance(); - if (this.hour === 0) { - this.hour = c.get(java.util.Calendar.HOUR_OF_DAY); - } - - if (this.minute === 0) { - this.minute = c.get(java.util.Calendar.MINUTE); - } - - let validTime = getValidTime(this, this.hour, this.minute); - this._setNativeValueSilently(validTime.hour, validTime.minute); - return this._android; + const nativeView = new android.widget.TimePicker(this._context); + const listener = new TimeChangedListener(this); + nativeView.setOnTimeChangedListener(listener); + (nativeView).listener = listener; + (nativeView).calendar = java.util.Calendar.getInstance(); + return nativeView; } - get android(): android.widget.TimePicker { - return this._android; - } + public _initNativeView(): void { + const nativeView: any = this.nativeView; + nativeView.listener.owner = this; - public _setNativeValueSilently(hour: number, minute: number) { - if (this._android) { - this._android.setOnTimeChangedListener(null); + const calendar = (nativeView).calendar; + const hour = hourProperty.isSet(this) ? this.hour : calendar.get(java.util.Calendar.HOUR_OF_DAY); + const minute = minuteProperty.isSet(this) ? this.minute : calendar.get(java.util.Calendar.MINUTE); - this._android.setCurrentHour(new java.lang.Integer(hour)); - this._android.setCurrentMinute(new java.lang.Integer(minute)); - - this.minute = minute; - this.hour = hour; - - this._android.setOnTimeChangedListener(this._listener); + const validTime = getValidTime(this, hour, minute); + if (!timeProperty.isSet(this)) { + this.time = new Date(0, 0, 0, validTime.hour, validTime.minute); } } - public _setNativeTime() { - this._setNativeValueSilently(this.hour, this.minute); - } - - [timeProperty.getDefault](): Date { - let nativeView = this._android; - return new Date(0, 0, 0, nativeView.getCurrentHour().intValue(), nativeView.getCurrentMinute().intValue()); - } - [timeProperty.setNative](value: Date) { - this._setNativeValueSilently(this.hour, this.minute); - } - - [minuteProperty.getDefault](): number { - return this._android.getCurrentMinute().intValue(); - } [minuteProperty.setNative](value: number) { - this._setNativeValueSilently(this.hour, value); + this.updatingNativeValue = true; + try { + if (apiLevel >= 23) { + (this.nativeView).setMinute(value); + } else { + this.nativeView.setCurrentMinute(new java.lang.Integer(value)); + } + } finally { + this.updatingNativeValue = false; + } } - [hourProperty.getDefault](): number { - return this._android.getCurrentHour().intValue() - } [hourProperty.setNative](value: number) { - this._setNativeValueSilently(value, this.minute); + this.updatingNativeValue = true; + try { + if (apiLevel >= 23) { + (this.nativeView).setHour(value); + } else { + this.nativeView.setCurrentHour(new java.lang.Integer(value)); + } + } finally { + this.updatingNativeValue = false; + } } } \ No newline at end of file diff --git a/tns-core-modules/ui/transition/transition.android.ts b/tns-core-modules/ui/transition/transition.android.ts index 60aa41da1..17b6aea7c 100644 --- a/tns-core-modules/ui/transition/transition.android.ts +++ b/tns-core-modules/ui/transition/transition.android.ts @@ -422,17 +422,17 @@ function _completePageRemoval(fragment: any, isBack: boolean) { } export function _removePageNativeViewFromAndroidParent(page: Page): void { - if (page._nativeView && page._nativeView.getParent) { - let androidParent = page._nativeView.getParent(); + if (page.nativeView && page.nativeView.getParent) { + let androidParent = page.nativeView.getParent(); if (androidParent && androidParent.removeView) { if (traceEnabled()) { - traceWrite(`REMOVED ${page}._nativeView from its Android parent`, traceCategories.Transition); + traceWrite(`REMOVED ${page}.nativeView from its Android parent`, traceCategories.Transition); } if (page._context) { page._tearDownUI(true); } - androidParent.removeView(page._nativeView); + androidParent.removeView(page.nativeView); } } } diff --git a/tns-core-modules/ui/utils.ios.ts b/tns-core-modules/ui/utils.ios.ts index 5335631fa..e79b46601 100644 --- a/tns-core-modules/ui/utils.ios.ts +++ b/tns-core-modules/ui/utils.ios.ts @@ -31,7 +31,7 @@ export module ios { let width = utils.layout.toDevicePixels(size.width); let height = utils.layout.toDevicePixels(size.height); - var superview = (rootView._nativeView).superview; + var superview = (rootView.nativeView).superview; var superViewRotationRadians; if (superview) { superViewRotationRadians = atan2f(superview.transform.b, superview.transform.a); diff --git a/tns-core-modules/ui/web-view/web-view.android.ts b/tns-core-modules/ui/web-view/web-view.android.ts index e320d6472..de11a3cf2 100644 --- a/tns-core-modules/ui/web-view/web-view.android.ts +++ b/tns-core-modules/ui/web-view/web-view.android.ts @@ -14,12 +14,9 @@ function initializeWebViewClient(): void { } class WebViewClientImpl extends android.webkit.WebViewClient { - private _view: WebViewBase; - constructor(view: WebViewBase) { + constructor(public owner: WebViewBase) { super(); - - this._view = view; return global.__native(this); } @@ -32,23 +29,23 @@ function initializeWebViewClient(): void { public onPageStarted(view: android.webkit.WebView, url: string, favicon: android.graphics.Bitmap) { super.onPageStarted(view, url, favicon); - - if (this._view) { + const owner = this.owner; + if (owner) { if (traceEnabled()) { traceWrite("WebViewClientClass.onPageStarted(" + url + ", " + favicon + ")", traceCategories.Debug); } - this._view._onLoadStarted(url, WebViewBase.navigationTypes[WebViewBase.navigationTypes.indexOf("linkClicked")]); + owner._onLoadStarted(url, WebViewBase.navigationTypes[WebViewBase.navigationTypes.indexOf("linkClicked")]); } } public onPageFinished(view: android.webkit.WebView, url: string) { super.onPageFinished(view, url); - - if (this._view) { + const owner = this.owner; + if (owner) { if (traceEnabled()) { traceWrite("WebViewClientClass.onPageFinished(" + url + ")", traceCategories.Debug); } - this._view._onLoadFinished(url, undefined); + owner._onLoadFinished(url, undefined); } } @@ -62,23 +59,24 @@ function initializeWebViewClient(): void { super.onReceivedError(view, errorCode, description, failingUrl); - if (this._view) { + const owner = this.owner; + if (owner) { if (traceEnabled()) { traceWrite("WebViewClientClass.onReceivedError(" + errorCode + ", " + description + ", " + failingUrl + ")", traceCategories.Debug); } - this._view._onLoadFinished(failingUrl, description + "(" + errorCode + ")"); + owner._onLoadFinished(failingUrl, description + "(" + errorCode + ")"); } } else { let request: any = arguments[1]; let error: any = arguments[2]; super.onReceivedError(view, request, error); - - if (this._view) { + const owner = this.owner; + if (owner) { if (traceEnabled()) { traceWrite("WebViewClientClass.onReceivedError(" + error.getErrorCode() + ", " + error.getDescription() + ", " + (error.getUrl && error.getUrl()) + ")", traceCategories.Debug); } - this._view._onLoadFinished(error.getUrl && error.getUrl(), error.getDescription() + "(" + error.getErrorCode() + ")"); + owner._onLoadFinished(error.getUrl && error.getUrl(), error.getDescription() + "(" + error.getErrorCode() + ")"); } } } @@ -88,79 +86,100 @@ function initializeWebViewClient(): void { } export class WebView extends WebViewBase { - private _android: android.webkit.WebView; - private _webViewClient: android.webkit.WebViewClient; - - get android(): android.webkit.WebView { - return this._android; - } + nativeView: android.webkit.WebView; public _createNativeView() { initializeWebViewClient(); - this._webViewClient = new WebViewClient(this); - this._android = new android.webkit.WebView(this._context); - this._android.getSettings().setJavaScriptEnabled(true); - this._android.getSettings().setBuiltInZoomControls(true); - this._android.setWebViewClient(this._webViewClient); - return this._android; + + const nativeView = new android.webkit.WebView(this._context); + nativeView.getSettings().setJavaScriptEnabled(true); + nativeView.getSettings().setBuiltInZoomControls(true); + const client = new WebViewClient(this); + nativeView.setWebViewClient(client); + (nativeView).client = client; + return nativeView; + } + + public _initNativeView(): void { + (this.nativeView).client.owner = this; } public _resetNativeView() { - if (this.android) { - this.android.destroy(); + const nativeView = this.nativeView; + if (nativeView) { + nativeView.destroy(); } + + (nativeView).client.owner = null; super._resetNativeView(); } public _loadFileOrResource(path: string, content: string) { - if (!this._android) { + const nativeView = this.nativeView; + if (!nativeView) { return; } const baseUrl = `file:///${path.substring(0, path.lastIndexOf('/') + 1)}`; - this._android.loadDataWithBaseURL(baseUrl, content, "text/html", "utf-8", null); + nativeView.loadDataWithBaseURL(baseUrl, content, "text/html", "utf-8", null); } public _loadHttp(src: string) { - if (!this._android) { + const nativeView = this.nativeView; + if (!nativeView) { return; } - this._android.loadUrl(src); + nativeView.loadUrl(src); } public _loadData(src: string) { - if (!this._android) { + const nativeView = this.nativeView; + if (!nativeView) { return; } const baseUrl = `file:///${knownFolders.currentApp().path}/`; - this._android.loadDataWithBaseURL(baseUrl, src, "text/html", "utf-8", null); + nativeView.loadDataWithBaseURL(baseUrl, src, "text/html", "utf-8", null); } get canGoBack(): boolean { - return this._android.canGoBack(); + return this.nativeView.canGoBack(); } public stopLoading() { - if (this._android) { - this._android.stopLoading(); + const nativeView = this.nativeView; + if (nativeView) { + nativeView.stopLoading(); } } get canGoForward(): boolean { - return this._android.canGoForward(); + const nativeView = this.nativeView; + if (nativeView) { + return nativeView.canGoForward(); + } + return false; } public goBack() { - this._android.goBack(); + const nativeView = this.nativeView; + if (nativeView) { + return nativeView.goBack(); + } } public goForward() { - this._android.goForward(); + const nativeView = this.nativeView; + if (nativeView) { + return nativeView.goForward(); + } } public reload() { - this._android.reload(); + const nativeView = this.nativeView; + if (nativeView) { + return nativeView.reload(); + } } } \ No newline at end of file diff --git a/tns-core-modules/ui/web-view/web-view.ios.ts b/tns-core-modules/ui/web-view/web-view.ios.ts index 7472a210c..9b68559a9 100644 --- a/tns-core-modules/ui/web-view/web-view.ios.ts +++ b/tns-core-modules/ui/web-view/web-view.ios.ts @@ -87,7 +87,7 @@ export class WebView extends WebViewBase { constructor() { super(); - this._ios = UIWebView.new(); + this.nativeView = this._ios = UIWebView.new(); this._delegate = UIWebViewDelegateImpl.initWithOwner(new WeakRef(this)); } diff --git a/tns-platform-declarations/android/org.nativescript.widgets.d.ts b/tns-platform-declarations/android/org.nativescript.widgets.d.ts index cbd717bbf..7add67918 100644 --- a/tns-platform-declarations/android/org.nativescript.widgets.d.ts +++ b/tns-platform-declarations/android/org.nativescript.widgets.d.ts @@ -343,8 +343,8 @@ getRotationAngle(): number; setRotationAngle(angle: number): void; - setUri(uri: string, decodeWidth: number, decodeHeight: number, useCache: boolean, - async: boolean, listener: image.Worker.IOnImageLoadedListener): void; + setUri(uri: string, decodeWidth: number, decodeHeight: number, useCache: boolean, async: boolean): void; + setImageLoadedListener(listener: image.Worker.OnImageLoadedListener): void; } export class TabLayout extends android.widget.HorizontalScrollView { diff --git a/tsconfig.json b/tsconfig.json index 692faec70..fdde277ed 100644 --- a/tsconfig.json +++ b/tsconfig.json @@ -21,6 +21,7 @@ "obj", "tns-platform-declarations/references.d.ts", "tns-core-modules/references.d.ts", - "tns-platform-declarations/ios/objc-x86_64/" + "tns-platform-declarations/ios/objc-x86_64/", + "node-tests/" ] }