对应源码位置:cocos2d-x-3.3\cocos\ui\UI*
Layout的原理
这里选取少量的重点部分。
//找当前节点的下一个可以聚焦的节点
//主要看思想 已经删了很多了
Widget* Layout::getNextFocusedWidget(FocusDirection direction, Widget *current)
{
Widget *nextWidget = nullptr;
ssize_t previousWidgetPos = _children.getIndex(current);
//选取下一个 孩子节点
previousWidgetPos = previousWidgetPos + 1;
if (previousWidgetPos < _children.size())
{
//找下一个Widget
nextWidget = this->getChildWidgetByIndex(previousWidgetPos);
//handle widget
if (nextWidget)
{
//可以聚焦再看
if (nextWidget->isFocusEnabled())
{
//如果是个 Layout 就在里面找 直到找到一个控件
Layout* layout = dynamic_cast<Layout*>(nextWidget);
if (layout)
{
layout->_isFocusPassing = true;
return layout->findNextFocusedWidget(direction, layout);
}
else
{
//否则 就是你了 回调一下
this->dispatchFocusEvent(current, nextWidget);
return nextWidget;
}
}
else
{
return this->getNextFocusedWidget(direction, nextWidget);
}
}
else
{
return current;
}
}
}
//看看 Layout具体怎么绘制
//这就是Node 节点都有的 visit
void Layout::visit(Renderer *renderer, const Mat4 &parentTransform, uint32_t parentFlags)
{
if (!_visible)
{
return;
}
//空函数
adaptRenderers();
//核心部分 处理布局 后面详细
doLayout();
//先假装 不满足 直走else
if (_clippingEnabled)
{
switch (_clippingType)
{
case ClippingType::STENCIL:
stencilClippingVisit(renderer, parentTransform, parentFlags);
break;
case ClippingType::SCISSOR:
scissorClippingVisit(renderer, parentTransform, parentFlags);
break;
default:
break;
}
}
//就是以前普通的visit
else
{
Widget::visit(renderer, parentTransform, parentFlags);
}
}
//处理布局的核心
void Layout::doLayout()
{
//如果布局没有变化
if (!_doLayoutDirty)
{
return;
}
//排序
sortAllChildren();
//创建 Layoutmanager
LayoutManager* executant = this->createLayoutManager();
if (executant)
{
//核心部分
executant->doLayout(this);
}
_doLayoutDirty = false;
}
//就是根据布局的类型生成对应的manager
LayoutManager* Layout::createLayoutManager()
{
LayoutManager* exe = nullptr;
switch (_layoutType)
{
case Type::VERTICAL:
exe = LinearVerticalLayoutManager::create();
break;
case Type::HORIZONTAL:
exe = LinearHorizontalLayoutManager::create();
break;
case Type::RELATIVE:
exe = RelativeLayoutManager::create();
break;
default:
break;
}
return exe;
}
LayoutManager 的分析
class CC_GUI_DLL LayoutManager : public Ref
{
public:
virtual ~LayoutManager(){};
LayoutManager(){};
//虚函数
virtual void doLayout(LayoutProtocol *layout) = 0;
friend class Layout;
};
//垂直的线性布局
class CC_GUI_DLL LinearVerticalLayoutManager : public LayoutManager
{
private:
LinearVerticalLayoutManager(){};
virtual ~LinearVerticalLayoutManager(){};
static LinearVerticalLayoutManager* create();
virtual void doLayout(LayoutProtocol *layout) override;
friend class Layout;
};
//水平的线性布局
class CC_GUI_DLL LinearHorizontalLayoutManager : public LayoutManager
{
private:
LinearHorizontalLayoutManager(){};
virtual ~LinearHorizontalLayoutManager(){};
static LinearHorizontalLayoutManager* create();
virtual void doLayout(LayoutProtocol *layout) override;
friend class Layout;
};
//具体实现 选一个 其思想就是 计算出最终的位置 相对布局更加复杂
void LinearHorizontalLayoutManager::doLayout(LayoutProtocol* layout)
{
//获取布局本身的大小
Size layoutSize = layout->getLayoutContentSize();
//获取布局的孩子节点
Vector<Node*> container = layout->getLayoutElements();
float leftBoundary = 0.0f;
for (auto& subWidget : container)
{
//获取这个控件
Widget* child = dynamic_cast<Widget*>(subWidget);
if (child)
{
LinearLayoutParameter* layoutParameter = dynamic_cast<LinearLayoutParameter*>(child->getLayoutParameter());
if (layoutParameter)
{
LinearLayoutParameter::LinearGravity childGravity = layoutParameter->getGravity();
Vec2 ap = child->getAnchorPoint();
Size cs = child->getContentSize();
//计算最终位置
//x的位置 找到锚点位置 确定锚点位置的坐标
float finalPosX = leftBoundary + (ap.x * cs.width);
float finalPosY = layoutSize.height - (1.0f - ap.y) * cs.height;
//此时为 top的方式
switch (childGravity)
{
case LinearLayoutParameter::LinearGravity::NONE:
case LinearLayoutParameter::LinearGravity::TOP:
break;
case LinearLayoutParameter::LinearGravity::BOTTOM:
//放在底部
finalPosY = ap.y * cs.height;
break;
//垂直 居中
case LinearLayoutParameter::LinearGravity::CENTER_VERTICAL:
finalPosY = layoutSize.height / 2.0f - cs.height * (0.5f - ap.y);
break;
default:
break;
}
//再加上外边距
Margin mg = layoutParameter->getMargin();
finalPosX += mg.left;
finalPosY -= mg.top;
child->setPosition(Vec2(finalPosX, finalPosY));
leftBoundary = child->getRightBoundary() + mg.right;
}
}
}
}
重点分析RelativeLayoutManager
很长,但很有意思。
Vector<Widget*> RelativeLayoutManager::getAllWidgets(cocos2d::ui::LayoutProtocol *layout)
{
//获取所有孩子节点
Vector<Node*> container = layout->getLayoutElements();
Vector<Widget*> widgetChildren;
for (auto& subWidget : container)
{
//看看 是不是 Widget 子类
Widget* child = dynamic_cast<Widget*>(subWidget);
if (child)
{
//获取 layoutParameter
RelativeLayoutParameter* layoutParameter = dynamic_cast<RelativeLayoutParameter*>(child->getLayoutParameter());
layoutParameter->_put = false;
//未处理布局数据加1
_unlayoutChildCount++;
//加入到vector
widgetChildren.pushBack(child);
}
}
return widgetChildren;
}
//获取相对的Widget
Widget* RelativeLayoutManager::getRelativeWidget(Widget* widget)
{
Widget* relativeWidget = nullptr;
RelativeLayoutParameter* layoutParameter = dynamic_cast<RelativeLayoutParameter*>(widget->getLayoutParameter());
//获取其相对的控件的名字
const std::string relativeName = layoutParameter->getRelativeToWidgetName();
//看有没有
if (!relativeName.empty())
{
for (auto& sWidget : _widgetChildren)
{
if (sWidget)
{
//遍历子节点 看看有没有他所说的那个名字的节点
RelativeLayoutParameter* rlayoutParameter = dynamic_cast<RelativeLayoutParameter*>(sWidget->getLayoutParameter());
if (rlayoutParameter && rlayoutParameter->getRelativeName() == relativeName)
{
relativeWidget = sWidget;
_relativeWidgetLP = rlayoutParameter;
break;
}
}
}
}
return relativeWidget;
}
//核心方法
void RelativeLayoutManager::doLayout(LayoutProtocol *layout)
{
//获取所有的孩子控件
_widgetChildren = this->getAllWidgets(layout);
//如果未处理节点数目大于0
while (_unlayoutChildCount > 0)
{
for (auto& subWidget : _widgetChildren)
{
//当前要处理的 控件 _widget 为内部变量
_widget = static_cast<Widget*>(subWidget);
//获取参数
RelativeLayoutParameter* layoutParameter = dynamic_cast<RelativeLayoutParameter*>(_widget->getLayoutParameter());
if (layoutParameter)
{
if (layoutParameter->_put)
{
continue;
}
//根据相对位置计算 返回值表示计算成功了没 成功了就继续
bool ret = this->caculateFinalPositionWithRelativeWidget(layout);
if (!ret) {
continue;
}
//计算 附加上边距的最终值。
this->caculateFinalPositionWithRelativeAlign();
//成功了
_widget->setPosition(Vec2(_finalPositionX, _finalPositionY));
//这个计算好了
layoutParameter->_put = true;
}
}
_unlayoutChildCount--;
}
_widgetChildren.clear();
}
bool RelativeLayoutManager::caculateFinalPositionWithRelativeWidget(LayoutProtocol *layout)
{
//获取 锚点 和大小
Vec2 ap = _widget->getAnchorPoint();
Size cs = _widget->getContentSize();
//最终位置
_finalPositionX = 0.0f;
_finalPositionY = 0.0f;
//获取相对节点
Widget* relativeWidget = this->getRelativeWidget(_widget);
//获取参数
RelativeLayoutParameter* layoutParameter = dynamic_cast<RelativeLayoutParameter*>(_widget->getLayoutParameter());
//相对关系
RelativeLayoutParameter::RelativeAlign align = layoutParameter->getAlign();
//获取布局的大小
Size layoutSize = layout->getLayoutContentSize();
switch (align)
{
case RelativeLayoutParameter::RelativeAlign::NONE:
//位于父级空间的 左上
case RelativeLayoutParameter::RelativeAlign::PARENT_TOP_LEFT:
_finalPositionX = ap.x * cs.width;
_finalPositionY = layoutSize.height - ((1.0f - ap.y) * cs.height);
break;
case RelativeLayoutParameter::RelativeAlign::PARENT_TOP_CENTER_HORIZONTAL:
//位于父级空间的 顶部的水平居中
_finalPositionX = layoutSize.width * 0.5f - cs.width * (0.5f - ap.x);
_finalPositionY = layoutSize.height - ((1.0f - ap.y) * cs.height);
break;
//位于父级空间的 顶部的右侧
case RelativeLayoutParameter::RelativeAlign::PARENT_TOP_RIGHT:
_finalPositionX = layoutSize.width - ((1.0f - ap.x) * cs.width);
_finalPositionY = layoutSize.height - ((1.0f - ap.y) * cs.height);
break;
//位于父级空间的 左边 垂直居中
case RelativeLayoutParameter::RelativeAlign::PARENT_LEFT_CENTER_VERTICAL:
_finalPositionX = ap.x * cs.width;
_finalPositionY = layoutSize.height * 0.5f - cs.height * (0.5f - ap.y);
break;
//位于父级空间的 正中间
case RelativeLayoutParameter::RelativeAlign::CENTER_IN_PARENT:
_finalPositionX = layoutSize.width * 0.5f - cs.width * (0.5f - ap.x);
_finalPositionY = layoutSize.height * 0.5f - cs.height * (0.5f - ap.y);
break;
//位于父级空间的 右边 垂直居中
case RelativeLayoutParameter::RelativeAlign::PARENT_RIGHT_CENTER_VERTICAL:
_finalPositionX = layoutSize.width - ((1.0f - ap.x) * cs.width);
_finalPositionY = layoutSize.height * 0.5f - cs.height * (0.5f - ap.y);
break;
//位于父级空间的 左下角
case RelativeLayoutParameter::RelativeAlign::PARENT_LEFT_BOTTOM:
_finalPositionX = ap.x * cs.width;
_finalPositionY = ap.y * cs.height;
break;
//位于父级空间的 底部的水平居中
case RelativeLayoutParameter::RelativeAlign::PARENT_BOTTOM_CENTER_HORIZONTAL:
_finalPositionX = layoutSize.width * 0.5f - cs.width * (0.5f - ap.x);
_finalPositionY = ap.y * cs.height;
break;
//位于父级空间的 右下角
case RelativeLayoutParameter::RelativeAlign::PARENT_RIGHT_BOTTOM:
_finalPositionX = layoutSize.width - ((1.0f - ap.x) * cs.width);
_finalPositionY = ap.y * cs.height;
break;
//位于其它控件的上面 左侧对齐
case RelativeLayoutParameter::RelativeAlign::LOCATION_ABOVE_LEFTALIGN:
if (relativeWidget)
{
//相对的那个控件 还没有计算 不行
if (_relativeWidgetLP && !_relativeWidgetLP->_put)
{
return false;
}
//相对于的控件 已经计算过了
//获取相对者的 上边界与左边界
float locationTop = relativeWidget->getTopBoundary();
float locationLeft = relativeWidget->getLeftBoundary();
//计算 自己的位置
_finalPositionY = locationTop + ap.y * cs.height;
_finalPositionX = locationLeft + ap.x * cs.width;
}
break;
//位于其它控件的上面 居中
case RelativeLayoutParameter::RelativeAlign::LOCATION_ABOVE_CENTER:
if (relativeWidget)
{
if (_relativeWidgetLP && !_relativeWidgetLP->_put)
{
return false;
}
Size rbs = relativeWidget->getContentSize();
float locationTop = relativeWidget->getTopBoundary();
_finalPositionY = locationTop + ap.y * cs.height;
_finalPositionX = relativeWidget->getLeftBoundary() + rbs.width * 0.5f + ap.x * cs.width - cs.width * 0.5f;
}
break;
//上面右边对齐
case RelativeLayoutParameter::RelativeAlign::LOCATION_ABOVE_RIGHTALIGN:
if (relativeWidget)
{
if (_relativeWidgetLP && !_relativeWidgetLP->_put)
{
return false;
}
float locationTop = relativeWidget->getTopBoundary();
float locationRight = relativeWidget->getRightBoundary();
_finalPositionY = locationTop + ap.y * cs.height;
_finalPositionX = locationRight - (1.0f - ap.x) * cs.width;
}
break;
//左侧 顶部对齐
case RelativeLayoutParameter::RelativeAlign::LOCATION_LEFT_OF_TOPALIGN:
if (relativeWidget)
{
if (_relativeWidgetLP && !_relativeWidgetLP->_put)
{
return false;
}
float locationTop = relativeWidget->getTopBoundary();
float locationLeft = relativeWidget->getLeftBoundary();
_finalPositionY = locationTop - (1.0f - ap.y) * cs.height;
_finalPositionX = locationLeft - (1.0f - ap.x) * cs.width;
}
break;
//左侧居中
case RelativeLayoutParameter::RelativeAlign::LOCATION_LEFT_OF_CENTER:
if (relativeWidget)
{
if (_relativeWidgetLP && !_relativeWidgetLP->_put)
{
return false;
}
Size rbs = relativeWidget->getContentSize();
float locationLeft = relativeWidget->getLeftBoundary();
_finalPositionX = locationLeft - (1.0f - ap.x) * cs.width;
_finalPositionY = relativeWidget->getBottomBoundary() + rbs.height * 0.5f + ap.y * cs.height - cs.height * 0.5f;
}
break;
//左侧底部对齐
case RelativeLayoutParameter::RelativeAlign::LOCATION_LEFT_OF_BOTTOMALIGN:
if (relativeWidget)
{
if (_relativeWidgetLP && !_relativeWidgetLP->_put)
{
return false;
}
float locationBottom = relativeWidget->getBottomBoundary();
float locationLeft = relativeWidget->getLeftBoundary();
_finalPositionY = locationBottom + ap.y * cs.height;
_finalPositionX = locationLeft - (1.0f - ap.x) * cs.width;
}
break;
//右侧 顶部对齐
case RelativeLayoutParameter::RelativeAlign::LOCATION_RIGHT_OF_TOPALIGN:
if (relativeWidget)
{
if (_relativeWidgetLP && !_relativeWidgetLP->_put)
{
return false;
}
float locationTop = relativeWidget->getTopBoundary();
float locationRight = relativeWidget->getRightBoundary();
_finalPositionY = locationTop - (1.0f - ap.y) * cs.height;
_finalPositionX = locationRight + ap.x * cs.width;
}
break;
//右侧 居中
case RelativeLayoutParameter::RelativeAlign::LOCATION_RIGHT_OF_CENTER:
if (relativeWidget)
{
if (_relativeWidgetLP && !_relativeWidgetLP->_put)
{
return false;
}
Size rbs = relativeWidget->getContentSize();
float locationRight = relativeWidget->getRightBoundary();
_finalPositionX = locationRight + ap.x * cs.width;
_finalPositionY = relativeWidget->getBottomBoundary() + rbs.height * 0.5f + ap.y * cs.height - cs.height * 0.5f;
}
break;
//右侧 底部对齐
case RelativeLayoutParameter::RelativeAlign::LOCATION_RIGHT_OF_BOTTOMALIGN:
if (relativeWidget)
{
if (_relativeWidgetLP && !_relativeWidgetLP->_put)
{
return false;
}
float locationBottom = relativeWidget->getBottomBoundary();
float locationRight = relativeWidget->getRightBoundary();
_finalPositionY = locationBottom + ap.y * cs.height;
_finalPositionX = locationRight + ap.x * cs.width;
}
break;
//下方 左对齐
case RelativeLayoutParameter::RelativeAlign::LOCATION_BELOW_LEFTALIGN:
if (relativeWidget)
{
if (_relativeWidgetLP && !_relativeWidgetLP->_put)
{
return false;
}
float locationBottom = relativeWidget->getBottomBoundary();
float locationLeft = relativeWidget->getLeftBoundary();
_finalPositionY = locationBottom - (1.0f - ap.y) * cs.height;
_finalPositionX = locationLeft + ap.x * cs.width;
}
break;
//下方居中
case RelativeLayoutParameter::RelativeAlign::LOCATION_BELOW_CENTER:
if (relativeWidget)
{
if (_relativeWidgetLP && !_relativeWidgetLP->_put)
{
return false;
}
Size rbs = relativeWidget->getContentSize();
float locationBottom = relativeWidget->getBottomBoundary();
_finalPositionY = locationBottom - (1.0f - ap.y) * cs.height;
_finalPositionX = relativeWidget->getLeftBoundary() + rbs.width * 0.5f + ap.x * cs.width - cs.width * 0.5f;
}
break;
//下方 右侧对齐
case RelativeLayoutParameter::RelativeAlign::LOCATION_BELOW_RIGHTALIGN:
if (relativeWidget)
{
if (_relativeWidgetLP && !_relativeWidgetLP->_put)
{
return false;
}
float locationBottom = relativeWidget->getBottomBoundary();
float locationRight = relativeWidget->getRightBoundary();
_finalPositionY = locationBottom - (1.0f - ap.y) * cs.height;
_finalPositionX = locationRight - (1.0f - ap.x) * cs.width;
}
break;
default:
break;
}
return true;
}
//看情况 处理边距
void RelativeLayoutManager::caculateFinalPositionWithRelativeAlign()
{
RelativeLayoutParameter* layoutParameter = dynamic_cast<RelativeLayoutParameter*>(_widget->getLayoutParameter());
Margin mg = layoutParameter->getMargin();
RelativeLayoutParameter::RelativeAlign align = layoutParameter->getAlign();
//handle margin
switch (align)
{
case RelativeLayoutParameter::RelativeAlign::NONE:
case RelativeLayoutParameter::RelativeAlign::PARENT_TOP_LEFT:
_finalPositionX += mg.left;
_finalPositionY -= mg.top;
break;
case RelativeLayoutParameter::RelativeAlign::PARENT_TOP_CENTER_HORIZONTAL:
_finalPositionY -= mg.top;
break;
case RelativeLayoutParameter::RelativeAlign::PARENT_TOP_RIGHT:
_finalPositionX -= mg.right;
_finalPositionY -= mg.top;
break;
case RelativeLayoutParameter::RelativeAlign::PARENT_LEFT_CENTER_VERTICAL:
_finalPositionX += mg.left;
break;
case RelativeLayoutParameter::RelativeAlign::CENTER_IN_PARENT:
break;
case RelativeLayoutParameter::RelativeAlign::PARENT_RIGHT_CENTER_VERTICAL:
_finalPositionX -= mg.right;
break;
case RelativeLayoutParameter::RelativeAlign::PARENT_LEFT_BOTTOM:
_finalPositionX += mg.left;
_finalPositionY += mg.bottom;
break;
case RelativeLayoutParameter::RelativeAlign::PARENT_BOTTOM_CENTER_HORIZONTAL:
_finalPositionY += mg.bottom;
break;
case RelativeLayoutParameter::RelativeAlign::PARENT_RIGHT_BOTTOM:
_finalPositionX -= mg.right;
_finalPositionY += mg.bottom;
break;
case RelativeLayoutParameter::RelativeAlign::LOCATION_ABOVE_LEFTALIGN:
_finalPositionY += mg.bottom;
_finalPositionX += mg.left;
break;
case RelativeLayoutParameter::RelativeAlign::LOCATION_ABOVE_RIGHTALIGN:
_finalPositionY += mg.bottom;
_finalPositionX -= mg.right;
break;
case RelativeLayoutParameter::RelativeAlign::LOCATION_ABOVE_CENTER:
_finalPositionY += mg.bottom;
break;
case RelativeLayoutParameter::RelativeAlign::LOCATION_LEFT_OF_TOPALIGN:
_finalPositionX -= mg.right;
_finalPositionY -= mg.top;
break;
case RelativeLayoutParameter::RelativeAlign::LOCATION_LEFT_OF_BOTTOMALIGN:
_finalPositionX -= mg.right;
_finalPositionY += mg.bottom;
break;
case RelativeLayoutParameter::RelativeAlign::LOCATION_LEFT_OF_CENTER:
_finalPositionX -= mg.right;
break;
case RelativeLayoutParameter::RelativeAlign::LOCATION_RIGHT_OF_TOPALIGN:
_finalPositionX += mg.left;
_finalPositionY -= mg.top;
break;
case RelativeLayoutParameter::RelativeAlign::LOCATION_RIGHT_OF_BOTTOMALIGN:
_finalPositionX += mg.left;
_finalPositionY += mg.bottom;
break;
case RelativeLayoutParameter::RelativeAlign::LOCATION_RIGHT_OF_CENTER:
_finalPositionX += mg.left;
break;
case RelativeLayoutParameter::RelativeAlign::LOCATION_BELOW_LEFTALIGN:
_finalPositionY -= mg.top;
_finalPositionX += mg.left;
break;
case RelativeLayoutParameter::RelativeAlign::LOCATION_BELOW_RIGHTALIGN:
_finalPositionY -= mg.top;
_finalPositionX -= mg.right;
break;
case RelativeLayoutParameter::RelativeAlign::LOCATION_BELOW_CENTER:
_finalPositionY -= mg.top;
break;
default:
break;
}
}
最后
下一篇分析 具体 UI控件 的实现。
本文深入剖析了cocos2d-x 3.3版本UI系统的核心原理,重点关注了UI组件的实现细节,为读者揭示了UI系统的工作机制。文章详述了部分关键源码,为后续分析具体UI控件的实现奠定了基础。
&spm=1001.2101.3001.5002&articleId=90452129&d=1&t=3&u=89584346bcb442688f210fd3a4ff07d1)
469

被折叠的 条评论
为什么被折叠?



