Lynx Linear Layout 详解默认布局系统的方向、权重分配、对齐与 Web 迁移差异【免费下载链接】lynxEmpower the Web community and invite more to build across platforms.项目地址: https://gitcode.com/GitHub_Trending/lynx10/lynx本篇技术文章基于 Lynx 官方 API 文档linear-layout.md整理并扩充系统讲解 Lynx 默认布局系统 Linear Layout 的完整用法主轴方向linear-direction、剩余空间分配linear-weight、Web 风格对齐属性justify-content/align-items/align-self以及与 Web 块级布局的差异和迁移注意事项。读完本文你不仅能直接使用 Linear Layout 完成各类简单列表与分栏页面还能从源码层面理解它的三阶段布局算法、拉伸stretch规则与空间分配边界避免从 Web 迁移到 Lynx 时最常见的布局坑。1. Linear Layout 是什么Linear Layout 是 Lynx 的默认布局系统设计理念类似于 Android 的LinearLayout当你不写display属性时Lynx 自动使用 Linear Layout。它适用于子元素只需顺序排列、不需要 Flexbox 或 Grid 那种复杂尺寸与定位能力的场景。与 Flexbox 一样Linear Layout 也使用主轴main axis和交叉轴cross axis模型主轴子元素排列的方向交叉轴与主轴垂直的方向。从源码结构看这个默认不是文档约定而是引擎行为。在布局对象的测量入口 layout_object.cc 中引擎根据解析出的display类型创建对应布局算法if (type DisplayType::kFlex) { algorithm_ new FlexLayoutAlgorithm(this); } else if (type DisplayType::kLinear) { if (attr_map_.getColumnCount().has_value()) { algorithm_ new StaggeredGridLayoutAlgorithm(this); } else { algorithm_ new LinearLayoutAlgorithm(this); } } else if (type DisplayType::kRelative) { ... } else if (type DisplayType::kGrid) { ... }这里还能看到一个细节当display: linear的元素带有columnCount属性时会改用StaggeredGridLayoutAlgorithm瀑布流布局这正是waterfall类组件的底层基础。Linear Layout 的布局数据集中保存在 linear_data.h 的LinearData结构体中包含linear_weight_sum_、linear_weight_、linear_orientation_、linear_layout_gravity_、linear_gravity_、linear_cross_gravity_等字段——这套字段命名与下文兼容属性一节一一对应。2. 基本用法子元素默认纵向column排列无需任何display声明// 子元素默认纵向排列 view classNamecontainer textItem 1/text textItem 2/text textItem 3/text /view.container { /* 无需声明 displaylinear 即默认 */ padding: 16px; } .explicit-container { display: linear; /* 也可以显式声明 */ }默认方向的证据在 default_layout_style.hstatic constexpr LinearOrientationType SL_DEFAULT_LINEAR_ORIENTATION LinearOrientationType::kVertical; // 即 column 方向 static constexpr float SL_DEFAULT_LINEAR_WEIGHT_SUM 0.0f; static constexpr float SL_DEFAULT_LINEAR_WEIGHT 0.0f;即默认主轴为纵向linear-weight与linear-weight-sum的默认值都是 0权重分配默认关闭由引擎自动计算。3. 主轴方向linear-direction使用linear-direction设置主轴方向默认值为column纵向语义上类似 Flexbox 的flex-direction.row { display: linear; linear-direction: row; } .column { display: linear; linear-direction: column; /* 默认 */ }linear-direction全部取值取值说明row水平排列子项column垂直排列子项默认row-reverse水平反向排列column-reverse垂直反向排列view classNamerow textLeft/text textCenter/text textRight/text /view源码层面linear-direction与旧属性linear-orientation共用同一个枚举类型。在样式解析入口 computed_css_style.cc 中bool ComputedCSSStyle::SetLinearOrientation(const tasm::CSSValue value, ...) { return CSSStyleUtils::ComputeEnumStyleLinearOrientationType( linear-orientation must be an enum!, parser_configs_); } bool ComputedCSSStyle::SetLinearDirection(const tasm::CSSValue value, ...) { return CSSStyleUtils::ComputeEnumStyleLinearOrientationType( linear-direction must be an enum!, parser_configs_); }两者最终都计算为LinearOrientationType这正是文档建议新代码优先使用linear-direction的底层原因二者完全等价只是新属性名与 Web 习惯flex-direction更一致。4. 权重分配linear-weightlinear-weight用于把主轴上的剩余空间按比例分给子元素取值为无单位的比例数字.container { display: linear; linear-direction: row; } .sidebar { width: 200px; /* 固定宽度 */ } .main { linear-weight: 1; } .aside { linear-weight: 0.5; }view classNamecontainer view classNamesidebartextSidebar/text/view view classNamemaintextMain Content/text/view view classNameasidetextAside/text/view /view在这个例子中固定宽度的sidebar先消耗主轴空间main和aside再按1 : 0.5的比例瓜分剩余空间。源码解析权重如何参与布局权重分配发生在布局算法的第一阶段。linear_layout_algorithm.cc 中DetermineItemSize()Algorithm-1的逻辑是先测量非权重子项遍历所有 in-flow 子项linear-weight 0的子项以及容器主轴尺寸不确定时直接正常测量记录基础尺寸bases[idx]再布局权重子项当容器主轴约束是 definite 模式且权重启用时调用LayoutWeightedChildren(bases)累加总尺寸最后把每个子项的主轴尺寸含前后 margin累加到total_main_size_交叉轴取最大值。LayoutWeightedChildren内部复用了与 Flex 相同的弹性尺寸工具ElasticLayoutUtils::ComputeElasticItemSizes关键代码如下void LinearLayoutAlgorithm::LayoutWeightedChildren( const InlineFloatArray base_sizes) { const float weight_sum container_style_-GetLinearWeightSum(); ... if (base::FloatsLarger(weight_sum, 0.f)) { infos.total_elastic_factor_override_ weight_sum; } const auto weight_getter [](const LayoutObject item) { return item.GetCSSStyle()-GetLinearWeight(); }; ElasticLayoutUtils::ComputeElasticItemSizes( infos, container_constraints_[MainAxis()].Size(), weight_getter, main_size_); }由这段实现可以推断出两个实际行为linear-weight-sum的作用若显式设置了linear-weight-sum且大于 0它会被强制作为弹性因子总和total_elastic_factor_override_否则由引擎根据各子项的linear-weight自动求和——与文档中linear-weight-sum 通常让引擎自动计算的建议一致容器主轴尺寸必须确定只有IsSLDefiniteMode(container_constraints_[kMainAxis].Mode())成立时才会执行权重布局否则权重子项按普通尺寸测量。也就是说在高度自动撑开的容器里给子项设linear-weight是不生效的Quirks 模式差异IsFullQuirksMode()下权重是否启用取决于linear-weight-sum 0现代模式下默认启用。另一个值得注意的机制在 linear_layout_algorithm.cc 的UpdateChildSizeInternalAlgorithm-3中// The main axis container of linear layout is always indefinite if child // main size is not specified as a definite value. if (child_constraints[MainAxis()].Mode() SLMeasureModeAtMost) { child_constraints[MainAxis()] OneSideConstraint::Indefinite(); }注释直白地说明了 Linear Layout 的核心尺寸规则子项主轴尺寸若未指定确定值约束就是 indefinite尺寸完全由内容决定不会自动填满容器。这一条是理解第 8 节与 Web 块级布局差异的关键。5. 对齐justify-content / align-items / align-selfLinear Layout 支持 Web 风格的对齐属性/* 主轴居中 */ .main-axis-center { display: linear; linear-direction: column; justify-content: center; } /* 交叉轴居中 */ .cross-axis-center { display: linear; linear-direction: row; align-items: center; } /* 对单个子项覆盖交叉轴对齐 */ .cross-axis-center .first-item { align-self: end; }注意当父容器的交叉轴尺寸确定例如linear-direction: column时指定了width而子项在该轴上没有指定尺寸或为auto时子项会沿交叉轴拉伸填满父容器。源码解析交叉轴自动拉伸上述注对应 linear_layout_algorithm.cc 中的这段逻辑if (used_container_constraints[CrossAxis()].Mode() SLMeasureModeDefinite) { if (((!IsSLDefiniteMode(child_constraints[CrossAxis()].Mode()) GetComputedLinearLayoutGravity(*child_style) LinearLayoutGravityType::kNone) !logic_direction_utils::GetCSSDimensionSize(child_style, CrossAxis()) .IsIntrinsic()) || IsLayoutGravityFill(GetComputedLinearLayoutGravity(*child_style))) { float stretched_size used_container_constraints[CrossAxis()].Size() - margin[CrossFront()] - margin[CrossBack()]; child_constraints[CrossAxis()] OneSideConstraint::Definite(stretched_size); } }翻译成白话当容器交叉轴尺寸确定且子项交叉轴尺寸不确定或显式要求 fill/stretch时子项被强制设定为容器交叉轴尺寸减去前后 margin。这就是列方向下子项宽度默认撑满父容器的实现。注意拉伸尺寸是减去交叉轴 margin 的与文档第 8 节child width equals the parents content-box width minus horizontal margins的表述严格一致。CrossAxisAlignmentlinear_layout_algorithm.cc则处理非拉伸情况下的偏移default起始偏移为 0after/end对齐时偏移为容器交叉轴内容尺寸减去子项 margin 盒尺寸center时取两者差的一半。此外交叉轴上的margin: auto优先级高于align-self会先按 auto margin 规则解析位置。源码解析justify-content 只有四种真正分支主轴对齐由AlignInFlowItems()linear_layout_algorithm.cc完成if (gravity LinearGravityType::kSpaceBetween) { if (item_count 1) avg_offset 0; else avg_offset remaining_size_ / (item_count - 1); } else if (IsGravityAfter(gravity)) { main_offset ... - total_main_size_; } else if (IsGravityCenter(gravity)) { main_offset (... - total_main_size_) / 2.f; }从源码结构看引擎只实现了space-between均分remaining_size_到item_count - 1个间隙、end整体后置和center整体居中三类分支其余取值走start默认分支。这直接印证了文档的重要提示space-around和space-evenly虽然在 CSS API 中存在但在 Linear Layout 中表现得和start一样需要真正的间隔均分请使用 Flexbox。remaining_size_的计算在AfterResultBorderBoxSize()中即容器主轴尺寸减去total_main_size_且负值被钳制为 0——子项总尺寸超出不产生负偏移。6. 属性参考linear-direction控制布局方向取值说明row水平排列子项column垂直排列子项默认row-reverse水平反向排列column-reverse垂直反向排列justify-content容器属性控制子项沿主轴的对齐取值说明start对齐到主轴起点end对齐到主轴终点flex-start对齐到主轴起点center沿主轴居中flex-end对齐到主轴终点space-between首尾子项分别贴两端中间子项间均匀分布剩余空间注意space-around和space-evenly存在于 CSS API 中但在 Linear Layout 中表现同start。需要真正的间隔均分请使用 Flexbox。align-items容器属性设置所有子项的默认交叉轴对齐取值说明center交叉轴居中start对齐到交叉轴起点end对齐到交叉轴终点flex-start对齐到交叉轴起点flex-end对齐到交叉轴终点注意Linear Layout 中align-items只控制对齐不支持stretch和auto。子项是否填满父容器交叉轴取决于父容器交叉轴尺寸是否确定见第 5 节的拉伸规则。baseline目前仅 Flexbox 支持Linear Layout 不可用。align-self子项属性控制单个子项的交叉轴对齐覆盖容器的align-items取值说明auto使用父容器的align-items值center交叉轴居中start对齐到交叉轴起点end对齐到交叉轴终点flex-start对齐到交叉轴起点flex-end对齐到交叉轴终点注意baseline与stretch目前仅 Flexbox 支持不能作为 Linear Layout 子项的align-self取值。linear-weight数值表示子项分得主轴剩余空间的比例份额.item { linear-weight: 1; /* 占剩余主轴空间的一份 */ }兼容属性Legacy老代码可能使用以下 Linear 专属属性维护既有页面时可以保留新示例建议优先使用linear-direction与 Web 对齐属性旧属性推荐替代linear-orientationlinear-directionlinear-gravityjustify-contentlinear-layout-gravityalign-selflinear-cross-gravityalign-itemslinear-weight-sum通常让引擎自动计算这些旧属性在引擎中仍然完整保留computed_css_style.cc 提供了SetLinearWeightSum、SetLinearWeight、SetLinearOrientation、SetLinearGravity、SetLinearCrossGravity、SetLinearLayoutGravity等解析函数css_decoder.cc 则把linear-orientation、linear-gravity、linear-layout-gravity、linear-cross-gravity、linear-direction等属性 ID 逐一映射到对应枚举其中linear-direction与linear-orientation如前所述映射到同一枚举。7. Linear Layout vs. Flexbox能力Linear LayoutFlexbox性能简单布局下更快略慢复杂尺寸不处理复杂的收缩shrink场景支持 flex 收缩换行不支持支持 wrap多行对齐不支持支持align-content子项排序可用order支持order最适合场景简单列表复杂弹性布局建议简单列表用 Linear Layout复杂布局用 Flexbox。从源码结构看二者共用LayoutAlgorithm基类体系并各自独立实现linear_layout_algorithm.h 对比 Flex 算法Linear Layout 只有测量 → 容器定尺寸 → 对齐三步对应头文件中注释的 Algorithm-1/2/3没有 wrap 与多行分配逻辑这正是其更轻量的原因。8. 与 Web 块级布局的差异迁移注意事项这是从 Web 迁移到 Lynx 时最常见的坑之一。默认布局行为对照行为Web 块级布局Lynx Linear Layout默认默认displayblock普通流linear默认linear-direction: column子项宽度width: auto使用父容器可用宽度默认column方向下宽度是交叉轴尺寸未指定时填满父容器交叉轴子项高度由内容决定默认column方向下高度是主轴尺寸由内容、linear-weight或显式尺寸决定嵌套元素尺寸宽度用每层父容器的可用宽度高度由内容决定交叉轴尺寸可跟随父容器但主轴尺寸不自动填满空元素尺寸视overflow与min-height可能为 0主轴尺寸未指定且无内容时通常为 0子项尺寸示例/* Web: width: auto 使用父容器可用宽度 */ .parent { width: 200px; } .child { /* width 默认 auto使用父容器可用宽度 */ } /* Lynx: 未指定的交叉轴尺寸填满父容器交叉轴 */ .parent { width: 200px; display: linear; linear-direction: column; } .child { /* width 默认 auto等于父容器内容区宽度减去水平 margin */ } /* Lynx: 主轴尺寸不自动填满 */ .parent { height: 200px; display: linear; linear-direction: column; } .child { /* 未指定高度时高度仍由内容决定不会自动变成 200px */ }第二段差异的直接证据就是第 4 节引用的UpdateChildSizeInternal源码注释主轴未指定确定尺寸时约束为 indefinite内容撑多高就是多高。嵌套元素尺寸示例/* Web: 嵌套 div 自动使用父容器可用宽度 */ .grandparent { width: 100px; height: 100px; } .parent { /* width 默认 auto解析为 100px高度仍由内容决定 */ } .child { /* width 默认 auto跟随父容器可用宽度高度仍由内容决定 */ } /* Lynx: 交叉轴宽度跟随父容器但主轴高度不自动填满 */ .grandparent { width: 100px; height: 100px; display: linear; linear-direction: column; } .parent { /* 未设 width 则为 100px 宽未设 height 且无内容时高度可能为 0 */ } .child { /* 未设 width 跟随父容器未设 height 且无内容时高度可能为 0 */ } /* 当元素必须填满两个维度时显式指定主轴尺寸 */ .parent { width: 100%; height: 100%; } .child { width: 100%; height: 100%; }迁移口诀交叉轴column 方向的 width自动撑满这一点 Web 和 Lynx 表现一致主轴尺寸column 方向的 height永不自动撑满需要占满就必须显式写height: 100%或使用linear-weight。9. 常用布局模式纵向列表.list { display: linear; /* linear-direction: column 是默认值 */ gap: 12px; padding: 16px; } .list-item { padding: 12px; background-color: #f5f5f5; border-radius: 8px; }横向按钮组等分剩余空间.button-group { display: linear; linear-direction: row; gap: 8px; padding: 16px; } .button-group .button { linear-weight: 1; /* 等分剩余主轴空间 */ }侧边栏 主内容区.layout { display: linear; linear-direction: row; height: 100vh; } .sidebar { width: 250px; } .main { linear-weight: 1; /* 占据剩余主轴空间 */ }注意这个模式依赖第 4 节的前提.layout的主轴宽度方向尺寸必须确定由100vh的视口高度保证交叉轴确定宽度通常来自屏幕宽度这一确定约束linear-weight: 1才能生效。交叉轴居中的工具栏.toolbar { display: linear; linear-direction: row; align-items: center; }10. 小结与延伸阅读Linear Layout 是 Lynx 面向简单顺序布局的轻量方案核心要点回顾零配置默认不写display即 Linear Layout默认column方向默认值定义见 default_layout_style.hlinear-direction四方向row / column / row-reverse / column-reverse与旧属性linear-orientation同枚举等价linear-weight分剩余空间仅在容器主轴尺寸确定时生效linear-weight-sum一般交给引擎自动求和对齐用 Web 属性justify-content、align-items、align-self但space-around/space-evenly退化为startbaseline/stretch不支持迁移核心差异主轴尺寸不自动填满列方向下跨轴自动撑满减去 margin空元素主轴尺寸通常归零。相关文档与源码入口同系列布局文档flex-layout.md、grid-layout.md、relative-layout.md迁移指南migration-guide.md、css-differences.md核心算法实现linear_layout_algorithm.h、linear_layout_algorithm.ccdisplay 到布局算法的分发layout_object.cc样式解析与属性映射computed_css_style.cc、css_decoder.cc【免费下载链接】lynxEmpower the Web community and invite more to build across platforms.项目地址: https://gitcode.com/GitHub_Trending/lynx10/lynx创作声明:本文部分内容由AI辅助生成(AIGC),仅供参考
