☰
【xilem基础语法学与练】第6课:水平布局与样式设置
2026/10/12 4:43:07 网站建设 项目流程

引语:Xilem 0.4 的 API 现状

在开始本课之前,需要坦诚地说明一件事。

Xilem 0.4 目前仍处于 Alpha 阶段。从官方文档(docs.rs)和源码来看,其公开 API 与很多教程中描述的有所不同。本课基于官方文档中确认存在的 API 来编写。

关于布局 API:官方文档中使用的是 flex(Axis::Vertical, …) 和 flex(Axis::Horizontal, …),以及简写形式 flex_col(…) 和 row(…)。

关于样式 API:Xilem 0.4 的 View 层面尚未提供完善的样式修饰符链式 API(如 .text_color()、.font_size()、.padding() 等)。样式控制目前主要通过底层 Masonry widget 的属性来实现,在 View 层的直接支持有限。这是 Alpha 阶段的已知局限,后续版本可能会补充。

因此,本课的重点放在水平布局和布局组合技巧上,样式部分会如实说明当前能做什么、不能做什么。

一、水平布局:flex(Axis::Horizontal, …)

前面学习了 flex(Axis::Vertical, …)(垂直布局)。本课学习它的"兄弟"——水平布局。

基本语法:

use xilem::view::{Axis, flex, label, text_button};

// 水平排列三个元素
flex(Axis::Horizontal, (
label(“左”),
label(“中”),
label(“右”),
))

flex 的第一个参数决定排列方向:

  • Axis::Vertical:从上到下排列(已学)
  • Axis::Horizontal:从左到右排列(本课重点)

子元素仍然用元组 () 包裹,语法和垂直布局完全一致。

二、水平布局实战:计数器

一个典型的计数器界面——按钮和数字水平排列:

usexilem::view::{Axis,flex,label,text_button};usexilem::{EventLoop,WindowOptions,WidgetView,Xilem};#[derive(Default)]structCounter{num:i32,}fnapp_logic(data:&mutCounter)->implWidgetView<Counter>+use<>{flex(Axis::Horizontal,(text_button("-1",|data:&mutCounter|data.num-=1),label(format!(" {} ",data.num)),text_button("+1",|data:&mutCounter|data.num+=1),))}fnmain()->Result<(),winit::error::EventLoopError>{letapp=Xilem::new_simple(Counter::default(),app_logic,WindowOptions::new("水平计数器"),);app.run_in(EventLoop::with_user_event())?;Ok(())}

运行效果:
窗口中水平排列着 [-1] [ 0 ] [+1],点击按钮数字变化。

三、嵌套布局:垂直 + 水平组合

实际界面几乎不会只用一种方向。嵌套是构建复杂布局的核心技巧。

目标界面:

┌──────────────────────────┐
│ 标题:我的计数器 │ ← 水平行
│ │
│ [-1] 42 [+1] │ ← 水平行
│ │
│ [归零] │ ← 水平行
└──────────────────────────┘

代码:

fnapp_logic(data:&mutCounter)->implWidgetView<Counter>+use<>{// 外层:垂直布局(三行)flex(Axis::Vertical,(// 第一行:标题label("标题:我的计数器"),// 第二行:按钮 + 数字 + 按钮(水平排列)flex(Axis::Horizontal,(text_button("-1",|data:&mutCounter|data.num-=1),label(format!(" {} ",data.num)),text_button("+1",|data:&mutCounter|data.num+=1),)),// 第三行:归零按钮text_button("归零",|data:&mutCounter|data.num=0),))}

嵌套规则很简单:

  • 外层 flex(Axis::Vertical, …) 决定整体从上到下排列
  • 内层 flex(Axis::Horizontal, …) 决定某一行内部从左到右排列
  • 可以无限嵌套——水平里套垂直,垂直里套水平

四、简写形式:flex_col 和 row

Xilem 提供了两个快捷函数,减少打字量:

  • flex_col((…)) 等价于 flex(Axis::Vertical, (…))
  • row((…)) 等价于 flex(Axis::Horizontal, (…))

用简写重写上面的计数器:

usexilem::view::{flex_col,row,label,text_button};fnapp_logic(data:&mutCounter)->implWidgetView<Counter>+use<>{flex_col((label("标题:我的计数器"),row((text_button("-1",|data:&mutCounter|data.num-=1),label(format!(" {} ",data.num)),text_button("+1",|data:&mutCounter|data.num+=1),)),text_button("归零",|data:&mutCounter|data.num=0),))}

代码更短,含义完全相同。后续示例中会交替使用两种写法,帮助你熟悉它们。

五、布局嵌套的思维方法

很多初学者面对复杂界面时会"不知道从哪里开始拆"。这里给一个实用的思维方法:从外到内,逐层拆分。

以这个界面为例:

┌────────────────────────────┐
│ 🛒 购物车 │ ← 第1行
│ ───────────────────────── │
│ 商品A ¥99 [-] 2 [+] │ ← 第2行(水平)
│ 商品B ¥399 [-] 1 [+] │ ← 第3行(水平)
│ ───────────────────────── │
│ 总价: ¥498 │ ← 第4行
└────────────────────────────┘

拆分步骤:

  1. 最外层:整体是上到下排列 → flex_col((…))
  2. 第1行:一行文字 → label(“🛒 购物车”)
  3. 第2行:左到右排列 → row((label(…), label(…), text_button(…), label(…), text_button(…)))
  4. 第3行:和第2行结构相同 → 同样用 row(…)
  5. 第4行:一行文字 → label(…)
flex_col((label("🛒 购物车"),row((label("商品A"),label("¥99"),text_button("-",|_|{}),label("2"),text_button("+",|_|{}),)),row((label("商品B"),label("¥399"),text_button("-",|_|{}),label("1"),text_button("+",|_|{}),)),label("总价: ¥498"),))

口诀:先判断整体方向,再逐行判断内部方向

六、样式设置的现状说明

这是很多从 Web 或其他 GUI 框架转过来的开发者最关心的部分。需要如实说明当前情况:

Xilem 0.4(Alpha 阶段)的 View 层面,尚未提供完善的链式样式修饰符 API。

具体来说:

目前能做的:

  • 文本内容通过 label(format!(…)) 动态生成
  • 按钮文本通过 text_button(“文本”, callback) 设置
  • 布局方向通过 Axis::Vertical / Axis::Horizontal 控制
  • 组件间距由 Masonry 布局引擎自动管理

目前 View 层不直接支持的:

  • 文字颜色设置(如 .text_color(Color::RED))
  • 字号设置(如 .font_size(24.0))
  • 背景颜色设置
  • 内边距 / 外边距精确控制
  • 边框样式

这些样式能力在底层 Masonry widget 中存在,但在 Xilem 的 View 层 API 中尚未充分暴露。这是 Alpha 阶段的已知局限。

如果你确实需要样式控制,目前的做法是:

  • 通过 format! 在文本内容中嵌入视觉提示(如 emoji、特殊符号)
  • 等待后续版本完善样式 API
  • 直接使用 Masonry 层的 widget API(超出本教程范围)

七、综合示例:带状态的水平布局

把水平布局和状态管理结合起来,做一个"温度转换器":

usexilem::view::{Axis,flex,label,text_button};usexilem::{EventLoop,WindowOptions,WidgetView,Xilem};structTempConverter{celsius:f64,}implDefaultforTempConverter{fndefault()->Self{TempConverter{celsius:0.0}}}fnapp_logic(data:&mutTempConverter)->implWidgetView<TempConverter>+use<>{letfahrenheit=data.celsius*9.0/5.0+32.0;flex(Axis::Vertical,(label("温度转换器"),// 摄氏度控制(水平行)flex(Axis::Horizontal,(text_button("-1°C",|data:&mutTempConverter|data.celsius-=1.0),label(format!(" {:.1}°C ",data.celsius)),text_button("+1°C",|data:&mutTempConverter|data.celsius+=1.0),)),// 华氏度显示(只读)label(format!("= {:.1}°F",fahrenheit)),))}fnmain()->Result<(),winit::error::EventLoopError>{letapp=Xilem::new_simple(TempConverter::default(),app_logic,WindowOptions::new("温度转换器"),);app.run_in(EventLoop::with_user_event())?;Ok(())}

这个例子展示了:

  • 垂直布局包裹整体结构
  • 水平布局排列按钮和文本
  • 状态变化时,华氏度自动重新计算并显示

八、练习题

请尝试独立完成以下练习。

练习 1:水平排列三个按钮

  • 要求:窗口中水平排列三个按钮,文本分别为"苹果"、“香蕉”、“橘子”。点击任意按钮,在下方显示"你选了:xxx"。窗口大小 500×200。
  • 提示:外层 flex(Axis::Vertical, …),第一行用 flex(Axis::Horizontal, …) 放三个按钮,第二行用 label 显示选择结果。状态用一个 String 字段。

练习 2:加减乘除四则运算器

  • 要求:显示一个数字(初始值 10),下方水平排列四个按钮:+5、-3、×2、÷2。点击对应按钮执行运算并更新显示。窗口 400×250。
  • 提示:数字类型用 f64。÷2 按钮的闭包中写 data.num /= 2.0。四个按钮放在 flex(Axis::Horizontal, …) 中。

练习 3:嵌套布局——简易名片

  • 要求:用嵌套布局模拟一张名片:
    ┌────────────────────────────┐
    │ 姓名:张三 │
    │ 水平排列:电话 | 邮箱 │
    │ 水平排列:公司 | 职位 │
    └────────────────────────────┘

不需要交互,纯静态展示即可。窗口 400×200。

  • 提示:外层 flex(Axis::Vertical, …),内部两行各用 flex(Axis::Horizontal, …)。

练习 4:思考题

下面这段代码有什么问题?

flex(Axis::Horizontal,(label("A"),flex(Axis::Horizontal,(label("B"),label("C"),)),label("D"),))

问:最终界面上 A、B、C、D 四个文字的排列顺序是什么?是水平一排还是有多行?

九、练习题答案

练习 1 答案:

usexilem::view::{Axis,flex,label,text_button};usexilem::{EventLoop,WindowOptions,WidgetView,Xilem};structFruitPicker{selected:String,}implDefaultforFruitPicker{fndefault()->Self{FruitPicker{selected:"未选择".to_string(),}}}fnapp_logic(data:&mutFruitPicker)->implWidgetView<FruitPicker>+use<>{flex(Axis::Vertical,(flex(Axis::Horizontal,(text_button("苹果",|data:&mutFruitPicker|{data.selected="苹果".to_string();}),text_button("香蕉",|data:&mutFruitPicker|{data.selected="香蕉".to_string();}),text_button("橘子",|data:&mutFruitPicker|{data.selected="橘子".to_string();}),)),label(format!("你选了:{}",data.selected)),))}fnmain()->Result<(),winit::error::EventLoopError>{letapp=Xilem::new_simple(FruitPicker::default(),app_logic,WindowOptions::new("水果选择"),);app.run_in(EventLoop::with_user_event())?;Ok(())}

练习 2 答案:

usexilem::view::{Axis,flex,label,text_button};usexilem::{EventLoop,WindowOptions,WidgetView,Xilem};structCalculator{num:f64,}implDefaultforCalculator{fndefault()->Self{Calculator{num:10.0}}}fnapp_logic(data:&mutCalculator)->implWidgetView<Calculator>+use<>{flex(Axis::Vertical,(label(format!("当前值:{:.2}",data.num)),flex(Axis::Horizontal,(text_button("+5",|data:&mutCalculator|data.num+=5.0),text_button("-3",|data:&mutCalculator|data.num-=3.0),text_button("×2",|data:&mutCalculator|data.num*=2.0),text_button("÷2",|data:&mutCalculator|data.num/=2.0),)),))}fnmain()->Result<(),winit::error::EventLoopError>{letapp=Xilem::new_simple(Calculator::default(),app_logic,WindowOptions::new("四则运算器"),);app.run_in(EventLoop::with_user_event())?;Ok(())}

练习 3 答案:

usexilem::view::{Axis,flex,label};usexilem::{EventLoop,WindowOptions,WidgetView,Xilem};fnapp_logic(_data:&mut())->implWidgetView<()>+use<>{flex(Axis::Vertical,(label("姓名:张三"),flex(Axis::Horizontal,(label("电话:138xxxx"),label(" | "),label("邮箱:zhangsan@example.com"),)),flex(Axis::Horizontal,(label("公司:Rust科技有限公司"),label(" | "),label("职位:高级开发工程师"),)),))}fnmain()->Result<(),winit::error::EventLoopError>{letapp=Xilem::new_simple((),app_logic,WindowOptions::new("名片"),);app.run_in(EventLoop::with_user_event())?;Ok(())}

练习 4 答案:

最终界面上 A、B、C、D 全部在同一行水平排列,顺序是从左到右:A、B、C、D。

原因:
外层 flex(Axis::Horizontal, …) 把三个子元素水平排列——label(“A”)、内层 flex(Axis::Horizontal, …)、label(“D”)。内层的 flex(Axis::Horizontal, …) 本身又是一个水平容器,把 B 和 C 水平排列。所以最终效果是:A 在左边,中间是 B 和 C(水平排列),D 在右边。整体仍然是一行。

嵌套同方向的 flex 不会换行——水平套水平,结果还是水平一排。只有嵌套不同方向的 flex 才会产生多行/多列效果。

十、本课知识点总结

水平布局:
flex(Axis::Horizontal, (child1, child2, …)) 将子元素从左到右排列。简写形式 row((…)) 效果相同。

嵌套布局:
外层决定整体方向,内层决定局部方向。垂直套水平 = 多行,每行内部水平排列。水平套垂直 = 多列,每列内部垂直排列。同方向嵌套不会换行。

布局拆分方法:
面对复杂界面,先判断整体方向(垂直 or 水平),再逐行/逐列判断内部方向。从外到内,逐层拆分。

简写函数:
flex_col(…) = flex(Axis::Vertical, …),row(…) = flex(Axis::Horizontal, …)。

样式现状:
Xilem 0.4 Alpha 阶段的 View 层尚未提供完善的链式样式 API(文字颜色、字号、内边距等)。这是框架发展阶段的已知局限,后续版本预计会补充。当前如需样式控制,可关注 Masonry 底层 widget 的属性,或等待 View 层 API 完善。

下一课可以进入条件渲染(one_of)或动态列表(for_each)

需要专业的网站建设服务?

联系我们获取免费的网站建设咨询和方案报价,让我们帮助您实现业务目标

立即咨询