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);
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;
this.widget.centerY = -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);
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;
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. 相对定位组合
let panel = new Laya.Sprite();
panel.size(100, 100);
let widget = panel.addComponent(Laya.Widget);
widget.bottom = 50;
widget.right = 50;
2. 居中偏移
let widget = panel.addComponent(Laya.Widget);
widget.centerX = 100;
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. 避免冲突
widget.top = 10;
widget.bottom = 10;
widget.top = 10;
widget.top = 0;
widget.bottom = 0;
3. 与 Box 组件配合
@regClass()
export class UILayout extends Laya.Sprite {
onAwake(): void {
let container = new Laya.Box();
container.size(800, 600);
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. 性能优化
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 {
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;
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)