这里选取少量的重点部分。
//找当前节点的下一个可以聚焦的节点 //主要看思想 已经删了很多了 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; } } } }很长,但很有意思。
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控件 的实现。