【Laya】Widget 使用指南
LayaAir引擎的Laya.Widget组件使用指南摘要: Laya.Widget是LayaAir引擎提供的相对布局组件,用于实现响应式UI布局。通过设置top/bottom/left/right/centerX/centerY等属性,可以轻松实现元素在父容器中的定位和自适应布局。 核心功能包括: 边距定位 - 设置元素与父容器各边的距离 居中对齐 - 实现水平和垂直居中 拉伸填充 - 使元素自
·
Laya.Widget 使用指南
简介
Laya.Widget 是 LayaAir 引擎的 相对布局组件,用于设置显示对象相对于父容器的位置。通过设置 top、bottom、left、right、centerX、centerY 等属性,可以实现响应式布局,当父容器尺寸变化时自动调整位置。
适用场景:
- UI 元素的相对布局(按钮、面板、标签等)
- 响应式界面设计
- 锚点定位(如固定在屏幕角落的元素)
- 屏幕适配和自适应布局
继承关系:
Laya.Widget → Laya.Component → Laya.Object
目录
API 参考
边距属性
| 属性 | 类型 | 说明 |
|---|---|---|
top |
number |
距顶边的距离(像素) |
bottom |
number |
距底边的距离(像素) |
left |
number |
距左边的距离(像素) |
right |
number |
距右边的距离(像素) |
居中属性
| 属性 | 类型 | 说明 |
|---|---|---|
centerX |
number |
距水平中心轴的距离(像素) |
centerY |
number |
距垂直中心轴的距离(像素) |
只读属性
| 属性 | 类型 | 说明 |
|---|---|---|
owner |
Sprite |
所属的显示对象(只读) |
方法
| 方法 | 参数 | 返回值 | 说明 |
|---|---|---|---|
resetLayout() |
- | void |
重新计算布局 |
resetLayoutX() |
- | boolean |
重置水平布局 |
resetLayoutY() |
- | boolean |
重置垂直布局 |
onReset() |
- | void |
重置所有边界和中心坐标为 null |
静态属性
| 属性 | 类型 | 说明 |
|---|---|---|
EMPTY |
Widget |
已初始化的 Widget 静态实例 |
基础用法
1. 添加 Widget 组件
@regClass()
export class GameUI extends Laya.Sprite {
private panel: Laya.Sprite;
private widget: Laya.Widget;
onAwake(): void {
// 创建一个面板
this.panel = new Laya.Sprite();
this.panel.graphics.drawRect(0, 0, 200, 100, "#FF0000");
this.addChild(this.panel);
// 添加 Widget 组件
this.widget = this.panel.addComponent(Laya.Widget);
}
}
2. 固定在父容器边缘
// 固定在左上角
this.widget.top = 10;
this.widget.left = 10;
// 固定在右上角
this.widget.top = 10;
this.widget.right = 10;
// 固定在左下角
this.widget.bottom = 10;
this.widget.left = 10;
// 固定在右下角
this.widget.bottom = 10;
this.widget.right = 10;
3. 居中对齐
// 水平居中
this.widget.centerX = 0;
// 垂直居中
this.widget.centerY = 0;
// 完全居中(水平+垂直)
this.widget.centerX = 0;
this.widget.centerY = 0;
// 居中并偏移
this.widget.centerX = 50; // 水平居中后向右偏移 50 像素
this.widget.centerY = -20; // 垂直居中后向上偏移 20 像素
4. 拉伸填充
// 顶部固定,填充整个宽度
this.widget.top = 0;
this.widget.left = 0;
this.widget.right = 0;
// 左右固定,填充整个高度
this.widget.left = 0;
this.widget.top = 0;
this.widget.bottom = 0;
// 填充整个父容器
this.widget.top = 0;
this.widget.bottom = 0;
this.widget.left = 0;
this.widget.right = 0;
5. 重置布局
// 重置所有属性
this.widget.onReset();
// 重新计算布局
this.widget.resetLayout();
// 只重置水平布局
this.widget.resetLayoutX();
// 只重置垂直布局
this.widget.resetLayoutY();
实用示例
示例1: 屏幕四角固定元素
@regClass()
export class CornerElements extends Laya.Sprite {
onAwake(): void {
this.setupCorners();
}
private setupCorners(): void {
// 左上角 - 小地图
let miniMap = this.createPanel(150, 150, "#4488FF");
let widget1 = miniMap.addComponent(Laya.Widget);
widget1.top = 20;
widget1.left = 20;
this.addChild(miniMap);
// 右上角 - 玩家信息
let playerInfo = this.createPanel(200, 80, "#44FF88");
let widget2 = playerInfo.addComponent(Laya.Widget);
widget2.top = 20;
widget2.right = 20;
this.addChild(playerInfo);
// 左下角 - 技能栏
let skillBar = this.createPanel(300, 60, "#FF8844");
let widget3 = skillBar.addComponent(Laya.Widget);
widget3.bottom = 20;
widget3.left = 20;
this.addChild(skillBar);
// 右下角 - 聊天框
let chatBox = this.createPanel(250, 150, "#FF44FF");
let widget4 = chatBox.addComponent(Laya.Widget);
widget4.bottom = 20;
widget4.right = 20;
this.addChild(chatBox);
}
private createPanel(w: number, h: number, color: string): Laya.Sprite {
let panel = new Laya.Sprite();
panel.graphics.drawRect(0, 0, w, h, color);
panel.size(w, h);
return panel;
}
}
示例2: 居中弹窗
@regClass()
export class CenterDialog extends Laya.Sprite {
private bg: Laya.Sprite;
private widget: Laya.Widget;
constructor() {
super();
this.setupDialog();
}
private setupDialog(): void {
// 创建背景
this.bg = new Laya.Sprite();
this.bg.graphics.drawRect(0, 0, 400, 300, "#333333");
this.bg.size(400, 300);
this.addChild(this.bg);
// 添加 Widget 实现居中
this.widget = this.addComponent(Laya.Widget);
this.widget.centerX = 0;
this.widget.centerY = 0;
}
// 显示对话框
public show(parent: Laya.Sprite): void {
parent.addChild(this);
}
}
示例3: 响应式头部栏
@regClass()
export class ResponsiveHeader extends Laya.Sprite {
private bg: Laya.Sprite;
private title: Laya.Text;
constructor() {
super();
this.setupHeader();
}
private setupHeader(): void {
// 背景条 - 横向填充整个宽度
this.bg = new Laya.Sprite();
this.bg.graphics.drawRect(0, 0, 100, 60, "#222222");
this.bg.size(100, 60);
let bgWidget = this.bg.addComponent(Laya.Widget);
bgWidget.top = 0;
bgWidget.left = 0;
bgWidget.right = 0;
this.addChild(this.bg);
// 标题 - 居中显示
this.title = new Laya.Text();
this.title.text = "游戏标题";
this.title.fontSize = 24;
this.title.color = "#FFFFFF";
this.title.size(200, 40);
let titleWidget = this.title.addComponent(Laya.Widget);
titleWidget.centerX = 0;
titleWidget.top = 10;
this.bg.addChild(this.title);
}
}
示例4: 侧边栏面板
@regClass()
export class SidePanel extends Laya.Sprite {
private panel: Laya.Sprite;
private toggleBtn: Laya.Sprite;
constructor() {
super();
this.setupPanel();
}
private setupPanel(): void {
// 侧边栏 - 左侧固定,填充整个高度
this.panel = new Laya.Sprite();
this.panel.graphics.drawRect(0, 0, 200, 400, "#444444");
this.panel.size(200, 400);
let panelWidget = this.panel.addComponent(Laya.Widget);
panelWidget.top = 0;
panelWidget.bottom = 0;
panelWidget.left = 0;
this.addChild(this.panel);
// 切换按钮 - 固定在侧边栏右边缘外侧
this.toggleBtn = new Laya.Sprite();
this.toggleBtn.graphics.drawRect(0, 0, 30, 60, "#FF8800");
this.toggleBtn.size(30, 60);
let btnWidget = this.toggleBtn.addComponent(Laya.Widget);
btnWidget.centerY = 0;
btnWidget.left = 200; // 相对于父容器左侧 200 像素
this.addChild(this.toggleBtn);
}
}
示例5: 动态布局更新
@regClass()
export class DynamicLayout extends Laya.Script {
@property(Laya.Widget)
public widget: Laya.Widget = null;
private originalTop: number = 10;
onAwake(): void {
this.originalTop = this.widget.top;
}
// 切换到顶部
public stickToTop(): void {
this.widget.onReset();
this.widget.top = this.originalTop;
this.widget.left = 10;
this.widget.resetLayout();
}
// 切换到右下角
public moveToBottomRight(): void {
this.widget.onReset();
this.widget.bottom = 10;
this.widget.right = 10;
this.widget.resetLayout();
}
// 居中显示
public center(): void {
this.widget.onReset();
this.widget.centerX = 0;
this.widget.centerY = 0;
this.widget.resetLayout();
}
// 填充整个父容器
public fill(): void {
this.widget.onReset();
this.widget.top = 0;
this.widget.bottom = 0;
this.widget.left = 0;
this.widget.right = 0;
this.widget.resetLayout();
}
}
高级技巧
1. 相对定位组合
// 距离右下角各 50 像素,但自身尺寸为 100x100
let panel = new Laya.Sprite();
panel.size(100, 100);
let widget = panel.addComponent(Laya.Widget);
widget.bottom = 50; // 底边向上 50
widget.right = 50; // 右边向左 50
// 这会将面板定位在距离父容器右下角 (50, 50) 的位置
2. 居中偏移
// 水平居中后向右偏移 100 像素
let widget = panel.addComponent(Laya.Widget);
widget.centerX = 100;
// 垂直居中后向上偏移 50 像素
widget.centerY = -50;
3. 监听父容器尺寸变化
@regClass()
export class ResponsiveElement extends Laya.Sprite {
private widget: Laya.Widget;
onAwake(): void {
this.widget = this.addComponent(Laya.Widget);
this.widget.centerX = 0;
this.widget.centerY = 0;
// 监听父容器尺寸变化
if (this.parent) {
this.parent.on(Laya.Event.RESIZE, this, this.onParentResize);
}
}
private onParentResize(): void {
console.log("父容器尺寸变化,重新计算布局");
this.widget.resetLayout();
}
}
4. 条件布局切换
@regClass()
export class AdaptiveLayout extends Laya.Script {
@property(Laya.Widget)
public widget: Laya.Widget = null;
// 根据屏幕方向切换布局
public adaptToOrientation(isLandscape: boolean): void {
this.widget.onReset();
if (isLandscape) {
// 横屏:靠左显示
this.widget.top = 10;
this.widget.left = 10;
this.widget.bottom = 10;
} else {
// 竖屏:靠上显示
this.widget.top = 10;
this.widget.left = 10;
this.widget.right = 10;
}
this.widget.resetLayout();
}
// 根据屏幕尺寸切换布局
public adaptToSize(width: number, height: number): void {
this.widget.onReset();
if (width < 500) {
// 小屏幕:完全居中
this.widget.centerX = 0;
this.widget.centerY = 0;
} else {
// 大屏幕:固定在左上角
this.widget.top = 20;
this.widget.left = 20;
}
this.widget.resetLayout();
}
}
5. 多元素相对布局
@regClass()
export class MultiElementLayout extends Laya.Sprite {
onAwake(): void {
this.setupLayout();
}
private setupLayout(): void {
// 主面板 - 居中
let mainPanel = this.createPanel(400, 300, "#446688");
let mainWidget = mainPanel.addComponent(Laya.Widget);
mainWidget.centerX = 0;
mainWidget.centerY = 0;
this.addChild(mainPanel);
// 关闭按钮 - 相对于主面板右上角
let closeBtn = this.createPanel(30, 30, "#FF4444");
let btnWidget = closeBtn.addComponent(Laya.Widget);
btnWidget.top = 10;
btnWidget.right = 10;
mainPanel.addChild(closeBtn);
}
private createPanel(w: number, h: number, color: string): Laya.Sprite {
let panel = new Laya.Sprite();
panel.graphics.drawRect(0, 0, w, h, color);
panel.size(w, h);
return panel;
}
}
最佳实践
1. 布局模式选择
| 需求 | 推荐方案 | 示例 |
|---|---|---|
| 固定在某个角落 | 设置对应的边距 | top=10, left=10 |
| 居中显示 | 使用 centerX/centerY | centerX=0, centerY=0 |
| 拉伸填充 | 设置相反的边距 | top=0, bottom=0 |
| 宽度固定且居中 | centerX + 固定宽度 | centerX=0, width=200 |
2. 避免冲突
// ❌ 错误:同时设置 top 和 bottom 可能导致不可预期的行为
widget.top = 10;
widget.bottom = 10;
// 元素会被拉伸,但高度未定义
// ✅ 正确:使用组合方式明确意图
// 方式1:固定高度,设置顶部
widget.top = 10;
// 元素高度由 size() 设置
// 方式2:拉伸填充
widget.top = 0;
widget.bottom = 0;
3. 与 Box 组件配合
@regClass()
export class UILayout extends Laya.Sprite {
onAwake(): void {
// 使用 Box 作为容器
let container = new Laya.Box();
container.size(800, 600);
// 添加 Widget 使容器居中
let containerWidget = container.addComponent(Laya.Widget);
containerWidget.centerX = 0;
containerWidget.centerY = 0;
this.addChild(container);
// 子元素使用绝对定位
let item = new Laya.Sprite();
item.graphics.drawRect(0, 0, 100, 50, "#FF0000");
item.pos(50, 50);
container.addChild(item);
}
}
4. 性能优化
// ❌ 频繁调用 resetLayout
onUpdate(): void {
this.widget.resetLayout(); // 每帧都重置,性能差
}
// ✅ 只在必要时重置
onSomeConditionChange(): void {
this.widget.resetLayout(); // 只在布局变化时重置
}
// ✅ 使用脏标记
private _layoutDirty: boolean = false;
markLayoutDirty(): void {
this._layoutDirty = true;
}
onUpdate(): void {
if (this._layoutDirty) {
this.widget.resetLayout();
this._layoutDirty = false;
}
}
5. 屏幕适配
@regClass()
export class ScreenAdapter extends Laya.Sprite {
onAwake(): void {
this.setupUI();
this.adaptToScreen();
// 监听屏幕尺寸变化
Laya.stage.on(Laya.Event.RESIZE, this, this.adaptToScreen);
}
private setupUI(): void {
// 创建 UI 元素
let panel = new Laya.Sprite();
panel.graphics.drawRect(0, 0, 200, 100, "#444488");
panel.size(200, 100);
let widget = panel.addComponent(Laya.Widget);
widget.centerX = 0;
widget.centerY = 0;
this.addChild(panel);
}
private adaptToScreen(): void {
let screenWidth = Laya.stage.width;
let screenHeight = Laya.stage.height;
// 根据屏幕比例调整 UI 布局
if (screenWidth / screenHeight > 1.5) {
// 宽屏
console.log("宽屏模式");
} else {
// 窄屏
console.log("窄屏模式");
}
}
}
6. 布局调试
// 显示布局信息
function debugWidget(widget: Laya.Widget): void {
console.log("=== Widget Info ===");
console.log("top:", widget.top);
console.log("bottom:", widget.bottom);
console.log("left:", widget.left);
console.log("right:", widget.right);
console.log("centerX:", widget.centerX);
console.log("centerY:", widget.centerY);
console.log("owner:", widget.owner);
if (widget.owner) {
console.log("owner.x:", widget.owner.x);
console.log("owner.y:", widget.owner.y);
console.log("owner.width:", widget.owner.width);
console.log("owner.height:", widget.owner.height);
}
}
注意事项
- 父容器尺寸:Widget 基于父容器的尺寸计算相对位置,确保父容器已设置尺寸
- 属性冲突:同时设置相反方向的边距(如 top + bottom)会拉伸元素
- 性能考虑:避免频繁调用
resetLayout(),只在必要时更新 - 居中偏移:
centerX和centerY的值为 0 时完全居中,正值向右/下偏移,负值向左/上偏移 - 组件生命周期:Widget 在
onEnable时自动计算布局,之后监听父容器的 RESIZE 事件 - 静态实例:可使用
Laya.Widget.EMPTY作为空 Widget 实例的替代 - 重置属性:调用
onReset()会将所有边界和中心属性设为 null
相关文档
更多推荐


所有评论(0)