Cocos2d-x 3.9教程:9. Cocos2d-x中基于布局的容器控件

Cocos2d-x 3.9教程

9. Cocos2d-x中基于布局的容器控件

1.1. ScrollView滚动视图

滚动视图是一种常见的容器型控件,它里面可以放置其他组件。Cocos2d-x中的ScrollView本身也是一种Layout,所以也可以在其中使用线形或者相对布局。ScrollView的显示效果,主要是取决于:

setContentSize(),设置显示出来的区域大小“视口”。

setInnerContainerSize(),设置内部容器的大小,决定了拖动的区域大小;它必须大于或等于setContentSize()。

以及添加进内部的组件和布局位置。

ScrollView,内部的容器为innerLayout,我们的控件放在这个layout上。任一时刻拖动ScrollView,会在“视口”显示innerLayout的一部分:效果示意图如下:


拖动视口,改变的是内部容器innerLayout的位置偏移。则通过视口看到的内容得以改变:


注意,因此内部容器innerLayout上我们加入的控件,需要我们自己来摆放到正确的位置上。

#include "ui/UIScrollView.h"

ui::ScrollView *scrollView = ui::ScrollView::create();

 

scrollView->setBackGroundColorType(ui::Layout::BackGroundColorType::SOLID);

scrollView->setBackGroundColor(Color3B::RED);

scrollView->setPosition(Vec2(50, 100));

scrollView->setContentSize(Size(400, 300));//外部显示的大小

scrollView->setBounceEnabled(true);//是否开启拖动到头后的反弹效果

scrollView->setInnerContainerSize(Size(500, 500));//内部的尺寸,决定了拖动的区域大小;它必须大于或等于setContentSize

//scrollView->setTouchEnabled(true);//是否允许拖动空白处滚动视图,默认为是;否的话,只能拖动里面的组件来滚动视图

scrollView->setDirection(ui::ScrollView::Direction::VERTICAL);//允许拖动的方向

scrollView->setLayoutType(Layout::Type::VERTICAL);//垂直布局;ScrollView本身就是一种Layout

 

//绑定响应事件

scrollView->addEventListener(CC_CALLBACK_2(HelloWorld::onScrollViewEvent, this));

 

Button * button1 = Button::create("button.png");

scrollView->addChild(button1);

Button * button2 = Button::create("potentiometerButton.png");

scrollView->addChild(button2);

Button * button3 = Button::create("potentiometerProgress.png");

scrollView->addChild(button3);

Button * button4 = Button::create("potentiometerTrack.png");

scrollView->addChild(button4);

Button * button5 = Button::create("button_normal.png");

scrollView->addChild(button5);

Button * button6 = Button::create("CloseNormal.png");

scrollView->addChild(button6);

 

this->addChild(scrollView);

void HelloWorld::onScrollViewEvent(Ref* pSender, cocos2d::ui::ScrollView::EventType event)

{

ui::ScrollView * scrollView = (ui::ScrollView *)pSender;

 

switch (event)

{

case cocos2d::ui::ScrollView::EventType::SCROLL_TO_TOP:

CCLOG("scrollView is scroll to top");

break;

case cocos2d::ui::ScrollView::EventType::SCROLLING:

//CCLOG("scrollView is scrolling");

break;

case cocos2d::ui::ScrollView::EventType::SCROLL_TO_BOTTOM:

CCLOG("scrollView is scroll to bottom");

break;

case cocos2d::ui::ScrollView::EventType::BOUNCE_TOP:

CCLOG("scrollView is bounce to top");

break;

case cocos2d::ui::ScrollView::EventType::BOUNCE_BOTTOM:

CCLOG("scrollView is bounce to bottom");

break;

}

}

注意,由于cocos2d::extension库中也有ScrollView,为了避免编译时的冲突。建议类的名字写全路径,如:cocos2d::ui::ScrollView在使用Event时,同样也需要使用全路径:cocos2d::ui::ScrollView::EventType

最终效果如下:

初始位置:

拖动到最下面的位置:

Cocos2d-x 3.9教程:9. Cocos2d-x中基于布局的容器控件_第1张图片

由于我们设定了允许回弹,所以拖动到某一端时,还可以继续拖动一段距离,松开即可回弹:

 

1.2. TableView桌面视图

注意,目前至最新版cocos2d-x3.9为止,TableView是属于extension库的,它继承自extension库的ScrollView,而不是UI库中的ScroolView。

TableView是ScrollView的子类,是一种特殊的ScrollView。

它跟ScrollView类似,可以横向、纵向添加子节点。但TableView是为了解决容器内部出现大量类型相同、但内容数值不同的子节点时,一次性创建过多的子节点而占用大量资源的问题。TableView只会在需要时创建内部单元格(子节点TableViewCell),隐藏在可见区域之外的cell会被重复利用。

比如我们在传统的ScrollView中创建4个子节点,大体效果如下:


其中节点1完全不可见,被隐藏了起来。

我们在TableView中,可以重复利用这些“被隐藏起来的、类型相同的单元格”。在节点1被完全隐藏、不可见时(位于视图可见区域以外),而同时右边的节点4需要被显示出来。我们可以在这一霎那动态的创建节点4,但这样做我们并没有最佳的利用资源,因为随着后续的节点越来越多,TableView内部的单元格cell会越来越多。

但经过观察,节点1是已经完全显示不出来的,因此我们可以拿节点1这个对象出来,放到节点4应当出现的位置,然后把其上的信息修改为节点4应当显示的信息。如图所示:

Cocos2d-x 3.9教程:9. Cocos2d-x中基于布局的容器控件_第2张图片

这样一来,上述示例情况下,我们永远只需要创建3个单元格即可达到创建N个节点的效果。这3个单元格一直被重复利用。但须注意的是,比如在TableView视图上滑动时,任一单元格节点上的值,应当予以更新。

使用起来需要以下几个步骤:

1.在场景类的头文件中:

#include "extensions/GUI/CCScrollView/CCTableView.h"

USING_NS_CC_EXT;

//实现TableViewDelegate接口,以响应触摸事件

//实现TableViewDataSource接口,以在创建内部Cell时提供必要数据

class HelloWorld : public cocos2d::Layer, public TableViewDelegate, public TableViewDataSource

{

......

//for TableViewDelegate

virtual void tableCellTouched(TableView* table, TableViewCell* cell);

 

//for TableViewDataSource

virtual TableViewCell* tableCellAtIndex(TableView *table, ssize_t idx);

virtual ssize_t numberOfCellsInTableView(TableView *table);

virtual Size tableCellSizeForIndex(TableView *table, ssize_t idx);

}

TableViewDelegate提供了触摸事件等接口。在事件发生时回调这些方法。

TableViewDataSource提供了TableView的数据源,在创建TableView及内部单元时,回调相应的方法,我们在这些方法中定制视图和具体的内容。

2.在相应的cpp文件中创建TableView并实现这些接口

//init()方法中

//当前类作为数据源,视图大小为500,400

TableView * tbView = TableView::create(this, Size(500, 400));

tbView->setDelegate(this);//当前类作为事件代理

tbView->setDirection(ScrollView::Direction::HORIZONTAL);//TableView中单元格的方向

tbView->setPosition(Vec2(100, 50));

tbView->setVerticalFillOrder(TableView::VerticalFillOrder::TOP_DOWN);//设定垂直填充方向

this->addChild(tbView);

 

tbView->reloadData();//从dataSource数据源加载数据,视图将被刷新

//实现各种必要接口

//界面上点击任何一个cell时,会调用此函数

void HelloWorld::tableCellTouched(TableView* table, TableViewCell* cell)

{

CCLOG("table cell touched at index:%d", cell->getIdx());

}

 

TableViewCell* HelloWorld::tableCellAtIndex(TableView *table, ssize_t idx)

{

//出队一个空闲的(没有被显示出来时,认为是空闲的)cell,重复利用它

TableViewCell * tbCell = table->dequeueCell();

 

string strIndex = StringUtils::format("cell%d", idx);

 

//如果没有空闲的cell,则创建一个

if (tbCell == NULL)

{

tbCell = TableViewCell::create();

 

Sprite * sp = Sprite::create("potentiometerButton.png");

sp->setAnchorPoint(Vec2::ZERO);

sp->setPosition(Vec2::ZERO);

tbCell->addChild(sp);

 

Label *label = Label::createWithTTF(strIndex.c_str(), "fonts/arial.ttf", 24, Size::ZERO, TextHAlignment::CENTER);

label->setPosition(Vec2(40, 40));

label->setTag(1);

tbCell->addChild(label);

}

else//原来那个cell对象拿来复用,但其上的信息应当按照实际index进行更新

{

Label * label = (Label*)tbCell->getChildByTag(1);

label->setString(strIndex.c_str());

}

 

return tbCell;

}

 

//创建每个cell时,会自动调用此函数,获得某个index下该单元的尺寸

Size HelloWorld::tableCellSizeForIndex(TableView *table, ssize_t idx)

{

if (idx == 2)

return Size(120, 120);

 

return Size(80, 80);

}

 

//创建TableView时,会自动调用此函数,获得应当需要创建多少个cell单元

ssize_t HelloWorld::numberOfCellsInTableView(TableView *table)

{

return 10;//它确定了TableView一共有多少个cell单元

}

最终效果如下:

 Cocos2d-x 3.9教程:9. Cocos2d-x中基于布局的容器控件_第3张图片

由于我们在tableCellSizeForIndex中进行了判断,若要创建的cell单元格的index为2,则返回Size(120, 120)。因此下标为2的单元格占的大小比其他单元格都要大。

若我们把init()方法中创建TableView时的排列方向进行修改,如:

tbView->setDirection(ScrollView::Direction::VERTICAL);//TableView中单元格的方向

则最终效果如下:

 Cocos2d-x 3.9教程:9. Cocos2d-x中基于布局的容器控件_第4张图片

1.3. PageView页面视图

容器型控件,一次显示一页,每个页面之间左右拖拽实现切换。

静止效果,第1页和第3页不可见:

 Cocos2d-x 3.9教程:9. Cocos2d-x中基于布局的容器控件_第5张图片

拖拽中的效果,中间蓝框部分可见,其余不可见:

 

注意,上图中蓝色框为可见部分大小(也就是控件的content size)与页面的大小没有关系。而每一页的大小应当保持一致。

创建PageView控件,然后再创建每一页,每一页必须是一个“Layout”类型(或子类型)的,然后PageView再把每一页添加进去。PageView的可显示部分大小,通过setContentSize()来设定。每个页面Layout的大小,也通过其setContentSize()方法来设定。PageView和页面Layout之间的大小关系,应当提前予以设计。

#include "ui/UIPageView.h"

#include "ui/UILayout.h"

PageView * pageView = PageView::create();

pageView->setContentSize(Size(300, 400));

pageView->setPosition(Vec2(50, 100));

 

for (int i = 0; i < 3; i++)

{

Layout *layout = Layout::create();

layout->setLayoutType(Layout::Type::ABSOLUTE);

layout->setContentSize(Size(300, 400));

layout->setBackGroundColorType(Layout::BackGroundColorType::SOLID);

 

switch (i)

{

case 0:

layout->setBackGroundColor(Color3B::BLUE);

break;

case 1:

layout->setBackGroundColor(Color3B::YELLOW);

break;

case 2:

layout->setBackGroundColor(Color3B::GREEN);

break;

}

 

string str = StringUtils::format("第%2d页", i + 1);

Label *label = Label::createWithTTF(G2U(str.c_str()), "fonts/abc.ttf", 24);

label->setPosition(Vec2(150, 380));

layout->addChild(label);

Button * button1 = Button::create("button.png");

button1->setPosition(Vec2(150, 300));

layout->addChild(button1);

Button * button2 = Button::create("potentiometerButton.png");

button2->setPosition(Vec2(150, 240));

layout->addChild(button2);

Button * button3 = Button::create("potentiometerProgress.png");

button3->setPosition(Vec2(150, 160));

layout->addChild(button3);

 

pageView->addPage(layout);

}

 

//绑定page view的事件响应

pageView->addEventListener(CC_CALLBACK_2(HelloWorld::onPageViewEvent, this));

 

this->addChild(pageView);

void HelloWorld::onPageViewEvent(Ref* pSender, cocos2d::ui::PageView::EventType event)

{

PageView * pageView = (PageView*)pSender;

 

//根据手册,page view目前只有一种事件!

switch (event)

{

case PageView::EventType::TURNING:

CCLOG("pageView is turrning");

break;

}

}

最终效果如下:

静态状态:

Cocos2d-x 3.9教程:9. Cocos2d-x中基于布局的容器控件_第6张图片   Cocos2d-x 3.9教程:9. Cocos2d-x中基于布局的容器控件_第7张图片

切换中的状态:

      Cocos2d-x 3.9教程:9. Cocos2d-x中基于布局的容器控件_第8张图片

1.4. ListView列表视图

ListView列表视图控件,是一种特殊的ScrollView(ListView类继承自ScrollView)。它提供了纵向或者横向的添加条目,按照列表的方式来管理内部的item。可以方便的获取当前选中的条目,并动态的添加、删除指定下标的条目。

#include "ui/UIListView.h"

#include "ui/UILayout.h"

//创建ListView控件

ListView * listView = ListView::create();

listView->setDirection(ListView::Direction::VERTICAL);//垂直排列item(纵向)

listView->setBackGroundColorType(Layout::BackGroundColorType::SOLID);

listView->setBackGroundColor(Color3B::RED);

listView->setPosition(Vec2(50, 100));

listView->setTouchEnabled(true);

 

//创建一个“模型”,ListView可以用它克隆出来多个item!

Layout * default_item = Layout::create();

Button * button = Button::create("button.png");

default_item->setContentSize(button->getContentSize());

button->setPosition(Vec2(default_item->getContentSize().width / 2, default_item->getContentSize().height / 2));

button->setTitleText("model button");

 

default_item->addChild(button);

listView->setItemModel(default_item);//ListView绑定该模型

 

//我们listView的大小是要根据内容来设定的。所以先设定内容大小,再设定listView大小

listView->setContentSize(Size(default_item->getContentSize().width, 500));

 

//根据设定的“模型”,连续克隆出来3个并插入末尾

listView->pushBackDefaultItem();

listView->pushBackDefaultItem();

listView->pushBackDefaultItem();

 

//插入自定义(非模型)的item

//插入一个按钮

Button * customButton = Button::create("button.png");

customButton->setTitleText("button1");

listView->pushBackCustomItem(customButton);

 

//把一个layout当做一个item插入

Layout *customItem2 = Layout::create();

customItem2->setLayoutType(Layout::Type::ABSOLUTE);

customItem2->setContentSize(Size(300, 400));

customItem2->setBackGroundColorType(Layout::BackGroundColorType::SOLID);

customItem2->setBackGroundColor(Color3B::YELLOW);

string str = StringUtils::format("测试条目");

Label *label = Label::createWithTTF(G2U(str.c_str()), "fonts/abc.ttf", 24);

label->setPosition(Vec2(150, 380));

customItem2->addChild(label);

Button * button1 = Button::create("button.png");

button1->setPosition(Vec2(150, 300));

customItem2->addChild(button1);

Button * button2 = Button::create("potentiometerButton.png");

button2->setPosition(Vec2(150, 240));

customItem2->addChild(button2);

Button * button3 = Button::create("potentiometerProgress.png");

button3->setPosition(Vec2(150, 160));

customItem2->addChild(button3);

 

listView->pushBackCustomItem(customItem2);

 

listView->addEventListener((ListView::ccListViewCallback)CC_CALLBACK_2(HelloWorld::onPageViewEvent, this));

 

this->addChild(listView);

void HelloWorld::onListViewEvent(Ref* pSender, cocos2d::ui::ListView::EventType event)

{

switch (event)

{

case ListView::EventType::ON_SELECTED_ITEM_START:

CCLOG("on selected item start");

break;

case ListView::EventType::ON_SELECTED_ITEM_END:

CCLOG("on selected item end");

break;

}

}

最终效果如下:

 

1.5. RichText多格式文本

1.5.1. 基本用法

RichText多格式文本控件,也称富文本。它提供了一般文本框所不能提供的一些丰富功能,比如同一个文本框内不同问的颜色不同、字体不同、字号不同,图、文混编等等。

#include "ui/UIRichText.h"

RichText * richText = RichText::create();

richText->setPosition(Vec2(visibleSize.width / 2, visibleSize.height / 2));

richText->setContentSize(Size(300, 400));

 

//ture,忽略自身的size,使用内部纹理的size;若一个字符串很长,richText长度会跟着变长。

//false的话反之。若字符串过长,会让其“自动换行”

richText->ignoreContentAdaptWithSize(false);

//richText->setVerticalSpace(50);//设置行间距

//RichText中的字符串不能强制换行,\n没有效果

RichElementText * re1 = RichElementText::create(1, Color3B::WHITE, 255, G2U("测试字符串1\n"), "fonts/abc.ttf", 24);

RichElementText * re2 = RichElementText::create(2, Color3B::RED, 255, G2U("测试字符串2\n"), "fonts/abc.ttf", 36);

RichElementText * re3 = RichElementText::create(3, Color3B::BLUE, 255/2, G2U("测试字符串3\n"), "fonts/abc.ttf", 24);

RichElementText * re4 = RichElementText::create(4, Color3B::GREEN, 255, G2U("测试字符串4\n"), "fonts/abc.ttf", 24);

RichElementText * re5 = RichElementText::create(5, Color3B::YELLOW, 255, G2U("测试字符串5\n"), "fonts/abc.ttf", 24);

 

richText->pushBackElement(re1);

richText->pushBackElement(re2);

richText->pushBackElement(re3);

richText->pushBackElement(re4);

richText->pushBackElement(re5);

 

RichElementText * re6 = RichElementText::create(5, Color3B::YELLOW, 255, G2U("今天天气不错"), "fonts/abc.ttf", 24);

RichElementImage * re7 = RichElementImage::create(7, Color3B::RED, 255, "smile.png");

 

richText->pushBackElement(re6);

richText->pushBackElement(re7);

 

//目前通过骨骼动画,实现RichText中的某个element的动画效果! //cocostudio::ArmatureDataManager::getInstance()->addArmatureFileInfo("cocosui/100/100.ExportJson");

//cocostudio::Armature *pAr = cocostudio::Armature::create("100");

//pAr->getAnimation()->play("Animation1");

 

//RichElementCustomNode* recustom = RichElementCustomNode::create(1, Color3B::WHITE, 255, pAr);

//richText->pushBackElement(recustom);

 

this->addChild(richText);

最终效果如下:

 Cocos2d-x 3.9教程:9. Cocos2d-x中基于布局的容器控件_第9张图片

其中蓝色字符串透明度为一半。

1.5.2. 手工换行

暂未找到RichText支持换行的方法,在上述效果中,由于我们设置了richText->ignoreContentAdaptWithSize(false);,字符串会根据RichText的contentSize来自动换行(根据宽度),但“\n”这种换行符是无效的。因此,目前想到的思路是,在ListView中添加RichText作为其item。

#include "ui/UIRichText.h"

#include "ui/UIListView.h"

ListView * listView = ListView::create();

listView->setBackGroundColorType(Layout::BackGroundColorType::GRADIENT);//渐变色

listView->setBackGroundColor(Color3B::RED, Color3B::BLUE);

listView->setContentSize(Size(400, 500));

listView->setPosition(Vec2(50, 100));

 

for (int i = 0; i < 3; i++)

{

RichText * richText = RichText::create();

richText->setContentSize(Size(400, 100));

richText->setPosition(Vec2(visibleSize.width / 2, visibleSize.height / 2));

 

//ture,忽略自身的size,使用内部纹理的size;若一个字符串很长,richText长度会跟着变长。

//false的话反之。若字符串过长,会让其“自动换行”

richText->ignoreContentAdaptWithSize(false);

 

string player = StringUtils::format("玩家%d:", i + 1);

RichElementText * rePlayer = RichElementText::create(0, Color3B::YELLOW, 255, G2U(player.c_str()),

"fonts/abc.ttf", 24);

RichElementText * re1 = RichElementText::create(1, Color3B::WHITE, 255, G2U("今天天气不错,"),

 

"fonts/abc.ttf", 24);

RichElementImage * re2 = RichElementImage::create(2, Color3B::RED, 255, "smile.png");

RichElementText * re3 = RichElementText::create(3, Color3B::BLUE, 255, G2U("心情也好起来了!"),

 

"fonts/abc.ttf", 24);

 

Sprite * sp = Sprite::create("pentacle.png");

//动画不起作用,因为RichElementCustomNode本身不是Node子类,无法作为sprite的parent

//sp->runAction(RepeatForever::create(RotateBy::create(1, 360)));

//只通过骨骼动画,实现RichText中的某个element的动画效果!

//cocostudio::ArmatureDataManager::getInstance()->addArmatureFileInfo("cocosui/100/100.ExportJson");

//cocostudio::Armature *pAr = cocostudio::Armature::create("100");

//pAr->getAnimation()->play("Animation1");

 

//RichElementCustomNode* recustom = RichElementCustomNode::create(1, Color3B::WHITE, 255, pAr);

//richText->pushBackElement(recustom);

RichElementCustomNode * re4 = RichElementCustomNode::create(4, Color3B::BLACK, 255, sp);

 

richText->pushBackElement(rePlayer);

richText->pushBackElement(re1);

richText->pushBackElement(re2);

richText->pushBackElement(re3);

richText->pushBackElement(re4);

 

listView->pushBackCustomItem(richText);

}

 

this->addChild(listView);

最终效果如下:

 


你可能感兴趣的:(tableview,scrollview,cocos2d-x,3.9,pageView)