cocostudio编辑器中的控件都是继承自Widget,这些控件都被添加到TouchGroup层中。所有的控件事件的接
收都是依靠最外层的TouchGroup传递的。
TouchGroupj继承自CCLayer,重写了CCTouchDelegate的四个方法。
bool TouchGroup::ccTouchBegan(CCTouch *pTouch, CCEvent *pEvent)
{
return checkEventWidget(pTouch, pEvent);
}
bool TouchGroup::checkEventWidget(CCTouch* touch, CCEvent *pEvent)
{
checkTouchEvent(m_pRootWidget,touch, pEvent);
return (m_pSelectedWidgets->count() > 0);
}
bool TouchGroup::checkTouchEvent(Widget *root, CCTouch* touch, CCEvent* pEvent)
{
/*这个数组,每一帧在visit方法中根据ZOder排好序的(详见Widget的sortAllChildren方法),
所以在TouchGroup中ZOder越大,先接收到事件*/
ccArray* arrayRootChildren = root->getChildren()->data;
int length = arrayRootChildren->num;
for (int i=length-1; i >= 0; i--)
{
Widget* widget = (Widget*)(arrayRootChildren->arr[i]);
if (checkTouchEvent(widget, touch, pEvent))
{
return true;
}
}
bool pass = root->onTouchBegan(touch, pEvent);
if (root->_hitted)
{
m_pSelectedWidgets->addObject(root);
return true;
}
return pass;
}
其中ccTouchBegan方法调用了checkEventWidget,checkEventWidget调用了checkTouchEvent。
checkTTouchEvent是一个递归函数。会递归检查TouchGroup中m_pRootWidget的子节点。
这里有一点值得要提的是往TouchGroup里加控件不要用addChild,要用addWidget,因为触摸事件检查的节点是m_pRootWidget为根的树,所有直接加到TouchGroup中是接收不到触摸事件的。
void TouchGroup::addWidget(Widget* widget)
{
m_pRootWidget->addChild(widget);
}
当控件没有子节点会调用Widget中的onTouchBegan中的检查控件的Enable,TouchEnable和hitTest。
所以Cocostudio的控件隐藏用setEnbale(false),不要用setVisible(false).setVisible只是不渲染,但是还是
会接收到事件的,setEnbale的话在渲染和事件的接收都会做判断的。
bool Widget::onTouchBegan(CCTouch *touch, CCEvent *unused_event)
{
_hitted = false;
if (isEnabled() && isTouchEnabled())
{
_touchStartPos = touch->getLocation();
if(hitTest(_touchStartPos) && clippingParentAreaContainPoint(_touchStartPos))
{
_hitted = true;
}
}
if (!_hitted)
{
return false;
}
setFocused(true);
//先传递给父节点处理
Widget* widgetParent = getWidgetParent();
if (widgetParent)
{
widgetParent->checkChildInfo(0,this,_touchStartPos);
}
pushDownEvent();
return !_touchPassedEnabled;
}
这其中还有一个机制,就是当一个控件接收到事件之后,会先传递给父节点处理.
其中checkChildInfo是个虚函数.控件可以重写这个方法,来接收处理子节点穿过来的事件。
virtual void checkChildInfo(int handleState,Widget* sender,const CCPoint &touchPoint);
其中handleState的值0为begin.1为move,2为end,3为cancel。
这个方法,一般的控件是没有重写的,也就是继续往父节点传,而不做处理。
只有ScrollView,PageView重写这个函数。
void ScrollView::checkChildInfo(int handleState,Widget* sender,const CCPoint &touchPoint)
{
interceptTouchEvent(handleState, sender, touchPoint);
}
基本上就这些了。
还有一问题就是onTouchEnd,onTouchCanel两个个方法都是在什么时候触发。
在Widget中的ccTouchBegan方法中如果控件检查符合条件被击中则setFocused(true)
ccTouchMoved中在移动的过程中会检查移动的点是否还在控件内,来设置焦点。
setFocused(hitTest(_touchMovePos));
在onTouchEnded中会检查控件是否获得焦点,如果获得焦点正常releaseUpEvent
没有焦点cancelUpEvent