Table of Contents
Cocos2d-x を使ったアプリでリストメニューを作成する方法です。使用言語は javascript (Cocos2d-js) です。
ccui.ListView はiOSでいえば UITableView のようなリストメニューを作るための Cocos UI です。
以下のようにスワイプでスクロールするメニューが作れます。
項目が多くて画面スクロールが必要なメニューを作る際に便利です。
Cocos UI を使うための準備
ccui.ListView などの Cocos UI を使用する場合は、project.json の modules に extensions を加える必要があります。
... "modules" : ["cocos2d", "extensions"], ...
not defined などのエラーが出る場合は、この設定が行われているか確認してください。
サンプルソースコード
実際に ccui.ListView を使ったシーンのソースコードです。
100個のリスト項目を作って縦に並べています。
// 色の設定
var color = {
backgroundColor: cc.color(245, 255, 245),
textColor: cc.color(0, 128, 255),
};
var UIListViewTest = cc.LayerColor.extend({
showingListItemCount: 8,
totalListItemCount: 100,
totalListViewHeight: 0,
listItemHeight: 240,
itemMargin: 8,
bufferZone: 240,
reuseItemOffset: 0,
lastContentPosY: 0,
listView: null,
ctor: function() {
this._super(color.backgroundColor);
var label = new cc.LabelTTF("ListViewテスト", "Arial", 60);
label.setPosition(cc.winSize.width / 2, cc.winSize.height - 80);
label.color = color.textColor;
this.addChild(label);
this.itemArray = [];
for (var i = 0; i < this.totalListItemCount; ++i) {
this.itemArray.push("リストアイテム " + i);
}
// ListViewを作成
this.listView = new ccui.ListView();
this.listView.setDirection(ccui.ScrollView.DIR_VERTICAL);
this.listView.setTouchEnabled(true);
this.listView.setBounceEnabled(true);
this.listView.setContentSize(cc.size(cc.winSize.width, cc.winSize.height - 160));
// ListViewの基準点は左下
this.listView.x = 0;
this.listView.y = 0;
// ListViewにタッチイベントを付与
this.listView.addEventListener(this.selectedItemEvent, this);
this.listView.setGravity(ccui.ListView.GRAVITY_CENTER_VERTICAL);
this.addChild(this.listView);
// リスト項目のテンプレートを作る
var listItemModel = new ccui.Layout();
listItemModel.setTouchEnabled(true);
listItemModel.setBackGroundColorType(ccui.Layout.BG_COLOR_SOLID);
listItemModel.setBackGroundColor(cc.color(200, 200, 200));
listItemModel.setContentSize(cc.size(cc.winSize.width, this.listItemHeight));
// リスト項目に載せるボタン
var listButton = new ccui.Button();
listButton.setName("ButtonItem");
listButton.titleFontSize = 64;
listButton.setTouchEnabled(true);
// listButton.loadTextures(res.button, res.button_selected, "");
listButton.x = listItemModel.width / 2;
listButton.y = listItemModel.height / 2;
listItemModel.addChild(listButton);
this.listView.setItemModel(listItemModel);
// はじめに表示する項目はコンストラクタ内で作っておく
for (i = 0; i < this.showingListItemCount; ++i) {
var item = listItemModel.clone();
item.setTag(i);
var button = item.getChildByName('ButtonItem');
button.setTitleText(this.itemArray[i]);
button.setTitleColor(color.textColor);
this.listView.pushBackCustomItem(item);
}
this.listView.setItemsMargin(this.itemMargin);
this.reuseItemOffset = (this.listItemHeight + this.itemMargin) * this.showingListItemCount;
this.scheduleUpdate();
return true;
},
onEnter: function() {
cc.log("onEnter");
// cc.Node の onEnter をこのオブジェクトを this として呼び出す
cc.Node.prototype.onEnter.call(this);
// onEnter で forceDoLayout を呼び出す必要あり
this.listView.forceDoLayout();
// ListViewの大きさ設定
this.totalListViewHeight = this.listItemHeight * this.totalListItemCount + (this.totalListItemCount - 1) * this.itemMargin;
this.listView.getInnerContainer().setContentSize(cc.size(this.listView.getInnerContainerSize().width, this.totalListViewHeight));
this.listView.jumpToTop();
},
getItemPositionYInView: function(item) {
var worldPos = item.getParent().convertToWorldSpaceAR(item.getPosition());
var viewPos = this.listView.convertToNodeSpaceAR(worldPos);
return viewPos.y;
},
updateItem: function(itemID, templateID) {
var itemTemplate = this.listView.getItems()[templateID];
var button = itemTemplate.getChildByName('ButtonItem');
itemTemplate.setTag(itemID);
button.setTitleText(this.itemArray[itemID]);
},
update: function(dt) {
var listViewHeight = this.listView.getContentSize().height;
var items = this.listView.getItems();
var isScrollUp = this.listView.getInnerContainer().getPosition().y < this.lastContentPosY;
// リスト項目の更新
for (var i = 0; i < this.showingListItemCount && i < this.totalListItemCount; ++i) {
var item = items[i];
var itemPosY = this.getItemPositionYInView(item);
if (isScrollUp) {
// 画面が上にスクロールしているとき(リスト項目が下にスクロールしているとき)
if (itemPosY < -this.bufferZone && item.getPosition().y + this.reuseItemOffset < this.totalListViewHeight) {
item.setPositionY(item.getPositionY() + this.reuseItemOffset);
var itemID = item.getTag() - items.length;
this.updateItem(itemID, i);
}
} else {
if (itemPosY > this.bufferZone + listViewHeight && item.getPositionY() - this.reuseItemOffset >= 0) {
item.setPositionY(item.getPositionY() - this.reuseItemOffset);
itemID = item.getTag() + items.length;
this.updateItem(itemID, i);
}
}
}
this.lastContentPosY = this.listView.getInnerContainer().getPosition().y;
},
selectedItemEvent: function(sender, type) {
// リスト項目をタップした時の処理
switch (type) {
case ccui.ListView.EVENT_SELECTED_ITEM:
// リスト項目をタップした瞬間
// スクロールさせたい時も反応してしまうので、イベントは下の ON_SELECTED_ITEM_END につけるべき
break;
case ccui.ListView.ON_SELECTED_ITEM_END:
var targetListView = sender;
var item = targetListView.getItem(targetListView.getCurSelectedIndex());
cc.log("Item: " + item.getTag());
break;
default:
break;
}
}
});
var ListViewTestScene = cc.Scene.extend({
onEnter: function() {
this._super();
this.addChild(new UIListViewTest());
}
});
補足説明
リスト項目の表示数は余裕を持たせる
表示するリストの項目数(サンプルコード内では変数 showingListItemCount で設定)は、実際に表示できる項目数よりも多めにしておいた方が、スクロールの際、項目が作成された瞬間をユーザーに見られる心配がありません。
これをギリギリにしていると、速くスクロールした時にリスト項目が消えたり現れたりするところが見えてしまいます。
同じ理由で、リスト項目がどれだけ画面の表示領域に近づいたらリスト項目を作成するかを設定している変数 bufferZone も余裕を持った大きめの値にしておくといいでしょう。
リスト項目はスクロールに合わせて再構成される
画面から出たリスト項目はリセットされるため、再度画面に表示される際に中身を再度設定する必要があります。このサンプルでは関数 updateItem でリスト項目内のボタンのテキストや位置の再調整を行なっています。
このスライド時のリスト項目の更新(削除・作成)は、毎フレームごとに関数 update が状態を監視して必要なときに行います。
リスト項目に画像を使用する
リスト項目に画像を使いたい場合は、コメントアウトしてある ccui.Button のメソッド loadTextures を設定してください。このメソッドの引数は以下のようになっています。
loadTextures("通常時の画像のパス", "タップ時の画像のパス", "使用不能時の画像のパス");
ListViewのタッチイベント
ユーザーがリストをスライドさせる際、リスト項目(ボタン)の上に指を置いてスワイプすることが多いとおもいます。
スワイプしようとしたのにボタンが反応してしまってはいけないので、スライドせずに指を離した場合だけボタンが反応するようにする必要があります。
したがって、イベントタイプは ccui.ListView.EVENT_SELECTED_ITEM ではなく ccui.ListView.ON_SELECTED_ITEM_END を受け取るようにしたほうがよいかと思います。
(Cocosのサンプルコードでは ccui.ListView.EVENT_SELECTED_ITEM でイベント処理を行っています)
以上が ccui.ListView の基本的な使い方です。

