前言
文档参考:https://docs.rs/xilem/latest/xilem/view/fn.button.html
版本:Xilem 0.4
一、button基础概念
button 是通用可自定义内容按钮组件,按钮内部可以放置任意非交互式视图:label、flex、sized_box、z_stack等布局组合都可以作为按钮内容。
- text_button = button(label(文本), 回调) 的简写,只能放纯文本。
- button 自由度更高,支持图标+文字、多行、复杂排版的按钮。
- 交互:鼠标左键点击触发回调;获得键盘焦点后,按 Space空格 / Enter回车 同样触发点击,键盘无障碍支持内置,不可修改快捷键。
- 如果需要监听鼠标中键、右键、触摸事件,请使用 button_any_pointer 。
重要限制:
button内部禁止嵌套另一个button、text_button这类交互式控件,会产生鼠标焦点抢占、交互异常问题;内部只能放纯展示布局原语和label。
引入头
use xilem::view::{button, text_button, label, flex_col, flex_row, sized_box};
use xilem::{FontWeight, palette};
二、函数签名
pub fn button<State, Action, V: WidgetView<State, Action>>(
child: V,
callback: impl Fn(&mut State) -> Action + Send + 'static,
) -> Button<impl Fn(&mut State, Option<PointerButton>) -> MessageResult<Action> + Send + 'static, V>
- child:按钮内部子视图,只能是非交互视图(label / flex / sized_box)
- callback:点击回调,接收 &mut State ,修改应用状态。
基础最简示例
// 普通自定义按钮,内部加粗文本
let content = label("增加计数").weight(FontWeight::BOLD);
button(content, |state: &mut AppState| {
state.count += 1;
})
复杂内容:flex_row组合(文字+符号)
let inner = flex_row((
label("👍"),
label("点赞").weight(FontWeight::BOLD),
));
button(inner, |_state: &mut AppState| {})
三、button、text_button、button_any_pointer三者对比
通用按钮,内部可嵌套布局;左键、空格、回车触发;不区分鼠标按键。
简写:等价 button(label(text), callback) ;只能放纯文本,写法简洁。
可以拿到鼠标按键信息(左键/中键/右键),回调参数携带 Option ,适合需要区分不同鼠标按键的场景。
四、按钮样式的两种方式
方式1:button自带链式样式
button实例自带 .background_color() 、 .corner_radius() 、 .border_color() 、 .hovered_border_color() ,直接修改按钮本身外观。
button(label("蓝色按钮"), |s:&mut AppState| s.count +=1)
.background_color(palette::css::DODGER_BLUE)
.corner_radius(10.0)
.border_color(palette::css::TRANSPARENT);
方式2:外层包sized_box修改尺寸
button本身会跟随内部内容大小;想要固定宽高、整行铺满,外层套 sized_box 。
sized_box(
button(label("整行大按钮"), |_|{})
).expand_width()
注意:
sized_box 控制尺寸;button链式方法控制按钮背景、圆角;不要混淆两者职责。
五、完整可运行示例
Cargo.toml
[package]
name = "xilem_lesson28"
version = "0.1.0"
edition = "2021"
[dependencies]
xilem = "0.4.0"
winit = "0.30"
src/main.rs
use xilem::palette;
use xilem::winit::error::EventLoopError;
use xilem::view::{button, flex_row, label, sized_box, text_button};
use xilem::{EventLoop, FontWeight, WidgetView, WindowOptions, Xilem};
#[derive(Default)]
struct AppState {
count: i32
}
fn app_logic(state: &mut AppState) -> impl WidgetView<AppState> + use<> {
flex_row((
// text_button简写,纯文本按钮
text_button("-1", |s:&mut AppState| s.count -= 1),
// 通用button,内部加粗label
button(label(format!("计数:{}", state.count)).weight(FontWeight::BOLD), |_|{})
.background_color(palette::css::LIGHT_GRAY)
.corner_radius(6.0),
// button内部放flex_row实现符号+文字
button(
flex_row((label("➕"), label("+1"))),
|s:&mut AppState| s.count +=1
)
.background_color(palette::css::SKY_BLUE)
.corner_radius(8.0),
// sized_box包裹,固定尺寸按钮
sized_box(
button(label("归零"), |s:&mut AppState| s.count =0)
.background_color(palette::css::ORANGE_RED)
.corner_radius(8.0)
).width(80.px()).height(36.px()),
))
}
fn main() -> Result<(), EventLoopError> {
let app = Xilem::new_simple(AppState::default(), app_logic);
app.run(EventLoop::with_user_event(), WindowOptions::default())
}
六、高频踩坑清单
七、课后练习题
仅使用本课 button ,搭配label、flex_row、sized_box;禁止嵌套button。
练习1(基础)
创建一个button,内部flex_row包含 label(“🔔”) 和加粗 label(“提醒”) ;设置按钮背景色 palette::css::GOLD ,圆角半径6;点击回调修改state.count+1。
参考答案
button(
flex_row((
label("🔔"),
label("提醒").weight(FontWeight::BOLD)
)),
|s:&mut AppState| s.count += 1
)
.background_color(palette::css::GOLD)
.corner_radius(6.0)
练习2(进阶)
外层flex_row横向布局:
参考答案
flex_row((
button(
flex_row((label("🔔"), label("提醒").weight(FontWeight::BOLD))),
|s:&mut AppState| s.count += 1
).background_color(palette::css::GOLD).corner_radius(6.0),
text_button("重置", |s:&mut AppState| s.count =0),
sized_box(
button(label("填充按钮"), |_|{})
.background_color(palette::css::LIGHT_BLUE)
).expand_width()
))
练习3(拓展)
flex_col纵向布局:
参考答案片段
flex_col((
sized_box(
button(
z_stack((
label("底层文字"),
label("上层叠加文字").weight(FontWeight::BOLD)
)),
|s:&mut AppState| s.count += 1
)
.background_color(palette::css::ALICE_BLUE)
.corner_radius(10.0)
).expand_width()
))


