Cocos2d-x 3.x基础学习: 按钮控件CCControlButton

2015年03月23日 10:54 0 点赞 0 评论 更新于 2025-11-21 18:13

一、CCControl 控件类概述

按钮类 CCControlButton 继承于控件类 CCControl。控件类 CCControl 的主要作用是向子类提供一系列的控件触发事件。当子控件触发相关事件后,就会执行对应的控件事件回调函数,这与之前讲的 CCMenu 中的菜单按钮回调机制类似。

CCControl 控件类主要有三个子类:

  1. 开关控件 CCControlSwitch
  2. 滑块控件 CCControlSlider
  3. 按钮控件 CCControlButton

本文将重点介绍其子类之一的按钮类 CCControlButton

二、Cocos2d-x 3.x 中的变化

在 Cocos2d-x 3.x 版本中,CCControlButton 相关部分有以下变化:

  1. 命名前缀:去掉了 “CC” 前缀。
  2. 对象类:对象类 CCObject 改为 Ref
  3. 按钮事件回调:按钮事件回调依旧使用 cccontrol_selector,未使用 CC_CALLBACK_2
  4. 按钮状态CCControlState 改为强枚举 Control::State,具体状态如下:
    • NORMAL:正常状态
    • HIGH_LIGHTED:高亮状态(即在内部触摸状态下)
    • DISABLED:禁用状态
    • SELECTED:选中状态
  5. 按钮事件CCControlEvent 改为强枚举 Control::EventType,具体事件如下:
    • TOUCH_DOWN:刚刚开始触摸按钮时
    • DRAG_INSIDE:在内部拖动时(保持触摸状态下)
    • DRAG_OUTSIDE:在外部拖动时(保持触摸状态下)
    • DRAG_ENTER:拖动刚进入内部时(保持触摸状态下)
    • DRAG_EXIT:拖动刚离开内部时(保持触摸状态下)
    • TOUCH_UP_INSIDE:在内部抬起手指(保持触摸状态下)
    • TOUCH_UP_OUTSIDE:在外部抬起手指(保持触摸状态下)
    • TOUCH_CANCEL:取消触点
  6. 其他方面:其他变化不大。

三、CCControlButton 详细介绍

(一)基础认知

按钮控件 CCControlButton 在游戏开发中极为常见,几乎每个游戏都会用到。它不仅有多种不同的按钮状态,还会触发一些按钮事件。此外,CCControlButton 使用的背景图片是点九图 CCScale9Sprite,这种图片格式能使按钮在变大时尽量保持棱角不失真。

(二)按钮状态和事件

  1. 按钮状态 CCControlState
    • CCControlStateNormal:正常状态
    • CCControlStateHighlighted:高亮状态(即在内部触摸状态下)
    • CCControlStateDisabled:禁用状态
    • CCControlStateSelected:选中状态
  2. 按钮事件 CCControlEvent
    • CCControlEventTouchDown:刚刚开始触摸按钮时
    • CCControlEventTouchDragInside:在内部拖动时(保持触摸状态下)
    • CCControlEventTouchDragOutside:在外部拖动时(保持触摸状态下)
    • CCControlEventTouchDragEnter:拖动刚进入内部时(保持触摸状态下)
    • CCControlEventTouchDragExit:拖动刚离开内部时(保持触摸状态下)
    • CCControlEventTouchUpInside:在内部抬起手指(保持触摸状态下)
    • CCControlEventTouchUpOutside:在外部抬起手指(保持触摸状态下)
    • CCControlEventTouchCancel:取消触点

(三)绑定按钮事件的方法

// 绑定控件事件
addTargetWithActionForControlEvents(this, cccontrol_selector(HelloWorld::touchDownAction), CCControlEventTouchDown);
void addTargetWithActionForControlEvents(CCObject* target, SEL_CCControlHandler action, CCControlEvent controlEvents);

// 删除控件事件
// removeTargetWithActionForControlEvents(this, cccontrol_selector(HelloWorld::touchDownAction), CCControlEventTouchDown);
void removeTargetWithActionForControlEvents(CCObject* target, SEL_CCControlHandler action, CCControlEvent controlEvents);

(四)需要引用的头文件及命名空间

#include "cocos-ext.h" // 包含cocos-ext.h头文件
using namespace cocos2d::extension; // 引用cocos2d::extension命名空间

(五)常用操作

class CCControlButton : public CCControl
{
/**
* 创建CCControlButton的三种方法
*/
// 使用点九图CCScale9Sprite,按钮中不带标签CCLabel
static CCControlButton* create(CCScale9Sprite* sprite);

// 使用标签label,可以是CCLabelTTF、CCLabelBMFont
static CCControlButton* create(CCNode* label, CCScale9Sprite* backgroundSprite);

// title:标签内容
// fontName:字体资源,如 "Arial"
// fontSize:字体大小,默认 12
// 内部创建的标签为CCLabelTTF
static CCControlButton* create(std::string title, const char* fontName, float fontSize);

/**
* 属性设置1(在当前CCControlState下)
* getCurrentTitle , getCurrentTitleColor ,
* TitleLabel , BackgroundSprite , PreferredSize
*/
// 当前显示的标签内容,只读getCurrentTitle
CC_SYNTHESIZE_READONLY(CCString*, m_currentTitle, CurrentTitle);

// 当前显示的标签内容颜色,只读getCurrentTitleColor
CC_SYNTHESIZE_READONLY_PASS_BY_REF(ccColor3B, m_currentTitleColor, CurrentTitleColor);

// 设置当前CCControlState下的标签,set/get
CC_SYNTHESIZE_RETAIN(CCNode*, m_titleLabel, TitleLabel);

// 设置当前CCControlState下的背景精灵,set/get
CC_SYNTHESIZE_RETAIN(CCScale9Sprite*, m_backgroundSprite, BackgroundSprite);

// 设置按钮大小,若标签label大小大于按钮,将自动扩展,set/get。
// 不过经过测试:设置了,反而按钮大小被固定了,没有按照label的大小进行自动伸展。
CC_PROPERTY(CCSize, m_preferredSize, PreferredSize);

/**
* 属性设置2(在指定CCControlState下)
* setTitleLabelForState , setTitleForState , setTitleColorForState ,
* setTitleTTFForState , setTitleTTFSizeForState ,
* setTitleBMFontForState ,
* setBackgroundSpriteForState , setBackgroundSpriteFrameForState , getBackgroundSpriteForState
*/
// 设置在指定CCControlState下的 字体标签
// 若未设置标签,默认为CCButtonStateNormal状态的字体标签
// 字体标签可以是CCLabelTTF、CCLabelBMFont
virtual void setTitleLabelForState(CCNode* label, CCControlState state);
virtual CCNode* getTitleLabelForState(CCControlState state);

// 设置在指定CCControlState下的 标签内容
// 若未设置标签,默认返回CCButtonStateNormal状态的标签内容
virtual void setTitleForState(CCString* title, CCControlState state);
virtual CCString* getTitleForState(CCControlState state);

// 设置在指定CCControlState下的 标签颜色
virtual void setTitleColorForState(ccColor3B color, CCControlState state);
virtual const ccColor3B getTitleColorForState(CCControlState state);

// 设置在指定CCControlState下的 标签为CCLabelTTF
// fntFile为字体资源名,如 "Arial"
virtual void setTitleTTFForState(const char* fntFile, CCControlState state);
virtual const char* getTitleTTFForState(CCControlState state);

// 设置在指定CCControlState下的 CCLabelTTF标签的字体大小
virtual void setTitleTTFSizeForState(float size, CCControlState state);
virtual float getTitleTTFSizeForState(CCControlState state);

// 设置在指定CCControlState下的 标签为CCLabelBMFont
// fntFile为字体资源名,如 *.fnt
virtual void setTitleBMFontForState(const char* fntFile, CCControlState state);
virtual const char * getTitleBMFontForState(CCControlState state);

// 使用点九图CCScale9Sprite,设置在指定CCControlState下的 背景精灵
virtual void setBackgroundSpriteForState(CCScale9Sprite* sprite, CCControlState state);

// 使用精灵帧CCSpriteFrame,设置在指定CCControlState下的 背景精灵
// 其实在内部实现的代码,实际上是利用精灵帧spriteFrame创建了点九图CCScale9Sprite作为背景精灵
virtual void setBackgroundSpriteFrameForState(CCSpriteFrame* spriteFrame, CCControlState state);

// 获取在指定CCControlState下的 背景图
virtual CCScale9Sprite* getBackgroundSpriteForState(CCControlState state);

/**
* 继承于父类
*/
virtual void setEnabled(bool enabled); // 是否启用
virtual void setSelected(bool enabled); // 是否选中
virtual void setHighlighted(bool enabled); // 是否高亮
};

四、代码实战

(一)代码来源

代码来源于 Cocos2d-x 的官方项目 TestCpp

(二)具体实现步骤

1. 引入头文件和命名空间

#include "cocos-ext.h"
using namespace cocos2d::extension;

2. 在 HelloWorld.h 中声明按钮事件的回调函数、显示按钮状态的 Label

CCLabelTTF* displayLabel; // 显示按钮状态的label
void touchDownAction(CCObject* sender, CCControlEvent controlEvent); // 刚刚开始触摸按钮时
void touchDragInsideAction(CCObject* sender, CCControlEvent controlEvent); // 在内部拖动时(保持触摸状态下)
void touchDragOutsideAction(CCObject* sender, CCControlEvent controlEvent); // 在外部拖动时(保持触摸状态下)
void touchDragEnterAction(CCObject* sender, CCControlEvent controlEvent); // 拖动刚进入内部时(保持触摸状态下)
void touchDragExitAction(CCObject* sender, CCControlEvent controlEvent); // 拖动刚离开内部时(保持触摸状态下)
void touchUpInsideAction(CCObject* sender, CCControlEvent controlEvent); // 在内部抬起手指(保持触摸状态下)
void touchUpOutsideAction(CCObject* sender, CCControlEvent controlEvent); // 在外部抬起手指(保持触摸状态下)
void touchCancelAction(CCObject* sender, CCControlEvent controlEvent); // 取消触点

3. 在 HelloWorld.cppinit 中创建按钮,并绑定按钮事件

bool HelloWorld::init()
{
if ( !CCLayer::init() )
{
return false;
}

// 获取可视区域尺寸大小
CCSize mysize = CCDirector::sharedDirector()->getVisibleSize();
// 获取可视区域的原点位置
CCPoint origin = CCDirector::sharedDirector()->getVisibleOrigin();
// 屏幕正中心位置
CCPoint midPos = ccp(mysize.width/2, mysize.height/2);

// 显示按钮状态的标签displayLabel
displayLabel = CCLabelTTF::create("No Event", "Marker Felt", 32);
displayLabel->setPosition( midPos + ccp(0, 100) );
this->addChild(displayLabel);

// 按钮中的背景精灵CCScale9Sprite
CCScale9Sprite* bgNormal = CCScale9Sprite::create("btnNormal.png"); // 正常背景
CCScale9Sprite* bgHighlighted = CCScale9Sprite::create("btnHighlighted.png"); // 高亮背景

// 按钮中的标签CCLabelTTF
CCLabelTTF* titleNormal = CCLabelTTF::create("Button is Normal !", "Marker Felt", 30);
CCLabelTTF* titleHighlighted = CCLabelTTF::create("Button is Highlighted !", "Marker Felt", 30);

// 创建按钮CCControlButton
CCControlButton* btn = CCControlButton::create(titleNormal, bgNormal);
btn->setPosition( midPos );
this->addChild(btn);

// 设置按钮高亮时的状态
btn->setTitleLabelForState(titleHighlighted, CCControlStateHighlighted); // 高亮标签
btn->setTitleColorForState(ccRED, CCControlStateHighlighted); // 红色
btn->setBackgroundSpriteForState(bgHighlighted, CCControlStateHighlighted); // 高亮背景

// 写了这句话,反而大小被固定了,没有按照label的大小进行自动伸展
// btn->setPreferredSize( CCSizeMake(120,40) );

// 绑定事件,用于显示按钮状态
btn->addTargetWithActionForControlEvents(this, cccontrol_selector(HelloWorld::touchDownAction), CCControlEventTouchDown); // 刚刚开始触摸按钮时
btn->addTargetWithActionForControlEvents(this, cccontrol_selector(HelloWorld::touchDragInsideAction), CCControlEventTouchDragInside); // 在内部拖动时(保持触摸状态下)
btn->addTargetWithActionForControlEvents(this, cccontrol_selector(HelloWorld::touchDragOutsideAction), CCControlEventTouchDragOutside); // 在外部拖动时(保持触摸状态下)
btn->addTargetWithActionForControlEvents(this, cccontrol_selector(HelloWorld::touchDragEnterAction), CCControlEventTouchDragEnter); // 拖动刚进入内部时(保持触摸状态下)
btn->addTargetWithActionForControlEvents(this, cccontrol_selector(HelloWorld::touchDragExitAction), CCControlEventTouchDragExit); // 拖动刚离开内部时(保持触摸状态下)
btn->addTargetWithActionForControlEvents(this, cccontrol_selector(HelloWorld::touchUpInsideAction), CCControlEventTouchUpInside); // 在内部抬起手指(保持触摸状态下)
btn->addTargetWithActionForControlEvents(this, cccontrol_selector(HelloWorld::touchUpOutsideAction), CCControlEventTouchUpOutside); // 在外部抬起手指(保持触摸状态下)
btn->addTargetWithActionForControlEvents(this, cccontrol_selector(HelloWorld::touchCancelAction), CCControlEventTouchCancel); // 取消触点

return true;
}

4. 实现按钮事件的回调函数

// 刚刚开始触摸按钮时
void HelloWorld::touchDownAction(CCObject *sender, CCControlEvent controlEvent)
{
displayLabel->setString("Touch Down");
}

// 在内部拖动时(保持触摸状态下)
void HelloWorld::touchDragInsideAction(CCObject *sender, CCControlEvent controlEvent)
{
displayLabel->setString("Drag Inside");
}

// 在外部拖动时(保持触摸状态下)
void HelloWorld::touchDragOutsideAction(CCObject *sender, CCControlEvent controlEvent)
{
displayLabel->setString("Drag Outside");
}

// 拖动刚进入内部时(保持触摸状态下)
void HelloWorld::touchDragEnterAction(CCObject *sender, CCControlEvent controlEvent)
{
displayLabel->setString("Drag Enter");
}

// 拖动刚离开内部时(保持触摸状态下)
void HelloWorld::touchDragExitAction(CCObject *sender, CCControlEvent controlEvent)
{
displayLabel->setString("Drag Exit");
}

// 在内部抬起手指(保持触摸状态下)
void HelloWorld::touchUpInsideAction(CCObject *sender, CCControlEvent controlEvent)
{
displayLabel->setString("Touch Up Inside.");
}

// 在外部抬起手指(保持触摸状态下)
void HelloWorld::touchUpOutsideAction(CCObject *sender, CCControlEvent controlEvent)
{
displayLabel->setString("Touch Up Outside.");
}

// 取消所有触摸
void HelloWorld::touchCancelAction(CCObject *sender, CCControlEvent controlEvent)
{
displayLabel->setString("Touch Cancel");
}

(三)分析与总结

  1. 按钮大小伸展问题:当标签 CCLabelTTF 的大小大于按钮大小时,按钮会自动进行伸展,这解释了小按钮图片变大的现象。
  2. 高亮状态标签显示问题:设置了高亮状态下的标签为 titleHighlighted,但标签内容仍显示 “Button is Normal”,而字体颜色和按钮的背景精灵图确实已更换,此问题原因暂时不明。
  3. 按钮事件CCControlEventTouchDragEnter(拖动刚进入内部时)和 CCControlEventTouchDragExit(拖动刚离开内部时)这两个事件较难观察,因为只有在正好进入或离开按钮内部时才看得到效果,手部操作稍有抖动就会变成 DragInsideDragOutside 状态。

作者信息

boke

boke

共发布了 3994 篇文章