diff --git a/apps/tests/ui/scroll-view/scroll-view-tests.ts b/apps/tests/ui/scroll-view/scroll-view-tests.ts index 1c359919a..3446ac9fb 100644 --- a/apps/tests/ui/scroll-view/scroll-view-tests.ts +++ b/apps/tests/ui/scroll-view/scroll-view-tests.ts @@ -265,6 +265,50 @@ class ScrollLayoutTest extends testModule.UITest { // Check verticalOffset after navigation TKUnit.assertAreClose(this.testView.horizontalOffset, 100, 0.1, "this.testView.horizontalOffset after navigation"); } + + public test_scrollView_vertical_raised_scroll_event() { + this.testView.orientation = enums.Orientation.vertical; + + var scrollY: number; + this.testView.on(scrollViewModule.ScrollView.scrollEvent, (args: scrollViewModule.ScrollEventData) => { + scrollY = args.scrollY; + }); + + this.testView.width = 200; + this.testView.height = 300; + + var btn = new button.Button(); + btn.text = "test"; + btn.height = 500; + this.testView.content = btn; + this.waitUntilTestElementLayoutIsValid(); + + this.testView.scrollToVerticalOffset(100, false); + TKUnit.waitUntilReady(function () { return scrollY > 0; }); + TKUnit.assertEqual(this.testView.verticalOffset, scrollY); + } + + public test_scrollView_horizontal_raised_scroll_event() { + this.testView.orientation = enums.Orientation.horizontal; + + var scrollX: number; + this.testView.on(scrollViewModule.ScrollView.scrollEvent, (args: scrollViewModule.ScrollEventData) => { + scrollX = args.scrollX; + }); + + this.testView.width = 200; + this.testView.height = 300; + + var btn = new button.Button(); + btn.text = "test"; + btn.width = 500; + this.testView.content = btn; + this.waitUntilTestElementLayoutIsValid(); + + this.testView.scrollToHorizontalOffset(100, false); + TKUnit.waitUntilReady(function () { return scrollX > 0; }); + TKUnit.assertEqual(this.testView.horizontalOffset, scrollX); + } } export function createTestCase(): ScrollLayoutTest { diff --git a/ui/scroll-view/scroll-view-common.ts b/ui/scroll-view/scroll-view-common.ts index fe3c04b17..9d0836cca 100644 --- a/ui/scroll-view/scroll-view-common.ts +++ b/ui/scroll-view/scroll-view-common.ts @@ -1,6 +1,8 @@ import dependencyObservable = require("ui/core/dependency-observable"); import proxy = require("ui/core/proxy"); import enums = require("ui/enums"); +import definition = require("ui/scroll-view"); +import contentView = require("ui/content-view"); function isValidOrientation(value: any): boolean { return value === enums.Orientation.vertical || value === enums.Orientation.horizontal; @@ -13,4 +15,92 @@ export var orientationProperty = new dependencyObservable.Property( dependencyObservable.PropertyMetadataSettings.AffectsLayout, undefined, isValidOrientation) - ); \ No newline at end of file +); + +export class ScrollView extends contentView.ContentView implements definition.ScrollView { + private _scrollEventAttached: boolean; + public static scrollEvent = "scroll"; + + get orientation(): string { + return this._getValue(orientationProperty); + } + set orientation(value: string) { + this._setValue(orientationProperty, value); + } + + public addEventListener(arg: string, callback: any, thisArg?: any) { + super.addEventListener(arg, callback, thisArg); + + if (arg === definition.ScrollView.scrollEvent) { + this.attach(); + } + } + + public removeEventListener(arg: string, callback: any, thisArg?: any) { + super.addEventListener(arg, callback, thisArg); + + if (arg === definition.ScrollView.scrollEvent) { + this.dettach(); + } + } + + public onLoaded() { + super.onLoaded(); + + this.attach(); + } + + public onUnloaded() { + super.onUnloaded(); + + this.dettach(); + } + + private attach() { + if (!this._scrollEventAttached && this.isLoaded) { + this._scrollEventAttached = true; + + this.attachNative(); + } + } + + private dettach() { + if (this._scrollEventAttached && this.isLoaded) { + this._scrollEventAttached = false; + + this.dettachNative(); + } + } + + protected attachNative() { + // + } + + protected dettachNative() { + // + } + + get horizontalOffset(): number { + return 0; + } + + get verticalOffset(): number { + return 0; + } + + get scrollableWidth(): number { + return 0; + } + + get scrollableHeight(): number { + return 0; + } + + public scrollToVerticalOffset(value: number, animated: boolean) { + // + } + + public scrollToHorizontalOffset(value: number, animated: boolean) { + // + } +} \ No newline at end of file diff --git a/ui/scroll-view/scroll-view.android.ts b/ui/scroll-view/scroll-view.android.ts index db5a36cb6..d66ccea5a 100644 --- a/ui/scroll-view/scroll-view.android.ts +++ b/ui/scroll-view/scroll-view.android.ts @@ -1,9 +1,9 @@ import dependencyObservable = require("ui/core/dependency-observable"); import definition = require("ui/scroll-view"); -import contentView = require("ui/content-view"); import common = require("./scroll-view-common"); import utils = require("utils/utils"); import enums = require("ui/enums"); +import proxy = require("ui/core/proxy"); global.moduleMerge(common, exports); @@ -11,9 +11,10 @@ common.orientationProperty.metadata.onValueChanged = function scrollViewOrientat (data.object)._onOrientationChanged(data.oldValue, data.newValue); } -export class ScrollView extends contentView.ContentView implements definition.ScrollView { +export class ScrollView extends common.ScrollView implements definition.ScrollView { private _android: org.nativescript.widgets.VerticalScrollView | org.nativescript.widgets.HorizontalScrollView; private _androidViewId: number; + private handler: android.view.ViewTreeObserver.OnScrollChangedListener; get android(): android.view.ViewGroup { return this._android; @@ -23,13 +24,6 @@ export class ScrollView extends contentView.ContentView implements definition.Sc return this._android; } - get orientation(): string { - return this._getValue(common.orientationProperty); - } - set orientation(value: string) { - this._setValue(common.orientationProperty, value); - } - get horizontalOffset(): number { if (!this._android) { return 0; @@ -68,8 +62,7 @@ export class ScrollView extends contentView.ContentView implements definition.Sc if (animated) { this._android.smoothScrollTo(0, value); - } - else { + } else { this._android.scrollTo(0, value); } } @@ -81,8 +74,7 @@ export class ScrollView extends contentView.ContentView implements definition.Sc if (animated) { this._android.smoothScrollTo(value, 0); - } - else { + } else { this._android.scrollTo(value, 0); } } @@ -91,8 +83,7 @@ export class ScrollView extends contentView.ContentView implements definition.Sc public _createUI() { if (this.orientation === enums.Orientation.horizontal) { this._android = new org.nativescript.widgets.HorizontalScrollView(this._context); - } - else { + } else { this._android = new org.nativescript.widgets.VerticalScrollView(this._context); } @@ -101,17 +92,6 @@ export class ScrollView extends contentView.ContentView implements definition.Sc } this._android.setId(this._androidViewId); - - var that = new WeakRef(this); - this._android.getViewTreeObserver().addOnScrollChangedListener(new android.view.ViewTreeObserver.OnScrollChangedListener({ - onScrollChanged: function () { - var rootScrollView = that.get(); - if (rootScrollView && rootScrollView.android) { - var scrollX = rootScrollView.android.getScrollX(); //for horizontalScrollView - var scrollY = rootScrollView.android.getScrollY(); //for verticalScrollView - } - } - }); } public _onOrientationChanged(oldValue: string, newValue: string) { @@ -127,4 +107,28 @@ export class ScrollView extends contentView.ContentView implements definition.Sc } } } + + protected attachNative() { + var that = new WeakRef(this); + this.handler = new android.view.ViewTreeObserver.OnScrollChangedListener({ + onScrollChanged: function () { + var rootScrollView = that.get(); + if (rootScrollView && rootScrollView.android) { + rootScrollView.notify({ + object: rootScrollView, + eventName: definition.ScrollView.scrollEvent, + scrollX: rootScrollView.android.getScrollX() / utils.layout.getDisplayDensity(), + scrollY: rootScrollView.android.getScrollY() / utils.layout.getDisplayDensity() + }); + } + } + }); + + this._android.getViewTreeObserver().addOnScrollChangedListener(this.handler); + } + + protected dettachNative() { + this._android.getViewTreeObserver().removeOnScrollChangedListener(this.handler); + this.handler = null; + } } diff --git a/ui/scroll-view/scroll-view.d.ts b/ui/scroll-view/scroll-view.d.ts index a69d750f5..169f49854 100644 --- a/ui/scroll-view/scroll-view.d.ts +++ b/ui/scroll-view/scroll-view.d.ts @@ -3,11 +3,22 @@ */ declare module "ui/scroll-view" { import contentView = require("ui/content-view"); + import observable = require("data/observable"); + import dependencyObservable = require("ui/core/dependency-observable"); + + export var orientationProperty: dependencyObservable.Property; /** * Represents a scrollable area that can have content that is larger than its bounds. */ class ScrollView extends contentView.ContentView { + public static orientationProperty: dependencyObservable.Property; + + /** + * String value used when hooking to scroll event. + */ + public static scrollEvent: string; + /** * Gets a value that contains the vertical offset of the scrolled content. */ @@ -56,8 +67,13 @@ declare module "ui/scroll-view" { on(eventNames: string, callback: (data: observable.EventData) => void, thisArg?: any); /** - * Raised when a tap event occurs. + * Raised when a scroll event occurs. */ - on(event: "scroll", callback: (args: observable.EventData) => void, thisArg?: any); + on(event: "scroll", callback: (args: ScrollEventData) => void, thisArg?: any); + } + + interface ScrollEventData extends observable.EventData { + scrollX: number; + scrollY: number; } } \ No newline at end of file diff --git a/ui/scroll-view/scroll-view.ios.ts b/ui/scroll-view/scroll-view.ios.ts index dfe97584d..a3e1c1d8b 100644 --- a/ui/scroll-view/scroll-view.ios.ts +++ b/ui/scroll-view/scroll-view.ios.ts @@ -1,6 +1,5 @@ import view = require("ui/core/view"); import definition = require("ui/scroll-view"); -import contentView = require("ui/content-view"); import common = require("./scroll-view-common"); import enums = require("ui/enums"); import utils = require("utils/utils"); @@ -22,11 +21,18 @@ class UIScrollViewDelegateImpl extends NSObject implements UIScrollViewDelegate } public scrollViewDidScroll(textView: UIScrollView): void { - + if (this._owner) { + this._owner.notify({ + object: this._owner, + eventName: definition.ScrollView.scrollEvent, + scrollX: this._owner.horizontalOffset, + scrollY: this._owner.verticalOffset + }); + } } } -export class ScrollView extends contentView.ContentView implements definition.ScrollView { +export class ScrollView extends common.ScrollView implements definition.ScrollView { private _scroll: UIScrollView; private _contentMeasuredWidth: number = 0; private _contentMeasuredHeight: number = 0; @@ -35,25 +41,15 @@ export class ScrollView extends contentView.ContentView implements definition.Sc constructor() { super(); this._scroll = new UIScrollView(); - - this._delegate = UIScrollViewDelegateImpl.new().initWithOwner(this); } - public onLoaded() { - super.onLoaded(); + protected attachNative() { + this._delegate = UIScrollViewDelegateImpl.new().initWithOwner(this); this._scroll.delegate = this._delegate; } - public onUnloaded() { + protected dettachNative() { this._scroll.delegate = null; - super.onUnloaded(); - } - - get orientation(): string { - return this._getValue(common.orientationProperty); - } - set orientation(value: string) { - this._setValue(common.orientationProperty, value); } get horizontalOffset(): number {