对应源码位置: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 =