NativeScriptアプリ内でListViewコントロールを使用するには、コンポーネントのNativeScript実装が複雑で、カスタムアイテムテンプレート、バインディングなどがあるため、特別な注意が必要です。
NativeScriptモジュールは、ネイティブListViewの使用方法を簡素化するカスタムコンポーネントを提供します。
const listViewModule = require("tns-core-modules/ui/list-view");
import { ItemEventData, ListView } from "tns-core-modules/ui/list-view";
例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}`);
}
上記の例では、項目ソースプロパティ(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();
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" }
]);
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!");
});
});
この例では、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;
}
}
この例は、アイテムテンプレートセレクターを分離コードファイルの関数として指定する方法を示しています
式で表現できない複雑なロジックがアイテムテンプレートセレクターに含まれる場合、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";
}
この例は、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>
Native Component
Android | iOS |
---|---|
android.widget.ListView | UITableView |