.h
typedef enum
{
kCCMenuStateWaiting, // 表示没有菜单项被选中
kCCMenuStateTrackingTouch // 表示有菜单项被选中
} tCCMenuState;
enum {
//* priority used by the menu for the event handler
kCCMenuHandlerPriority = -128,
};
class CC_DLL CCMenu : public CCLayerRGBA // CCMenu的基类居然是CCLayer!
{
bool m_bEnabled; // CCMenu是否接收触摸事件
public:
CCMenu() : m_pSelectedItem(NULL) {}
virtual ~CCMenu(){}
// 创建一个空菜单
static CCMenu* create();
// 根据多个菜单项列创建菜单
static CCMenu* create(CCMenuItem* item, ...);
// 通过子菜单项的数组创建菜单
static CCMenu* createWithArray(CCArray* pArrayOfItems);
// 通过一个菜单项创建菜单
static CCMenu* createWithItem(CCMenuItem* item);
// 通过多个菜单项列创建菜单
static CCMenu* createWithItems(CCMenuItem *firstItem, va_list args);
// 初始化空菜单
bool init();
// 通过子菜单项数组初始化菜单
bool initWithArray(CCArray* pArrayOfItems);
// 垂直方向默认间隙排列
void alignItemsVertically();
//垂直方向指定间隙排列
void alignItemsVerticallyWithPadding(float padding);
// 水平方向默认间隙排列
void alignItemsHorizontally();
// 水平方向指定间隙排列
void alignItemsHorizontallyWithPadding(float padding);
// 通过每行个数排列
void alignItemsInColumns(unsigned int columns, ...);
void alignItemsInColumns(unsigned int columns, va_list args);
// ?
void alignItemsInColumnsWithArray(CCArray* rows);
// 通过每列个数排列
void alignItemsInRows(unsigned int rows, ...);
void alignItemsInRows(unsigned int rows, va_list args);
// ?
void alignItemsInRowsWithArray(CCArray* columns);
// 设置事件处理优先级,默认为kCCMenuTouchPriority
void setHandlerPriority(int newPriority);
// 基类方法
virtual void addChild(CCNode * child);
virtual void addChild(CCNode * child, int zOrder);
virtual void addChild(CCNode * child, int zOrder, int tag);
virtual void registerWithTouchDispatcher();
virtual void removeChild(CCNode* child, bool cleanup);
// 触摸事件
virtual bool ccTouchBegan(CCTouch* touch, CCEvent* event);
virtual void ccTouchEnded(CCTouch* touch, CCEvent* event);
virtual void ccTouchCancelled(CCTouch *touch, CCEvent* event);
virtual void ccTouchMoved(CCTouch* touch, CCEvent* event);
// 退出调用函数
virtual void onExit();
// ?
virtual void setOpacityModifyRGB(bool bValue) {CC_UNUSED_PARAM(bValue);}
virtual bool isOpacityModifyRGB(void) { return false;}
// 获取/设置菜单是否可用
virtual bool isEnabled() { return m_bEnabled; }
virtual void setEnabled(bool value) { m_bEnabled = value; };
protected:
// 返回被触摸的菜单项
CCMenuItem* itemForTouch(CCTouch * touch);
tCCMenuState m_eState;
CCMenuItem *m_pSelectedItem;
};
.cpp
static std::vector<unsigned int=""> ccarray_to_std_vector(CCArray* pArray) // 将CCArray转换成Vector
{
std::vector<unsigned int=""> ret;
CCObject* pObj;
CCARRAY_FOREACH(pArray, pObj)
{
CCInteger* pInteger = (CCInteger*)pObj;
ret.push_back((unsigned int)pInteger->getValue());
}
return ret;
}
enum
{
kDefaultPadding = 5,
};
//
//CCMenu
//
CCMenu* CCMenu::create()
{ // 传空参数,创建空菜单
return CCMenu::create(NULL, NULL);
}
CCMenu * CCMenu::create(CCMenuItem* item, ...)
{
va_list args;
va_start(args,item); //处理...形参
CCMenu *pRet = CCMenu::createWithItems(item, args);
va_end(args);
return pRet;
}
CCMenu* CCMenu::createWithArray(CCArray* pArrayOfItems)
{
CCMenu *pRet = new CCMenu();
if (pRet && pRet->initWithArray(pArrayOfItems))
{
pRet->autorelease(); // 设置自动释放
}
else
{
CC_SAFE_DELETE(pRet);
}
return pRet;
}
CCMenu* CCMenu::createWithItems(CCMenuItem* item, va_list args)
{
CCArray* pArray = NULL; // 将...形参转换成CCArray
if( item )
{
pArray = CCArray::create(item, NULL); // 创建一个CCArray
CCMenuItem *i = va_arg(args, CCMenuItem*);
while(i)
{
pArray->addObject(i); // 添加其他子菜单项
i = va_arg(args, CCMenuItem*);
}
}
return CCMenu::createWithArray(pArray);
}
CCMenu* CCMenu::createWithItem(CCMenuItem* item)
{
return CCMenu::create(item, NULL);
}
bool CCMenu::init()
{
return initWithArray(NULL);
}
bool CCMenu::initWithArray(CCArray* pArrayOfItems)
{
if (CCLayer::init())
{
// 设置触摸相关信息
setTouchPriority(kCCMenuHandlerPriority);
setTouchMode(kCCTouchesOneByOne);
setTouchEnabled(true);
// 打开可用状态
m_bEnabled = true;
// 菜单置于屏幕中间,并填充满整个屏幕
CCSize s = CCDirector::sharedDirector()->getWinSize();
this->ignoreAnchorPointForPosition(true);
setAnchorPoint(ccp(0.5f, 0.5f));
this->setContentSize(s);
setPosition(ccp(s.width/2, s.height/2));
// 遍历添加菜单项
if (pArrayOfItems != NULL)
{
int z=0;
CCObject* pObj = NULL;
CCARRAY_FOREACH(pArrayOfItems, pObj)
{
CCMenuItem* item = (CCMenuItem*)pObj;
this->addChild(item, z);
z++;
}
}
// 将选中项置为空
m_pSelectedItem = NULL;
// 将状态置为没有菜单项被选中
m_eState = kCCMenuStateWaiting;
// enable cascade color and opacity on menus
setCascadeColorEnabled(true);
setCascadeOpacityEnabled(true);
return true;
}
return false;
}
/*
* override add:
*/
void CCMenu::addChild(CCNode * child)
{
CCLayer::addChild(child);
}
void CCMenu::addChild(CCNode * child, int zOrder)
{
CCLayer::addChild(child, zOrder);
}
void CCMenu::addChild(CCNode * child, int zOrder, int tag)
{ // 确保child为CCMenuItem之内
CCAssert( dynamic_cast<ccmenuitem*>(child) != NULL, Menu only supports MenuItem objects as children);
CCLayer::addChild(child, zOrder, tag);
}
void CCMenu::onExit()
{
if (m_eState == kCCMenuStateTrackingTouch) // 如果有菜单项被选中,则取消选择状态
{
if (m_pSelectedItem)
{ // 将选中项菜单选中状态清除,并将用于记录的变量置为空
m_pSelectedItem->unselected();
m_pSelectedItem = NULL;
}
// 将状态置为等待触摸
m_eState = kCCMenuStateWaiting;
}
CCLayer::onExit();
}
void CCMenu::removeChild(CCNode* child, bool cleanup)
{
CCMenuItem *pMenuItem = dynamic_cast<ccmenuitem*>(child);
CCAssert(pMenuItem != NULL, Menu only supports MenuItem objects as children);
// 如果是被选中的菜单项,则先把选中置为空
if (m_pSelectedItem == pMenuItem)
{
m_pSelectedItem = NULL;
}
CCNode::removeChild(child, cleanup);
}
//Menu - Events
void CCMenu::setHandlerPriority(int newPriority)
{
CCTouchDispatcher* pDispatcher = CCDirector::sharedDirector()->getTouchDispatcher();
pDispatcher->setPriority(newPriority, this); // 设置触摸优先级
}
void CCMenu::registerWithTouchDispatcher()
{ // 注册触摸
CCDirector* pDirector = CCDirector::sharedDirector();
pDirector->getTouchDispatcher()->addTargetedDelegate(this, this->getTouchPriority(), true);
}
bool CCMenu::ccTouchBegan(CCTouch* touch, CCEvent* event)
{
CC_UNUSED_PARAM(event);
if (m_eState != kCCMenuStateWaiting || ! m_bVisible || !m_bEnabled)
{ // 排除不执行触摸事件状态
return false;
}
for (CCNode *c = this->m_pParent; c != NULL; c = c->getParent())
{ // 如果菜单父类不可见则返回
if (c->isVisible() == false)
{
return false;
}
}
<span style="white-space: pre;"> </span>// 选出触摸项
m_pSelectedItem = this->itemForTouch(touch);
if (m_pSelectedItem)
{
m_eState = kCCMenuStateTrackingTouch; // 将状态变为有菜单项被选中
m_pSelectedItem->selected(); // 对触摸项执行触摸事件
return true;
}
return false;
}
void CCMenu::ccTouchEnded(CCTouch *touch, CCEvent* event)
{
CC_UNUSED_PARAM(touch); // 消除不使用警告
CC_UNUSED_PARAM(event);
CCAssert(m_eState == kCCMenuStateTrackingTouch, [Menu ccTouchEnded] -- invalid state);
if (m_pSelectedItem)
{ // 解除菜单项选中状态
m_pSelectedItem->unselected();
m_pSelectedItem->activate();
}
m_eState = kCCMenuStateWaiting;
}
void CCMenu::ccTouchCancelled(CCTouch *touch, CCEvent* event)
{
CC_UNUSED_PARAM(touch);
CC_UNUSED_PARAM(event);
CCAssert(m_eState == kCCMenuStateTrackingTouch, [Menu ccTouchCancelled] -- invalid state);
if (m_pSelectedItem)
{ // 解除菜单项选中状态
m_pSelectedItem->unselected();
}
m_eState = kCCMenuStateWaiting;
}
void CCMenu::ccTouchMoved(CCTouch* touch, CCEvent* event)
{
CC_UNUSED_PARAM(event);
CCAssert(m_eState == kCCMenuStateTrackingTouch, [Menu ccTouchMoved] -- invalid state);
CCMenuItem *currentItem = this->itemForTouch(touch); // 获取当前触摸的子菜单项
if (currentItem != m_pSelectedItem)
{
if (m_pSelectedItem) // 如果当前有选中项,先取消选中
{
m_pSelectedItem->unselected();
}
m_pSelectedItem = currentItem; // 重新设置选中项
if (m_pSelectedItem)
{
m_pSelectedItem->selected();
}
}
}
//Menu - Alignment
// 垂直方向默认间隙排列
void CCMenu::alignItemsVertically()
{
this->alignItemsVerticallyWithPadding(kDefaultPadding);
}
// 垂直方向指定间隙排列
void CCMenu::alignItemsVerticallyWithPadding(float padding)
{
float height = -padding; // 第一个子菜单没有间隔,用于去掉这个重复
if (m_pChildren && m_pChildren->count() > 0)
{
CCObject* pObject = NULL;
CCARRAY_FOREACH(m_pChildren, pObject)
{
CCNode* pChild = dynamic_cast<ccnode*>(pObject);
if (pChild)
{
height += pChild->getContentSize().height * pChild->getScaleY() + padding; // 获取子菜单项和间隔的总高度
}
}
}
float y = height / 2.0f; // 因为定位点在中间,所以先除去2
if (m_pChildren && m_pChildren->count() > 0)
{
CCObject* pObject = NULL;
CCARRAY_FOREACH(m_pChildren, pObject)
{
CCNode* pChild = dynamic_cast<ccnode*>(pObject);
if (pChild)
{
pChild->setPosition(ccp(0, y - pChild->getContentSize().height * pChild->getScaleY() / 2.0f)); // 锚点在中间,所以除2
y -= pChild->getContentSize().height * pChild->getScaleY() + padding; // 减去一个子菜单项和间隔的高度
}
}
}
}
// 水平方向默认间隙排列
void CCMenu::alignItemsHorizontally(void)
{
this->alignItemsHorizontallyWithPadding(kDefaultPadding);
}
// 水平方向指定间隙排列
void CCMenu::alignItemsHorizontallyWithPadding(float padding) // 原理和垂直排列相似,只不过将x变成y
{
float width = -padding;
if (m_pChildren && m_pChildren->count() > 0)
{
CCObject* pObject = NULL;
CCARRAY_FOREACH(m_pChildren, pObject)
{
CCNode* pChild = dynamic_cast<ccnode*>(pObject);
if (pChild)
{
width += pChild->getContentSize().width * pChild->getScaleX() + padding;
}
}
}
float x = -width / 2.0f;
if (m_pChildren && m_pChildren->count() > 0)
{
CCObject* pObject = NULL;
CCARRAY_FOREACH(m_pChildren, pObject)
{
CCNode* pChild = dynamic_cast<ccnode*>(pObject);
if (pChild)
{
pChild->setPosition(ccp(x + pChild->getContentSize().width * pChild->getScaleX() / 2.0f, 0));
x += pChild->getContentSize().width * pChild->getScaleX() + padding;
}
}
}
}
void CCMenu::alignItemsInColumns(unsigned int columns, ...)
{ // 将...转换成va_list
va_list args;
va_start(args, columns);
this->alignItemsInColumns(columns, args);
va_end(args);
}
void CCMenu::alignItemsInColumns(unsigned int columns, va_list args)
{ // 将va_list转换成CCArray
CCArray* rows = CCArray::create();
while (columns)
{
rows->addObject(CCInteger::create(columns));
columns = va_arg(args, unsigned int);
}
alignItemsInColumnsWithArray(rows);
}
void CCMenu::alignItemsInColumnsWithArray(CCArray* rowsArray)
{
vector<unsigned int=""> rows = ccarray_to_std_vector(rowsArray); // 将CCArray转换成vector
int height = -5; // 为-5是用于除去第一行的间隙
unsigned int row = 0; // 索引,当前行序号
unsigned int rowHeight = 0; // 放置每一行高度(即每行最大高度)
unsigned int columnsOccupied = 0; // 已放置列数
unsigned int rowColumns; // 放置该行有多少列
if (m_pChildren && m_pChildren->count() > 0) // 子菜单项数组存在且个数不为0
{
CCObject* pObject = NULL; // 临时变量
CCARRAY_FOREACH(m_pChildren, pObject) // 遍历子菜单项数组
{
CCNode* pChild = dynamic_cast<ccnode*>(pObject); // 将CCObject对象强制转换成CCNode对象
if (pChild) // 如果强制转换成功
{
CCAssert(row < rows.size(), );
rowColumns = rows[row]; // 获取该行列数
CCAssert(rowColumns, );
float tmp = pChild->getContentSize().height; // 获取当前子菜单高度
rowHeight = (unsigned int)((rowHeight >= tmp || isnan(tmp)) ? rowHeight : tmp); // 如果行高大于当前子菜单项高度,或者当前子菜单项高度越界,rowHeight不变,否则rowHeight等于当前子菜单项高度(用来存放最大菜单项高度)
++columnsOccupied; // 已放置列数自加
if (columnsOccupied >= rowColumns) // 如果已放置列数大于等于该行可容纳列数
{
height += rowHeight + 5; // 高度加上当前高度加上间隙
columnsOccupied = 0; // 重置已放置列数
rowHeight = 0; // 重置每行行高的临时变量
++row; // 跳转至下一行
}
}
}
}
// check if too many rows/columns for available menu items
CCAssert(! columnsOccupied, );
CCSize winSize = CCDirector::sharedDirector()->getWinSize(); // 获取屏幕大小
row = 0;
rowHeight = 0;
rowColumns = 0;
float w = 0.0;
float x = 0.0;
float y = (float)(height / 2);
if (m_pChildren && m_pChildren->count() > 0)
{
CCObject* pObject = NULL;
CCARRAY_FOREACH(m_pChildren, pObject)
{
CCNode* pChild = dynamic_cast<ccnode>(pObject);
if (pChild)
{
if (rowColumns == 0) // 如果需要放置数量为0才重新读取数据
{
rowColumns = rows[row]; // 获取需要放置的数量
w = winSize.width / (1 + rowColumns); //占屏幕宽度大小平分
x = w;
}
float tmp = pChild->getContentSize().height;
rowHeight = (unsigned int)((rowHeight >= tmp || isnan(tmp)) ? rowHeight : tmp); // 获取当前行高度最大高度
pChild->setPosition(ccp(x - winSize.width / 2,
y - pChild->getContentSize().height / 2)); // 重新放置位置
x += w;
++columnsOccupied;
if (columnsOccupied >= rowColumns) // 如果已放置列数大于等于需要放置的列数
{
y -= rowHeight + 5; // 减去一个子菜单项和间隔的高度
columnsOccupied = 0;
rowColumns = 0;
rowHeight = 0;
++row;
}
}
}
}
}
//alignItemsInRows系列方法参照alignItemsInColumns
void CCMenu::alignItemsInRows(unsigned int rows, ...)
{
va_list args;
va_start(args, rows);
this->alignItemsInRows(rows, args);
va_end(args);
}
void CCMenu::alignItemsInRows(unsigned int rows, va_list args)
{
CCArray* pArray = CCArray::create();
while (rows)
{
pArray->addObject(CCInteger::create(rows));
rows = va_arg(args, unsigned int);
}
alignItemsInRowsWithArray(pArray);
}
void CCMenu::alignItemsInRowsWithArray(CCArray* columnArray)
{
vector<unsigned int=""> columns = ccarray_to_std_vector(columnArray);
vector<unsigned int=""> columnWidths;
vector<unsigned int=""> columnHeights;
int width = -10;
int columnHeight = -5;
unsigned int column = 0;
unsigned int columnWidth = 0;
unsigned int rowsOccupied = 0;
unsigned int columnRows;
if (m_pChildren && m_pChildren->count() > 0)
{
CCObject* pObject = NULL;
CCARRAY_FOREACH(m_pChildren, pObject)
{
CCNode* pChild = dynamic_cast<ccnode>(pObject);
if (pChild)
{
// check if too many menu items for the amount of rows/columns
CCAssert(column < columns.size(), );
columnRows = columns[column];
// can't have zero rows on a column
CCAssert(columnRows, );
// columnWidth = fmaxf(columnWidth, [item contentSize].width);
float tmp = pChild->getContentSize().width;
columnWidth = (unsigned int)((columnWidth >= tmp || isnan(tmp)) ? columnWidth : tmp);
columnHeight += (int)(pChild->getContentSize().height + 5);
++rowsOccupied;
if (rowsOccupied >= columnRows)
{
columnWidths.push_back(columnWidth);
columnHeights.push_back(columnHeight);
width += columnWidth + 10;
rowsOccupied = 0;
columnWidth = 0;
columnHeight = -5;
++column;
}
}
}
}
// check if too many rows/columns for available menu items.
CCAssert(! rowsOccupied, );
CCSize winSize = CCDirector::sharedDirector()->getWinSize();
column = 0;
columnWidth = 0;
columnRows = 0;
float x = (float)(-width / 2);
float y = 0.0;
if (m_pChildren && m_pChildren->count() > 0)
{
CCObject* pObject = NULL;
CCARRAY_FOREACH(m_pChildren, pObject)
{
CCNode* pChild = dynamic_cast<ccnode>(pObject);
if (pChild)
{
if (columnRows == 0)
{
columnRows = columns[column];
y = (float) columnHeights[column];
}
// columnWidth = fmaxf(columnWidth, [item contentSize].width);
float tmp = pChild->getContentSize().width;
columnWidth = (unsigned int)((columnWidth >= tmp || isnan(tmp)) ? columnWidth : tmp);
pChild->setPosition(ccp(x + columnWidths[column] / 2,
y - winSize.height / 2));
y -= pChild->getContentSize().height + 10;
++rowsOccupied;
if (rowsOccupied >= columnRows)
{
x += columnWidth + 5;
rowsOccupied = 0;
columnRows = 0;
columnWidth = 0;
++column;
}
}
}
}
}
CCMenuItem* CCMenu::itemForTouch(CCTouch *touch){
CCPoint touchLocation = touch->getLocation(); // 获取当前触摸点
if (m_pChildren && m_pChildren->count() > 0) // 判断子节点(子菜单项)是否存在,且个数不为0
{
CCObject* pObject = NULL; // 临时变量
CCARRAY_FOREACH(m_pChildren, pObject) // 遍历子菜单项数组
{
CCMenuItem* pChild = dynamic_cast<ccmenuitem>(pObject); // 强制转换,获取当前子菜单项
if (pChild && pChild->isVisible() && pChild->isEnabled()) // 如果当前子菜单项不为空、且可见、且可用
{
CCPoint local = pChild->convertToNodeSpace(touchLocation); // 把世界坐标转换到当前节点的本地坐标系中
CCRect r = pChild->rect(); // 获取子菜单项范围
r.origin = CCPointZero;
if (r.containsPoint(local)) // 判断子菜单项范围中是否包括触摸点
{
return pChild; // 返回该子菜单项
}
}
}
}
return NULL; // 返回空
}
看懂了。就是知识