源代码版本号来自3.1rc

转载请注明

cocos2d-x源代码分析总文件夹

http://blog.csdn.net/u011225840/article/details/31743129

1.继承结构

       control的设计总体感觉挺美的,在父类control定义了整个控制事件的基础以及管理,尽管其继承了Layer,但其本身和UI组件的实现并没有关联。

在子类(controlButton,controlSwitch,controlStepper等中实现不同的UI组件)。以下通过源代码来分析control与controlButton。一起来体会以下向对象的魅力。

2.源代码分析

        2.1control 

          看过我Scheduler源代码分析的朋友应该熟悉,Scheduler本身属于一种Manager。而详细定时的动作来自于CallBackSelector。

在control总体的设计中也是如此,control中定义了一系列情况,来订制合适触发何种事件,而该事件是否触发某种Invocation。是能够动态设置的。

该Invocation就能够理解为详细的动作。

2.1.1 EventType

    enum class EventType
{
TOUCH_DOWN = 1 << 0, // A touch-down event in the control.
DRAG_INSIDE = 1 << 1, // An event where a finger is dragged inside the bounds of the control.
DRAG_OUTSIDE = 1 << 2, // An event where a finger is dragged just outside the bounds of the control.
DRAG_ENTER = 1 << 3, // An event where a finger is dragged into the bounds of the control.
DRAG_EXIT = 1 << 4, // An event where a finger is dragged from within a control to outside its bounds.
TOUCH_UP_INSIDE = 1 << 5, // A touch-up event in the control where the finger is inside the bounds of the control.
TOUCH_UP_OUTSIDE = 1 << 6, // A touch-up event in the control where the finger is outside the bounds of the control.
TOUCH_CANCEL = 1 << 7, // A system event canceling the current touches for the control.
VALUE_CHANGED = 1 << 8 // A touch dragging or otherwise manipulating a control, causing it to emit a series of different values.
};

        開始时,看见如此定义事实上有些不懂。可是为何须要这么设置呢,这样能够通过| 操作同一时候指定两个Event事件,而假设简单的使用 1 2 3 4,就不能通过|或者其它操作来唯一确定多个事件。

        从上到下,事件各自是在内部触摸,内部拖动,外部拖动,拖动时进入,拖动时离开。内部松开。外部松开,取消,值发生改变。

2.1.2 State

       
    enum class State
{
NORMAL = 1 << 0, // The normal, or default state of a control—that is, enabled but neither selected nor highlighted.
HIGH_LIGHTED = 1 << 1, // Highlighted state of a control. A control enters this state when a touch down, drag inside or drag enter is performed. You can retrieve and set this value through the highlighted property.
DISABLED = 1 << 2, // Disabled state of a control. This state indicates that the control is currently disabled. You can retrieve and set this value through the enabled property.
SELECTED = 1 << 3 // Selected state of a control. This state indicates that the control is currently selected. You can retrieve and set this value through the selected property.
};

       在control组件下,每个state都会有相应的UI形态。普通状态下,UI展示的view能够同被选择状态下UI展示的view不同。通过一个map来相应state和UI的存取。

2.1.3 Control Events 的管理

       2.1.3.1 sendActionsForControlEvents

       触发相应事件列表的事件action,注意Events是怎样表示的(通过bit位的相符,而不是一个list。速度快!)
void Control::sendActionsForControlEvents(EventType controlEvents)
{
//retain和release的作用是保证运行该actions的过程中。control不会被delete。 //可能会有actions会release 事件来源Ref--control,所以须要先retain,保证其运行全然部events后再release。
retain();
// For each control events
for (int i = 0; i < kControlEventTotalNumber; i++)
{
// If the given controlEvents bitmask contains the curent event
//bit位适配
if (((int)controlEvents & (1 << i)))
{
// Call invocations
const auto& invocationList = this->dispatchListforControlEvent((Control::EventType)(1<<i)); for(const auto &invocation : invocationList) {
invocation->invoke(this);
} }
}
release();
}

Vector<Invocation*>& Control::dispatchListforControlEvent(EventType controlEvent)
{
//这个函数的作用是获得该类事件类型的InvocationVector
Vector<Invocation*>* invocationList = nullptr;
auto iter = _dispatchTable.find((int)controlEvent); // If the invocation list does not exist for the dispatch table, we create it
if (iter == _dispatchTable.end())
{
invocationList = new Vector<Invocation*>();
_dispatchTable[(int)controlEvent] = invocationList;
}
else
{
invocationList = iter->second;
}
return *invocationList;
}

     2.1.3.2 addTargetWithActionForControlEvents

     
void Control::addTargetWithActionForControlEvents(Ref* target, Handler action, EventType controlEvents)
{
// For each control events
for (int i = 0; i < kControlEventTotalNumber; i++)
{
// If the given controlEvents bitmask contains the curent event
if (((int)controlEvents & (1 << i)))
{
this->addTargetWithActionForControlEvent(target, action, (EventType)(1<<i));
}
}
}
void Control::addTargetWithActionForControlEvent(Ref* target, Handler action, EventType controlEvent)
{
// Create the invocation object
Invocation *invocation = Invocation::create(target, action, controlEvent); // Add the invocation into the dispatch list for the given control event
auto& eventInvocationList = this->dispatchListforControlEvent(controlEvent);
//此时pushback的同一时候也会retain
eventInvocationList.pushBack(invocation);
}

2.1.3.3 removeTargetWithActionForControlEvent

void Control::removeTargetWithActionForControlEvents(Ref* target, Handler action, EventType controlEvents)
{
// For each control events
for (int i = 0; i < kControlEventTotalNumber; i++)
{
// If the given controlEvents bitmask contains the curent event
if (((int)controlEvents & (1 << i)))
{
this->removeTargetWithActionForControlEvent(target, action, (EventType)(1 << i));
}
}
}
 
void Control::removeTargetWithActionForControlEvent(Ref* target, Handler action, EventType controlEvent)
{
// Retrieve all invocations for the given control event
//<Invocation*>
auto& eventInvocationList = this->dispatchListforControlEvent(controlEvent); //remove all invocations if the target and action are null
//TODO: should the invocations be deleted, or just removed from the array? Won't that cause issues if you add a single invocation for multiple events? if (!target && !action)
{
//remove objects
eventInvocationList.clear();
}
else
{
std::vector<Invocation*> tobeRemovedInvocations; //normally we would use a predicate, but this won't work here. Have to do it manually
for(const auto &invocation : eventInvocationList) {
bool shouldBeRemoved=true;
if (target)
{
shouldBeRemoved=(target==invocation->getTarget());
}
if (action)
{
shouldBeRemoved=(shouldBeRemoved && (action==invocation->getAction()));
}
// Remove the corresponding invocation object
if (shouldBeRemoved)
{
tobeRemovedInvocations.push_back(invocation);
}
} for(const auto &invocation : tobeRemovedInvocations) {
eventInvocationList.eraseObject(invocation);
}
}
}

       在介绍controlbutton之前。我必须要再次强调下control源代码关于事件类型和状态的处理:使用bit位是否match能够唯一确定存,并能够消除使用list的影响,适合于enum类比較少且须要同一时候传递多个的情况。

2.2 ControlButton

      2.2.1 Touch

        对于controlButton,何时触发什么事件是在触摸机制中决定的,通过分析其源代码能够非常好看出。
       
bool ControlButton::onTouchBegan(Touch *pTouch, Event *pEvent)
{
//是否接收该touch
if (!isTouchInside(pTouch) || !isEnabled() || !isVisible() || !hasVisibleParents() )
{
return false;
} //感觉这段与hasVisibleParents反复了,能够删除
for (Node *c = this->_parent; c != nullptr; c = c->getParent())
{
if (c->isVisible() == false)
{
return false;
}
} _isPushed = true;
this->setHighlighted(true);
//touch down事件仅仅在began中触发
sendActionsForControlEvents(Control::EventType::TOUCH_DOWN);
return true;
}

void ControlButton::onTouchMoved(Touch *pTouch, Event *pEvent)
{ if (!isEnabled() || !isPushed() || isSelected())
{
if (isHighlighted())
{
setHighlighted(false);
}
return;
} bool isTouchMoveInside = isTouchInside(pTouch);
//在inside内部move而且当前状态不是highlight,说明从外部移入到内部,触发事件drag enter
if (isTouchMoveInside && !isHighlighted())
{
setHighlighted(true);
sendActionsForControlEvents(Control::EventType::DRAG_ENTER);
}
//inside内部move而且当前状态时highlight,说明在内部移动,触发事件 drag inside
else if (isTouchMoveInside && isHighlighted())
{
sendActionsForControlEvents(Control::EventType::DRAG_INSIDE);
}
//outside move 可是当前状态是highlight,证明从内移动到外。触发事件drag exit
else if (!isTouchMoveInside && isHighlighted())
{
setHighlighted(false); sendActionsForControlEvents(Control::EventType::DRAG_EXIT);
}
//outside move 而且 不是highlight 在外部移动,触发事件 drag outside
else if (!isTouchMoveInside && !isHighlighted())
{
sendActionsForControlEvents(Control::EventType::DRAG_OUTSIDE);
}
}

void ControlButton::onTouchEnded(Touch *pTouch, Event *pEvent)
{
_isPushed = false;
setHighlighted(false); //在这里事实上应该添加推断的,对于controlButton放在scrollView或者tableView或者能够移动的layer上的时候
//应该给用户一个开关选择。依据移动了距离的多少推断用户是否要触发touch up inside和 outside 事件。 if (isTouchInside(pTouch))
{
sendActionsForControlEvents(Control::EventType::TOUCH_UP_INSIDE);
}
else
{
sendActionsForControlEvents(Control::EventType::TOUCH_UP_OUTSIDE);
}
}

void ControlButton::onTouchCancelled(Touch *pTouch, Event *pEvent)
{
_isPushed = false;
setHighlighted(false);
sendActionsForControlEvents(Control::EventType::TOUCH_CANCEL);
}

2.2.2 create and needlayout

      control button 本质是一个label与一个scale9sprite。在其初始化中能够看出。

      2.2.2.1 create相关

      
bool ControlButton::initWithLabelAndBackgroundSprite(Node* node, Scale9Sprite* backgroundSprite)
{
if (Control::init())
{
CCASSERT(node != nullptr, "Label must not be nil.");
LabelProtocol* label = dynamic_cast<LabelProtocol*>(node);
CCASSERT(backgroundSprite != nullptr, "Background sprite must not be nil.");
CCASSERT(label != nullptr || backgroundSprite != nullptr, ""); _parentInited = true; _isPushed = false; // Adjust the background image by default
setAdjustBackgroundImage(true);
setPreferredSize(Size::ZERO);
// Zooming button by default
_zoomOnTouchDown = true;
_scaleRatio = 1.1f; // Set the default anchor point
ignoreAnchorPointForPosition(false);
setAnchorPoint(Vec2::ANCHOR_MIDDLE); // Set the nodes,label
setTitleLabel(node);
setBackgroundSprite(backgroundSprite); // Set the default color and opacity
setColor(Color3B::WHITE);
setOpacity(255.0f);
setOpacityModifyRGB(true); // Initialize the dispatch table,開始时候的状态皆为normal setTitleForState(label->getString(), Control::State::NORMAL);
setTitleColorForState(node->getColor(), Control::State::NORMAL);
setTitleLabelForState(node, Control::State::NORMAL);
setBackgroundSpriteForState(backgroundSprite, Control::State::NORMAL); setLabelAnchorPoint(Vec2::ANCHOR_MIDDLE); // Layout update
needsLayout(); return true;
}
//couldn't init the Control
else
{
return false;
}
}

          controlButton通过4个map,将状态相关的信息与UI须要显示的view存储起来。titleDispatch存放的是不同状态下label的string。titleColor存放的是不同状态下label的颜色,titleLabel存放的是不同状态下title绑定的Node,backgroundsprite是不同状态下的sprite。

    

    std::unordered_map<int, std::string> _titleDispatchTable;
std::unordered_map<int, Color3B> _titleColorDispatchTable; Map<int, Node*> _titleLabelDispatchTable;
Map<int, Scale9Sprite*> _backgroundSpriteDispatchTable;

        而且通过一系列get set函数将状态与这些属性相关联,详细的不再赘述。

2.2.2.2 needlayout

      
void ControlButton::needsLayout()
{
//总体步骤:获取特定状态下的label和sprite。然后将button的size设置为两者的最大值,然后显示两者
if (!_parentInited) {
return;
}
// Hide the background and the label
if (_titleLabel != nullptr) {
_titleLabel->setVisible(false);
}
if (_backgroundSprite) {
_backgroundSprite->setVisible(false);
}
// Update anchor of all labels
this->setLabelAnchorPoint(this->_labelAnchorPoint); // Update the label to match with the current state
_currentTitle = getTitleForState(_state); _currentTitleColor = getTitleColorForState(_state); this->setTitleLabel(getTitleLabelForState(_state)); LabelProtocol* label = dynamic_cast<LabelProtocol*>(_titleLabel);
if (label && !_currentTitle.empty())
{
label->setString(_currentTitle);
} if (_titleLabel)
{
_titleLabel->setColor(_currentTitleColor);
}
if (_titleLabel != nullptr)
{
_titleLabel->setPosition(Vec2 (getContentSize().width / 2, getContentSize().height / 2));
} // Update the background sprite
this->setBackgroundSprite(this->getBackgroundSpriteForState(_state));
if (_backgroundSprite != nullptr)
{
_backgroundSprite->setPosition(Vec2 (getContentSize().width / 2, getContentSize().height / 2));
} // Get the title label size
Size titleLabelSize;
if (_titleLabel != nullptr)
{
titleLabelSize = _titleLabel->getBoundingBox().size;
} // Adjust the background image if necessary
if (_doesAdjustBackgroundImage)
{
// Add the margins
if (_backgroundSprite != nullptr)
{
_backgroundSprite->setContentSize(Size(titleLabelSize.width + _marginH * 2, titleLabelSize.height + _marginV * 2));
}
}
else
{
//TODO: should this also have margins if one of the preferred sizes is relaxed? if (_backgroundSprite != nullptr)
{
Size preferredSize = _backgroundSprite->getPreferredSize();
if (preferredSize.width <= 0)
{
preferredSize.width = titleLabelSize.width;
}
if (preferredSize.height <= 0)
{
preferredSize.height = titleLabelSize.height;
} _backgroundSprite->setContentSize(preferredSize);
}
} // Set the content size
//总体来说。须要注意的就是这里。将两者size的最大值赋给本身
Rect rectTitle;
if (_titleLabel != nullptr)
{
rectTitle = _titleLabel->getBoundingBox();
}
Rect rectBackground;
if (_backgroundSprite != nullptr)
{
rectBackground = _backgroundSprite->getBoundingBox();
} Rect maxRect = ControlUtils::RectUnion(rectTitle, rectBackground);
setContentSize(Size(maxRect.size.width, maxRect.size.height)); if (_titleLabel != nullptr)
{
_titleLabel->setPosition(Vec2(getContentSize().width/2, getContentSize().height/2));
// Make visible the background and the label
_titleLabel->setVisible(true);
} if (_backgroundSprite != nullptr)
{
_backgroundSprite->setPosition(Vec2(getContentSize().width/2, getContentSize().height/2));
_backgroundSprite->setVisible(true);
}
}

3.小结

         关于其它control类组件包含:
         controlColourPicker:颜色选择器
         controlHuePicker:色调选择器
         controlSwitch:开关
         controlSlider:滑块
         controlStepper:计步器
         controlPotentioMeter:恒电位仪表。。。(一个圆形仪表。能够旋转,而且有一个圆形的progress bar)
         controlsaturationbrightnessPicker:饱和度亮度选择器
 

cocos2d-x 源代码分析 : control 源代码分析 ( 控制类组件 controlButton)的更多相关文章

  1. 分析setting源代码获取sd卡大小

    分析setting源代码获取sd卡大小 android系统有一个特点,即开源,我们可以得到任何一个应用的源代码,比如我们不知道这样的android代码怎么写,我们可以打开模拟器里面的设置(settin ...

  2. 通过分析 JDK 源代码研究 TreeMap 红黑树算法实现

    本文转载自http://www.ibm.com/developerworks/cn/java/j-lo-tree/ 目录: TreeSet 和 TreeMap 的关系 TreeMap 的添加节点 Tr ...

  3. Memcached源代码分析 - Memcached源代码分析之消息回应(3)

    文章列表: <Memcached源代码分析 - Memcached源代码分析之基于Libevent的网络模型(1)> <Memcached源代码分析 - Memcached源代码分析 ...

  4. x264源代码简单分析:宏块分析(Analysis)部分-帧间宏块(Inter)

    ===================================================== H.264源代码分析文章列表: [编码 - x264] x264源代码简单分析:概述 x26 ...

  5. x264源代码简单分析:宏块分析(Analysis)部分-帧内宏块(Intra)

    ===================================================== H.264源代码分析文章列表: [编码 - x264] x264源代码简单分析:概述 x26 ...

  6. ffdshow 源代码分析 9: 编解码器有关类的总结

    ===================================================== ffdshow源代码分析系列文章列表: ffdshow 源代码分析 1: 整体结构 ffds ...

  7. ffdshow 源代码分析 8: 视频解码器类(TvideoCodecDec)

    ===================================================== ffdshow源代码分析系列文章列表: ffdshow 源代码分析 1: 整体结构 ffds ...

  8. Go/Python/Erlang编程语言对比分析及示例 基于RabbitMQ.Client组件实现RabbitMQ可复用的 ConnectionPool(连接池) 封装一个基于NLog+NLog.Mongo的日志记录工具类LogUtil 分享基于MemoryCache(内存缓存)的缓存工具类,C# B/S 、C/S项目均可以使用!

    Go/Python/Erlang编程语言对比分析及示例   本文主要是介绍Go,从语言对比分析的角度切入.之所以选择与Python.Erlang对比,是因为做为高级语言,它们语言特性上有较大的相似性, ...

  9. 常用 Java 静态代码分析工具的分析与比较

    常用 Java 静态代码分析工具的分析与比较 简介: 本文首先介绍了静态代码分析的基 本概念及主要技术,随后分别介绍了现有 4 种主流 Java 静态代码分析工具 (Checkstyle,FindBu ...

随机推荐

  1. 《大话操作系统——做坚实的project实践派》(3)

    watermark/2/text/aHR0cDovL2Jsb2cuY3Nkbi5uZXQvbG1ub3M=/font/5a6L5L2T/fontsize/400/fill/I0JBQkFCMA==/d ...

  2. 系统重装 U盘安装XP操作系统开机出现提示txtsetup.sif怎么办

    你的这个问题 是安装xp时把xp做成u盘出现的 原因是xp没有在根本上支持这种安装 到win7后才支持的 解决方法有以下几种 1,刻录成cd 2重新下载xp ghost版的不会出现这个问题 3证实Ul ...

  3. red5截屏

    在red5-web.xml添加 <bean id="rtmpSampleAccess" class="org.red5.server.stream.RtmpSamp ...

  4. Oracle 为表空间增加数据文件

    dba权限检查下 select tablespace_name, file_id, file_name, ),) total_space from dba_data_files order by ta ...

  5. .Net开发复习与总结

    1.文件操作 1.1递归目录,查找所有文件 #region 递归目录路径 private List<string> GetDirs(string path) { string[] dirs ...

  6. 教你用squid做CDN把公司做到上市

    我们都知道CDN(内容分发网络)是用来给网站加速用的,通过在现有的Internet中增加一层新的网络架构,将网站的内容发布到最接近用户的网络的“边缘”,使用户可以就近取得所需的内容,以提高用户访问网站 ...

  7. memcache原理和实际应用

    Memcache是什么 Memcache是danga.com的一个项目,最早是为 LiveJournal 服务的.眼下全世界不少人使用这个缓存项目来构建自己大负载的站点,来分担数据库的压力. 它能够应 ...

  8. DirectShow使用心得

    用了3天时间,将DShow加入到了游戏中,记录下心得,方便要使用的童鞋以及以后的自己查看.1. Video Mixing Renderer 9,内部使用Direct3D 9,需要Windows XP或 ...

  9. PathInfo模式的支持

    pathinfo,一种伪静态的用法, 1.让 Apache 支持 PathInfo 配置的 Apache 版本 : 2.2.13 在配置文件中加入 <Files *.php> Accept ...

  10. 在Ubuntu下利用Eclipse开发FFmpeg配置小结

    首先需要编译FFmpeg得到头文件和lib文件,参见:在Ubuntu下编译FFmpeg 选择File-New-C Project 选择Executable下的Empty Project,右侧选择Lin ...