ユーザーインターフェイス / コンポーネント / リストビュー

リストビュー

NativeScriptアプリ内でListViewコントロールを使用するには、コンポーネントのNativeScript実装が複雑で、カスタムアイテムテンプレート、バインディングなどがあるため、特別な注意が必要です。

NativeScriptモジュールは、ネイティブListViewの使用方法を簡素化するカスタムコンポーネントを提供します。

const listViewModule = require("tns-core-modules/ui/list-view");
import { ItemEventData, ListView } from "tns-core-modules/ui/list-view";
注釈: ScrollView内でListViewコンポーネントを、またはListViewのアイテム内でScrollViewを使用すると、ユーザーインターフェイスのパフォーマンスが低下し、ユーザーエクスペリエンスが悪化する可能性があります。 これらの問題を回避するために、ListViewがScrollViewにネストされている場合、およびコンポーネントがListView内で使用されている場合、シナリオでListViewの高さを明示的に指定する必要があります。

例1(ScrollView内のListView):

<ScrollView>
	<StackLayout>
		<ListView height="150" items="">
			<ListView.itemTemplate>
				<!-- ....... -->
			</ListView.itemTemplate>
		</ListView>
	</StackLayout>
</ScrollView>

例2(ListView内のScrollView):

<ScrollView>
<ListView items="">
	<ListView.itemTemplate>
		<StackLayout class="list-group-item">
			<ScrollView height="150" >
				<!-- ....... -->
			</ScrollView>
		</StackLayout>
	</ListView.itemTemplate>
</ListView>

基本

この例は、XMLページで"ListView"コンポーネントを設定する方法、 およびそのアイテムのプロパティをビューモデルのコレクションにバインドする方法を示しています。 サンプルでは、ListViewのitemTemplateを使用しながらカスタムUIを定義する方法が示されています。

<ListView items="{{ myTitles }}"
	itemTap="onItemTap"
	loaded="{{ onListViewLoaded }}"
	separatorColor="orangered" rowHeight="50"
	class="list-group" id="listView" row="2">
	<ListView.itemTemplate>
		<!-- The item template can only have a single root view container (e.g. GriLayout, StackLayout, etc.) -->
		<StackLayout class="list-group-item">
			<Label text="{{ title || 'Downloading...' }}" textWrap="true" class="title" />
		</StackLayout>
	</ListView.itemTemplate>
</ListView>
const fromObject = require("tns-core-modules/data/observable").fromObject;

function onNavigatingTo(args) {
	const page = args.object;
	const vm = fromObject({
		// Setting the listview binding source
		myTitles: [
			{ title: "The Da Vinci Code" },
			{ title: "Harry Potter and the Chamber of Secrets" },
			{ title: "The Alchemist" },
			{ title: "The Godfather" },
			{ title: "Goodnight Moon" },
			{ title: "The Hobbit" }
		]
	});
	page.bindingContext = vm;
}
exports.onNavigatingTo = onNavigatingTo;

function onListViewLoaded(args) {
	const listView = args.object;
}
exports.onListViewLoaded = onListViewLoaded;

function onItemTap(args) {
	const index = args.index;
	console.log(`Second ListView item tap ${index}`);
}
exports.onItemTap = onItemTap;
import { EventData, fromObject } from "tns-core-modules/data/observable";
import { ListView, ItemEventData } from "tns-core-modules/ui/list-view";
import { Page } from "tns-core-modules/ui/page";

export function onNavigatingTo(args: EventData) {
	const page = <Page>args.object;
	const vm = fromObject({
		// Setting the listview binding source
		myTitles: [
			{ title: "The Da Vinci Code" },
			{ title: "Harry Potter and the Chamber of Secrets" },
			{ title: "The Alchemist" },
			{ title: "The Godfather" },
			{ title: "Goodnight Moon" },
			{ title: "The Hobbit" }
		]
	});
	page.bindingContext = vm;
}

export function onListViewLoaded(args: EventData) {
	const listView = <ListView>args.object;
}

export function onItemTap(args: ItemEventData) {
	const index = args.index;
	console.log(`Second ListView item tap ${index}`);
}
注釈: ListViewのアイテムテンプレートには、1つのルートビューコンテナのみを含めることができます。

上記の例では、項目ソースプロパティ(myTitles)は配列であり、そのメンバーは監視可能なオブジェクトではありません。 つまり、配列メンバーを追加または削除しても、プロパティの変更はトリガーされません。 UIを更新するには、更新メソッドの使用中にアイテムが追加または削除された後、リストビューを明示的に更新する必要があります。

const listView = page.getViewById("listView");
page.bindingContext.myTitles.push({ title: "Game of Thrones" });
// Manually trigger the update so that the new color is shown.
listView.refresh();
const listView = page.getViewById("listView");
page.bindingContext.myTitles.push({ title: "Game of Thrones" });
// Manually trigger the update so that the new color is shown.
listView.refresh();
ヒント: ListViewのrefreshメソッドを使用して手動でUI更新をトリガーする代わりに、NativeScriptはObservableArrayを提供します。 リストビューのアイテムにObservableArrayを使用すると、そのメンバーが観察可能なオブジェクトになり、配列アイテムを追加/削除するとUIが自動的に更新されます。
const ObservableArray = require("tns-core-modules/data/observable-array").ObservableArray;
// Change the items source from Array to the NativeScript's ObservableArray
const titlesArray = new ObservableArray([
	{ title: "The Da Vinci Code" },
	{ title: "Harry Potter and the Chamber of Secrets" },
	{ title: "The Alchemist" },
	{ title: "The Godfather" },
	{ title: "Goodnight Moon" },
	{ title: "The Hobbit" }
]);

Improve this document

サンプルコード


コードビハインド

ListViewをプログラムで作成し、各アイテムのアイテムとUIを設定します。

const container = page.getViewById("container");

const listView = new listViewModule.ListView();
listView.className = "list-group";
listView.items = listViewArray;
// The itemLoading event is used to create the UI for each item that is shown in the ListView.
listView.on(listViewModule.ListView.itemLoadingEvent, (args) => {
	if (!args.view) {
		// Create label if it is not already created.
		args.view = new Label();
		args.view.className = "list-group-item";
	}
	(args.view).text = listViewArray.getItem(args.index).title;

});
listView.on(listViewModule.ListView.itemTapEvent, (args) => {
	const tappedItemIndex = args.index;
	const tappedItemView = args.view;
	dialogs.alert(`Index: ${tappedItemIndex} View: ${tappedItemView}`)
	.then(() => {
		console.log("Dialog closed!");
	});
});

container.addChild(listView);
const listView = new ListView();
listView.className = "list-group";
listView.items = listViewArray;
// The itemLoading event is used to create the UI for each item that is shown in the ListView.
listView.on(ListView.itemLoadingEvent, (args: ItemEventData) => {
	if (!args.view) {
		// Create label if it is not already created.
		args.view = new Label();
		args.view.className = "list-group-item";
	}
	(<any>args.view).text = listViewArray.getItem(args.index).title;

});
listView.on(ListView.itemTapEvent, (args: ItemEventData) => {
	const tappedItemIndex = args.index;
	const tappedItemView = args.view;
	alert(`Index: ${tappedItemIndex} View: ${tappedItemView}`)
	.then(() => {
		console.log("Dialog closed!");
	});
});

Improve this document

サンプルコード


イベント

この例では、itemTapイベントとloadMoreItemsイベントを設定する方法と、コードビハインドで必要なコールバックメソッドを設定する方法を示しています。

<ListView row="0" items="{{ listArray }}" itemTap="onItemTap" loadMoreItems="onLoadMoreItems" class="list-group">
	<ListView.itemTemplate>
		<StackLayout class="list-group-item">
			<Label text="{{ title || 'Downloading...' }}" textWrap="true" class="title" />
		</StackLayout>
	</ListView.itemTemplate>
</ListView>
// The event will be raise when an item inside the ListView is tapped.
function onItemTap(args) {
	const index = args.index;
	dialogs.alert(`ListView item tap ${index}`).then(() => {
		console.log("Dialog closed!");
	});
}
exports.onItemTap = onItemTap;

// The event will be raised when the ListView is scrolled so that the last item is visible. This even is intended to be used to add additional data in the ListView.
function onLoadMoreItems(args) {
	if (loadMore) {
		console.log("ListView -> LoadMoreItemsEvent");
		setTimeout(() => {
			listArray.push(
				moreListItems.getItem(
					Math.floor(Math.random() * moreListItems.length)
				)
			);
			listArray.push(
				moreListItems.getItem(
					Math.floor(Math.random() * moreListItems.length)
				)
			);
			listArray.push(
				moreListItems.getItem(
					Math.floor(Math.random() * moreListItems.length)
				)
			);
			listArray.push(
				moreListItems.getItem(
					Math.floor(Math.random() * moreListItems.length)
				)
			);
			listArray.push(
				moreListItems.getItem(
					Math.floor(Math.random() * moreListItems.length)
				)
			);
		}, 3000);

		loadMore = false;
	}
}
exports.onLoadMoreItems = onLoadMoreItems;
// The event will be raise when an item inside the ListView is tapped.
export function onItemTap(args: ItemEventData) {
	const index = args.index;
	alert(`ListView item tap ${index}`).then(() => {
		console.log("Dialog closed!");
	});
}
// The event will be raised when the ListView is scrolled so that the last item is visible.
// This even is intended to be used to add additional data in the ListView.
export function onLoadMoreItems(args: ItemEventData) {
	if (loadMore) {
		console.log("ListView -> LoadMoreItemsEvent");
		setTimeout(() => {
			listArray.push(
				moreListItems.getItem(
					Math.floor(Math.random() * moreListItems.length)
				)
			);
			listArray.push(
				moreListItems.getItem(
					Math.floor(Math.random() * moreListItems.length)
				)
			);
			listArray.push(
				moreListItems.getItem(
					Math.floor(Math.random() * moreListItems.length)
				)
			);
			listArray.push(
				moreListItems.getItem(
					Math.floor(Math.random() * moreListItems.length)
				)
			);
			listArray.push(
				moreListItems.getItem(
					Math.floor(Math.random() * moreListItems.length)
				)
			);
		}, 3000);

		loadMore = false;
	}
}

Improve this document

サンプルコード


複数テンプレートセレクター機能

この例は、アイテムテンプレートセレクターを分離コードファイルの関数として指定する方法を示しています

式で表現できない複雑なロジックがアイテムテンプレートセレクターに含まれる場合、ListViewが存在するページのコードビハインドでアイテムテンプレートセレクター関数を作成できます。 この関数は、それぞれのデータ項目、行インデックス、およびListView項目コレクション全体をパラメーターとして受け取ります。提供された情報に基づいて、使用するテンプレートのキーを返す必要があります。

<ListView row="3" items="{{ listArray }}"  class="list-group" itemTemplateSelector="selectItemTemplate">
	<ListView.itemTemplates>
		<template key="even">
			<StackLayout class="list-group-item" style.backgroundColor="white">
				<Label text="{{ 'Name: ' + name }}" class="h2" textWrap="true"/>
				<Label text="{{ 'Age: ' + age }}"/>
			</StackLayout>
		</template>
		<template key="odd">
			<StackLayout class="list-group-item" style.backgroundColor="gray">
				<Label text="{{ 'Name: ' + name }}" class="h2" textWrap="true" />
				<Label text="{{ 'Age: ' + age }}"/>
			</StackLayout>
		</template>
	</ListView.itemTemplates>
</ListView>
function selectItemTemplate(item, index, items) {
	return index % 2 === 0 ? "even" : "odd";
}
export function selectItemTemplate(item, index, items) {
	return index % 2 === 0 ? "even" : "odd";
}
<ListView row="1" items="{{ listArray }}"  class="list-group" itemTemplateSelector="selectItemTemplateAge">
	<ListView.itemTemplates>
		<template key="green">
			<StackLayout class="list-group-item" style.backgroundColor="green">
				<Label text="{{ 'Name: ' + name }}" class="h2" textWrap="true"/>
				<Label text="{{ 'Age: ' + age }}"/>
			</StackLayout>
		</template>
		<template key="red">
			<StackLayout class="list-group-item" style.backgroundColor="red">
				<Label text="{{ 'Name: ' + name }}" class="h2" textWrap="true" />
				<Label text="{{ 'Age: ' + age }}"/>
			</StackLayout>
		</template>
	</ListView.itemTemplates>
</ListView>
function selectItemTemplateAge(item, index, items) {
	return item.age > 18 ? "green" : "red";
}
export function selectItemTemplateAge(item, index, items) {
	return item.age > 18 ? "green" : "red";
}

Improve this document

サンプルコード


複数のテンプレート

この例は、XMLで複数のアイテムテンプレートとアイテムテンプレートセレクタを定義する方法を示しています

itemTemplateSelectorは、XMLで直接指定された式にすることができます。式のコンテキストは、各行のデータ項目です。 行インデックスを表すアイテムテンプレートセレクター式で特別な値$indexを使用できます。

<ListView row="1" items="{{ listArray }}"  class="list-group" itemTemplateSelector="$index % 2 === 0 ? 'even' : 'odd'">
	<ListView.itemTemplates>
		<template key="even">
			<StackLayout class="list-group-item" style.backgroundColor="white">
				<Label text="{{ 'Name: ' + name }}" class="h2" textWrap="true"/>
				<Label text="{{ 'Age: ' + age }}"/>
			</StackLayout>
		</template>
		<template key="odd">
			<StackLayout class="list-group-item" style.backgroundColor="gray">
				<Label text="{{ 'Name: ' + name }}" class="h2" textWrap="true" />
				<Label text="{{ 'Age: ' + age }}"/>
			</StackLayout>
		</template>
	</ListView.itemTemplates>
</ListView>

Improve this document

サンプルコード


Native Component

Android iOS
android.widget.ListView UITableView
入門

コアコンセプト

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

ツール

ハードウェアアクセス

プラグインの開発

リリース

アプリテンプレート

パフォーマンスの最適化

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

ガイド

サポートを受ける

トラブルシューティング

Siedkick