コアコンセプト / イベント

イベントとイベント処理

イベントは、特定のアクションの発生を示すためにイベントエミッタから送信されるメッセージです。 この動作は、ユーザの動作(タップなど)またはプログラムロジック(たとえば、サーバからの画像のダウンロードが完了したことを示すなど)によって生成されます。 イベントを発生させるオブジェクトは、イベント送信者(sender)またはイベント発生者(event raiser)と呼ばれます。 イベントを消費するオブジェクトは、イベントリスナー(listener)またはイベントハンドラ(event handler)と呼ばれます。

NativeScriptフレームワークは、イベントを扱うプロセスを強化するObservableクラスを提供します。 NativeScriptフレームワーク内の基本クラスの1つであるため、ほとんどすべてのNativeScriptオブジェクト(コンポーネント)には、イベントを処理するためのオプションがあります。

イベントリスナの追加

イベントハンドラを追加することは、イベントが発生したときに実行される関数(メソッド)を設定することを意味します。

JavaScript/TypeScriptを使用したイベントリスナーの追加

以下の例は、短縮構文(on)と完全な構文(addEventListener)を使用してイベントリスナーを追加する方法を示しています。 thisで表される3番目のオプション引数があります。このコードは、 "Hello World!"を表示する関数を設定する方法を示しています。 ボタンをタップするとコンソールにメッセージが表示されます。短縮構文と完全構文のどちらかを選択することも、XMLでイベントハンドラを宣言することもできます。

ヒント:この記事のすべての例は、NativeScript Playgroundでプレビューできます。この例はJavaScriptまたはTypeScriptで実行してください。

const Button = require("tns-core-modules/ui/button").Button;
const testButton = new Button();
testButton.text = "Test";

let onTap = function(args) {
	console.log("Hello World!");
};
// Adding a listener with the short syntax
testButton.on(Button.tapEvent, onTap, this);

// Adding a listener with the full syntax
testButton.addEventListener(Button.tapEvent, onTap, this);
import { Button } from "tns-core-modules/ui/button";
import { GestureEventData } from "tns-core-modules/ui/gestures";
const testButton = new Button();
testButton.text = "Test";

export function onTap(args: GestureEventData) {
	console.log("Tap arguments: ", args);
};

// Adding a listener with the short syntax
testButton.on(Button.tapEvent, onTap, this);

// Adding a lister with the full syntax
testButton.addEventListener(Button.tapEvent, onTap, this);

XML宣言を使用したイベントリスナーの追加

イベントハンドラを設定するもう1つの選択肢は、XML宣言を使用することです。 関数本体を作成するには、分離コードファイルが必要です(分離コードファイルのファイル名は同じですが、拡張子は.jsまたは.tsです)。

<!-- main-page.xml -->
<Page>
	<StackLayout>
		<Label touch="onTouch" />
	</StackLayout>
</Page>
// main-page.js
function onTouch(args) {
	console.log("Touch arguments: ", args);
}
exports.onTouch = onTouch;
// main-page.ts
export function onTouch(args: TouchGestureEventData) {
	console.log("Touch arguments", args);
}

MVVMパターンを使用したイベントリスナーの追加

多くの場合、NativeScriptではMVVMパターンはビューのバインディングコンテキストを提供する独立したビューモデルと共に使用されます。 そのような場合、イベントハンドラはバインディングコンテキスト構文を介して提供されなければなりません。

JavaScriptの例

// main-view-model.js
const observableModule = require("tns-core-modules/data/observable");

function HomeViewModel() {
	var viewModel = observableModule.fromObject({
		onButtonTap: function (args) {
			console.log("Button was pressed");
		},
	});

	return viewModel;
}
module.exports = HomeViewModel;
// main-page.js
const HomeViewModel = require("./home-view-model");

function pageLoaded(args) {
	const page = args.object;

	const homeViewModel = new HomeViewModel();
	page.bindingContext = homeViewModel;
}
exports.pageLoaded = pageLoaded;
<StackLayout class="home-panel">
	<Button text="Button" tap="" />
</StackLayout>

TypeScriptの例

// main-view-model.ts
import { Observable } from 'tns-core-modules/data/observable';
import { GestureEventData } from "tns-core-modules/ui/gestures";

export class HomeViewModel extends Observable {
	onButtonTap(args: GestureEventData): void {
		console.log("Button was pressed");
	}

	constructor() {
		super();
	}
}
// main-page.ts
import { EventData } from 'tns-core-modules/data/observable';
import { Page } from 'tns-core-modules/ui/page';
import { HomeViewModel } from './home-view-model';

export function pageLoaded(args: EventData) {
	const page = <Page>args.object;
page.bindingContext = new HomeViewModel();
}
<StackLayout class="home-panel">
	<Button text="Button" tap="" />
</StackLayout>

イベントリスナの削除

通常は、イベントリスナーを削除する必要はありません。ただし、イベントを1回だけ受信したい場合やリソースを解放したい場合に、削除する必要があります。

注釈:XML宣言を通じてイベントリスナーを削除するための構文はありません。

JavaScript/TypeScriptを使用したイベントリスナーの削除

以下の例では、testButtonインスタンスのtapイベントに対するすべてのリスナーを削除するために、短縮構文と完全構文を使用しています。 複数のオブジェクトがイベントリスナーの場合は、2番目のパラメータにコールバック関数の名前を設定できます。 このようにして、参照されたイベントリスナーだけが削除されます。異なるthis引数を持つ複数のイベントリスナが利用可能な場合、3番目のオプションパラメータが使用されます。

ボタンタップイベントリスナーの削除

const Button = require("tns-core-modules/ui/button").Button;

// Removing a listener with the short syntax
testButton.off(Button.tapEvent);

// Removing a listener with the full syntax
testButton2.removeEventListener(Button.tapEvent);
import { Button } from "tns-core-modules/ui/button";

// Removing a listener with the short syntax
testButton.off(Button.tapEvent);

// Removing a listener with the full syntax
testButton2.removeEventListener(Button.tapEvent);

イベントデータ型

イベントの引数の基本型はEventData型です。これは2つの共通のプロパティを提供します。

// example for using EventData interface
exports.onPageLoaded = function(args) {
	let page = args.object;
}
// example for using EventData interface
export function onPageLoaded(args: EventData) {
	let page = <Page>args.object;
}
<Page loaded="onPageLoaded">

NativeScriptには、EventDataインターフェースを拡張して特定のイベントに追加の機能を提供する特定のインターフェースが多数あります。 たとえば、TouchGestureEventDataは、 action,android,ios,type,view などの追加のプロパティを持つTouchイベントに提供されるインタフェースです。 特定のイベントを処理するときは、処理しているイベントデータの特定の引数についてAPIリファレンスを確認してください。

PropertyChangeイベント

このObservableクラスは、プロパティが変更されたときに呼び出される組み込みイベントpropertyChangeを提供します。

propertyChangeイベントの処理

以下のデモは、propertyChangeイベントを取得する方法を示しています。

const Observable = require("tns-core-modules/data/observable").Observable;
const observableObject = new Observable();

observableObject.on(Observable.propertyChangeEvent, function(propertyChangeData){
	console.log(propertyChangeData.propertyName + " has been changed and the new value is: " + propertyChangeData.value);
});
import { Observable } from "tns-core-modules/data/observable";
const observableObject = new Observable();

observableObject.on(Observable.propertyChangeEvent, function(propertyChangeData: PropertyChangeData){
	console.log(propertyChangeData.propertyName + " has been changed and the new value is: " + propertyChangeData.value);
});

カスタムクラスの作成と継承Observableクラス

このpropertyChangeイベントはデータバインディングシステム全体にとって重要です。 データバインディングメカニズムを利用するには、ビジネスオブジェクトにObservableクラスを継承させるだけです。

const observableModule = require("tns-core-modules/data/observable");

var MyClass = (function (_super) {
	__extends(MyClass, _super);

	function MyClass() {
		_super.apply(this, arguments);
	}

	Object.defineProperty(MyClass.prototype, "myProperty", {
		get: function () {
			return this._myProperty;
		},
		set: function (value) {
			this._myProperty = value;
		},
		enumerable: true,
		configurable: true
	});

	return MyClass;
})(observableModule.Observable);

exports.MyClass = MyClass;
import { Observable } from "tns-core-modules/data/observable";

export class MyClass extends Observable {
	private _myProperty:number;

	get myProperty(): number {
		return this._myProperty;
	}
	set myProperty(value: number) {
		this._myProperty = value;
	}
}
例4のコードは、プロパティ値が変更されたときにpropertyChangeイベントを発生させます。

PropertyChangeDataインタフェース

propertyChangeイベントが発生した後に受け取る引数はPropertyChangeData型です。このインタフェースは、5つの共通のプロパティを提供します。

カスタムイベントの作成

ビジネスロジックで要求されている場合は、特定のアクションに対してカスタムイベントを発生させる(または発行する)ことができます。 そのためにはアクションが完了したときにObservable.notify()メソッドを呼び出します。 このメソッドは、EventDataインタフェースのすべての実装者をイベントデータとして受け取ります。 それには、イベントに関する基本的な情報(eventNameとしての名前、およびobjectとしてのイベント送信者のインスタンス)が含まれます。

let eventData = {
	eventName: "myCustomEventName",
	object: this
};
this.notify(eventData);
let eventData: EventData = {
	eventName: "myCustomEventName",
	object: this
}
this.notify(eventData);

イベントを発生させるのに必要な最小限の情報はeventNameです。これは、このイベントに関連するすべてのイベントハンドラを実行するために使用されます。

次のステップはこのイベントにフックすることです:

let myCustomObject = new MyClass();
myCustomObject.on("myCustomEventName", function(eventData){
	console.log(eventData.eventName + " has been raised! by: " + eventData.object);
})

propertyChangeイベントにも同様のロジックが実装されているため、ビジネスロジックでそれが必要な場合は、 (propertyChangeイベントを発生するObservable.set()メソッドを使用せずに)notify()メソッドを使用してpropertyChangeを手動で発行できます。

メモリリークを回避する

ラジオ局との比較は概念を理解するのに便利ですが、イベントは内部ではもう少し複雑です。 リスナーに通知できるように、送信者にはリスナーへのポインタが含まれています。 リスナーオブジェクトをnullまたはundefinedに設定した場合でも、 送信者は生きていてリスナーオブジェクトへのライブ参照を持っているため、ガベージコレクションには適していません。 送信者とリスナーのオブジェクトの有効期間が大幅に異なる場合、これによりメモリリークが発生する可能性があります。

このシナリオを考えます:UI要素はたくさんの子コントロールを作成します。 それぞれが親のイベントにフックします。 その後、子コントロールが解放され(リストビューのスクロール中など)、メモリリークが発生します。

これらのメモリリークを防ぐために、リスナオブジェクトを解放する前にイベントリスナハンドラを削除することをお勧めします。 残念ながら、しばしばoffまたはremoveEventListener関数を呼び出す正確なタイミングを判断できないことがあります。 そのような場合は、NativeScriptフレームワークの別のオプション(弱いイベント)を使用してください。

弱いイベントでの作業

弱いイベントは、その名前が示すように、リスナーオブジェクトへの弱い参照を作成します。これは、イベントリスナーポインタを削除せずにリスナーオブジェクトを解放するのに役立ちます。

弱いイベントリスナーの追加

弱いイベントリスナーを使用することは、通常のイベントと非常によく似ています。以下のデモは、弱いイベントリスナーを追加する方法を示しています(コードのコメントは明確さのためのものも含まれています)。

var weakEventListenerModule = require("tns-core-modules/ui/core/weak-event-listener");
var Button = require("tns-core-modules/ui/button").Button;
var Observable = require("tns-core-modules/data/observable").Observable;

var testButton = new Button();
testButton.text = "Test";
testButton.on(Button.tapEvent, function () {
	source.set("testProperty", "change" + counter);
});

const source = new Observable();

let counter = 0;
let handlePropertyChange = function () {
	counter++;
	this.text = counter + "";
};

let weakEL = weakEventListenerModule.WeakEventListener;
let weakEventListenerOptions: weakEventListenerModule.WeakEventListenerOptions = {
	// create a weak reference to the event listener object
	targetWeakRef: new WeakRef(this),
	// create a weak reference to the event sender object
	sourceWeakRef: new WeakRef(this.source),
	// set the name of the event
	eventName: observable.Observable.propertyChangeEvent,
	// set the event handler
	handler: handlePropertyChange,
	// (optional) set the context in which to execute the handler
	handlerContext: testButton,
	// (optional) set a specialized property used for extra event recognition
	key: this.options.targetProperty
}
weakEL.addWeakEventListener(this.weakEventListenerOptions);
import * as weakEventListenerModule from "tns-core-modules/ui/core/weak-event-listener";
import { Button } from "tns-core-modules/ui/button";
import { Observable } from "tns-core-modules/data/observable";

const testButton = new Button();
testButton.text = "Test";
testButton.on(Button.tapEvent, function () {
	source.set("testProperty", "change" + counter);
});

const source = new Observable();

let counter = 0;
let handlePropertyChange = function () {
	counter++;
	this.text = counter + "";
};

let weakEL = weakEventListenerModule.WeakEventListener;
let weakEventListenerOptions: weakEventListenerModule.WeakEventListenerOptions = {
	// create a weak reference to the event listener object
	targetWeakRef: new WeakRef(this),
	// create a weak reference to the event sender object
	sourceWeakRef: new WeakRef(this.source),
	// set the name of the event
	eventName: observable.Observable.propertyChangeEvent,
	// set the event handler
	handler: handlePropertyChange,
	// specialized property used for extra event recognition
	key: this.options.targetProperty,
	// (optional) set the context in which to execute the handler
	handlerContext: testButton
}
weakEL.addWeakEventListener(this.weakEventListenerOptions);
例6は、弱いイベントリスナーを監視可能なオブジェクトインスタンスにアタッチする方法を示しています。 handlePropertyChange関数をよく見ると、このオブジェクトのtextプロパティは、 propertyChangeイベントが発生すると(button tapイベントを介して)変更されることがわかります。 この関数はhandlerContextプロパティの使い方を示します。 その値はイベントハンドラ関数内でthisへの引数として扱われます。

弱いイベントリスナの削除

イベントで関数を呼び出すときは、targetWeakRefおよびkeyプロパティはオプションです。 ただし、イベントリスナを削除することはできます。プロパティは、弱いイベントリスナーを格納するキーと値のペアのキーとして使用されます。

weakEL.removeWeakEventListener(this.weakEventListenerOptions);
weakEL.removeWeakEventListener(this.weakEventListenerOptions);
入門

コアコンセプト

ユーザーインターフェース

ツール

ハードウェアアクセス

プラグインの開発

リリース

アプリテンプレート

パフォーマンスの最適化

フレームワークモジュール

ガイド

サポートを受ける

トラブルシューティング

Siedkick