
ReactiveUI
一个简单、响应式的UI框架,提供更好的事件处理、便捷的UI数据绑定以及可复用组件,用于在Hytale中创建复杂且动态的用户界面!
Hytale ReactiveUI
一个现代化的、反应式的 Hytale 服务端模组 UI 框架,通过自动数据绑定、声明式事件处理和可复用组件简化 UI 管理。

特性
- 自动数据绑定 - 值变化时 UI 自动更新
- 声明式事件处理 - 简洁、流畅的 API 用于处理 UI 事件
- 可复用组件 - 构建具有正确生命周期管理的模块化 UI 元素
- 类型安全 - 带类型化父页面引用的泛型元素
- 数组/列表支持 - 轻松创建动态列表和重复元素
- 事件驱动架构 - 高效的事件路由和参数解码
安装
Gradle (Kotlin DSL)
repositories {
mavenCentral()
}
dependencies {
implementation("dev.jonrapp:hytale-reactiveui:1.0")
}
Gradle (Groovy)
repositories {
mavenCentral()
}
dependencies {
implementation 'dev.jonrapp:hytale-reactiveui:1.0'
}
Maven
<dependency>
<groupId>dev.jonrapp</groupId>
<artifactId>hytale-reactiveui</artifactId>
<version>1.0</version>
</dependency>
核心概念
1. 页面
页面是 Hytale 中 UI 的主要入口点。ReactiveUI 提供了 ReactiveUiPage,这是一个增强的页面实现,简化了事件处理、数据绑定和元素管理。它内置了对管理“主元素”(可通过单一元素轻松切换)的支持,非常适合标签页界面或向导式 UI。
public class MyPage extends ReactiveUiPage {
public MyPage(@Nonnull PlayerRef playerRef) {
super(playerRef, CustomPageLifetime.CanDismiss);
}
@Override
public void build(@Nonnull Ref<EntityStore> ref,
@Nonnull UICommandBuilder commands,
@Nonnull UIEventBuilder events,
@Nonnull Store<EntityStore> store) {
// 加载你的 UI 文件
commands.append("MyPage.ui");
// 绑定事件
bindEvent(
CustomUIEventBindingType.Activating,
"#TabButton",
events,
EventBinding.action("tab-clicked")
.onEvent(context -> showPrimaryElement(new MyTab(this)))
);
// 显示初始元素
showPrimaryElement(new MyTab(this));
}
@Override
public String getRootContentSelector() {
return "#Content"; // 主元素显示的位置
}
}
2. 元素
元素是可复用的 UI 组件,管理自己的生命周期、事件和数据绑定。
public class MyElement extends Element<MyPage> {
public MyElement(MyPage pageRef) {
super(pageRef);
}
@Override
protected void onCreate(String root, UICommandBuilder commands, UIEventBuilder events) {
// 加载元素 UI
commands.append(root, "MyElement.ui");
// 绑定按钮点击事件
bindEvent(
CustomUIEventBindingType.Activating,
"#SubmitButton",
events,
EventBinding.action("submit-clicked")
.onEvent(context -> handleSubmit())
);
}
private void handleSubmit() {
// 处理事件
}
}
3. 事件绑定
ReactiveUI 提供了一个流畅的 API,用于将事件绑定到 UI 元素,并自动清理。
// 简单的事件绑定
bindEvent(
CustomUIEventBindingType.Activating,
"#Button",
events,
EventBinding.action("button-clicked")
.onEvent(context -> {
// 处理点击
})
);
// 带参数的事件
bindEvent(
CustomUIEventBindingType.Activating,
"#ItemButton",
events,
EventBinding.action("item-selected")
.withEventData("itemId", Codec.STRING, "item_123")
.onEvent(context -> {
String itemId = context.getParameter("itemId");
// 使用参数
})
);
// 条件事件处理(如果已处理则返回 true)
bindEvent(
CustomUIEventBindingType.Activating,
"#ConditionalButton",
events,
EventBinding.action("conditional-action")
.onEventConditional(context -> {
if (someCondition()) {
// 处理事件
return true; // 事件被消费
}
return false; // 继续到下一个处理器
})
);
4. 自动数据绑定
使用 @UIBinding 注解实现值变化时自动更新 UI。
public class PlayerCard extends Element<MyPage> {
@UIBinding(selector = "#PlayerName.TextSpans")
private UIBindable<String> playerName;
@UIBinding(selector = "#PlayerScore.TextSpans")
private UIBindable<String> score;
public PlayerCard(MyPage pageRef) {
super(pageRef);
}
@Override
protected void onCreate(String root, UICommandBuilder commands, UIEventBuilder events) {
commands.append(root, "PlayerCard.ui");
// 设置初始值
playerName.set("Steve");
score.set("100");
}
public void updateScore(int newScore) {
// 调用 set() 时 UI 自动更新
score.set(String.valueOf(newScore));
}
}
关键点:
- 字段由框架自动初始化
- 调用
set()会立即更新 UI - 使用
set(value, commands)可以批量执行多个更新 - 支持
String、Message或任何类型(通过toString()转换)
5. 数组/列表元素
为列表、库存或重复模式创建元素的多个实例。
public class ItemList extends Element<MyPage> {
public ItemList(MyPage pageRef) {
super(pageRef);
}
@Override
protected void onCreate(String root, UICommandBuilder commands, UIEventBuilder events) {
commands.append(root, "ItemList.ui");
// 创建 10 个物品元素
for (int i = 0; i < 10; i++) {
ItemElement item = new ItemElement(pageRef, i);
item.create("#ItemContainer", i, commands, events);
}
}
}
public class ItemElement extends Element<MyPage> {
private final int index;
@UIBinding(selector = "#ItemIndex.TextSpans")
private UIBindable<String> itemIndex;
public ItemElement(MyPage pageRef, int index) {
super(pageRef);
this.index = index;
}
@Override
protected void onCreate(String root, UICommandBuilder commands, UIEventBuilder events) {
commands.append(root, "ItemElement.ui");
// 设置索引值(与创建时批量处理)
itemIndex.set(String.valueOf(index), commands);
}
}
工作原理:
create(root, index, commands, events)会创建一个带有索引选择器的容器- 每个实例都会获得唯一的选择器,例如
#ItemElement0、#ItemElement1等 - 数据绑定会自动限定到每个实例
高级用法
类型安全的页面引用
元素是泛型的,并提供对其父页面的类型安全访问:
public class MyElement extends Element<MySpecificPage> {
public MyElement(MySpecificPage pageRef) {
super(pageRef);
}
@Override
protected void onCreate(String root, UICommandBuilder commands, UIEventBuilder events) {
// 以完全类型安全的方式访问页面特有方法
pageRef.someCustomMethod();
}
}
手动事件注册
如需更多控制,可以直接注册事件处理器:
registerEventHandler("my-action", EventHandlerBuilder.create()
.withParameter("playerId", Codec.STRING)
.build(context -> {
String playerId = context.getParameter("playerId");
// 处理事件
})
);
生命周期管理
元素在卸载时会自动清理其事件处理器:
@Override
public void onUnload() {
super.onUnload(); // 清理所有已注册的事件
// 在此添加自定义清理代码
}
示例
请查看 examples 目录获取完整的工作示例,包括:
- 带主元素切换的标签页界面
- 带迭代元素的动态列表
- 带自动 UI 更新的数据绑定
- 带参数的事件处理
许可证
本项目采用 MIT 许可证 - 详情请见 LICENSE 文件。
贡献
欢迎贡献!请随时提交 Pull Request。
从旧版本(HyUI)迁移
如果你正在从本项目的先前版本(HyUI)迁移,唯一的变化是页面类和包的命名:
HyUiPage -> ReactiveUiPage
import dev.jonrapp.hyui. -> import dev.jonrapp.hytaleReactiveUi.
链接
正在加载版本记录…
正在加载评论…
评论在新手盒子客户端中发表,这里同步展示。