源代码版本号来自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. javascript - 封装ajax

    封装,使用有示例. // 封装示例: function ajax(url, method, params, done) { var xhr = null; method = method.toUppe ...

  2. Java Learning Path(四) 方法篇

    Java Learning Path(四) 方法篇 Java作为一门编程语言,最好的学习方法就是写代码.当你学习一个类以后,你就可以自己写个简单的例子程序来运行一下,看看有什么结果,然后再多调用几个类 ...

  3. different between method and function

    A method is on an object. A function is independent of an object. For Java, there are only methods. ...

  4. Mongodb基本操作入门,增删改查和索引

    主要进程 mongod.exe为启动数据库实例的进程. mongo是一个与mongod进程进行交互的JavaScript shell进程,它提供了一些交互的接口函数用户对数据库的管理. 基本命令 sh ...

  5. Atitit.业务系统的新特性 开发平台 新特性的来源总结

    Atitit.业务系统的新特性 开发平台 新特性的来源总结 1.1. 语言新特性(java c# php js python lisp c++ oc swift ruby  go dart1 1.2. ...

  6. baksmali反编译出现:UNEXPECTED TOP-LEVEL ERROR:....Too many open files

    解包大型apk文件,可能会出现例如以下错误, UNEXPECTED TOP-LEVEL ERROR: java.util.concurrent.ExecutionException: java.io. ...

  7. 【数据挖掘】关联分析之Apriori(转载)

    [数据挖掘]关联分析之Apriori 1.Apriori算法 如果一个事务中有X,则该事务中则很有可能有Y,写成关联规则 {X}→{Y} 将这种找出项目之间联系的方法叫做关联分析.关联分析中最有名的问 ...

  8. hadoop生态系统学习之路(八)hbase与hive的数据同步以及hive与impala的数据同步

    在之前的博文中提到,hive的表数据是能够同步到impala中去的. 一般impala是提供实时查询操作的,像比較耗时的入库操作我们能够使用hive.然后再将数据同步到impala中.另外,我们也能够 ...

  9. CyclicBarrier的工作原理及其实例

    CyclicBarrier是多线程中一个重要的类,主要用于线程组内部之间的线程的相互等待问题. 1.CyclicBarrier的工作原理 CyclicBarrier大致是可循环利用的屏障,顾名思义,这 ...

  10. 转载 iOS全局检测网络变化的实时状态

      昨天浏览了cocoaChina,发现了一遍文章是优化Reachablity框架的出来的检测网络类,大家都知道这个Reachablity框架是用来检测网络变化的!但是也是有一点bug,事实上,基于此 ...