决战-Pow!第二集:UI管理
2018-02-03 本文已影响11人
回忆并快
有了基本的模块化管理,接下来另外很重要的就是UI管理。
一个方便易用的UI框架,会大大提高我们开发界面逻辑效率。
今天来为大家带来一个我实现的UI管理(基于Layabox)
首先,LayaIDE里面已经包含了一个比较强大的UI编辑器。
Login界面
这里为了做UI的适配,可以添加了一个根节点root(宽高为iphone7的分辨率(这个分辨率我会动态变更),锚点anchor在中心)
然后根据相应UI需要进行何种对齐来进行设置内部节点,如下图。
绝对居中的部分,且向上偏移32个像素,这里只需要设置centerX和centerY属性
然后如果我想让某部分UI始终,左下对齐,可以这么编辑UI。 设置宽高同root(方便对位置),然后设置left和bottom属性为0,并且设置锚点为0,1
这些是我对UI的约定,为了程序中做适配。
然后导出并且发布资源,UI资源准备就基本完成了~
编写基本UI逻辑,其实就是创建一个自己管理的类来继承Laya的View。
class LoginPage extends ui.LoginPageUI{
constructor(){
super();
this.initialize();
}
initialize(){
this.startButton.on(Laya.Event.CLICK, this, this.onStart);
}
onStart(){
PluginManager.getInstance().openPlugin(PluginType.LOBBY);
}
}
像这个登录界面,逻辑就很简单,直接初始化后,监听startButton的点击事件,用来触发打开大厅插件。
当然除了这个还有一些对话框的需求,这里采用Laya的Dialog来解决,同样是继承一个类不过继承的是Dialog。
像这个房间列表(暂时用假数据代替内容)
房间列表,这个UI的大小即为模态层点击的区域,跟前面的界面编辑不太一样
class RoomDialog extends ui.RoomDialogUI{
private _roomList:Array<any> = [{name: "test"}, {name: "test"}, {name: "test"}, {name: "test"}, {name: "test"}, {name: "test"}, {name: "test"}, {name: "test"}, {name: "test"}]
constructor(){
super();
this.roomList.array = this._roomList;
this.roomList.vScrollBarSkin = "";
this.roomList.renderHandler = Handler.create(this, this.onRenderHandler);
this.roomList.mouseHandler = Handler.create(this, this.onMouseHandler);
this.roomList.refresh();
this.roomList.scrollBar.elasticBackTime = 150;
this.roomList.scrollBar.elasticDistance = 200;
}
onRenderHandler(cell:Laya.Box,index:number){
Logger.log("onRenderHandler");
}
onMouseHandler(cell:Laya.Event,index:number){
Logger.log("onMouseHandler");
}
}
然后这个UI通过什么来管理呢,我创建了一个UIManager来管理,这个类包含了界面打开,关闭,适配等等逻辑,还有对话框的管理。
class UIManager{
private static _instance: UIManager = null;
constructor() {}
public static getInstance(): UIManager {
return this._instance || (this._instance = new this());
}
initialize(){
}
closePage(classObj:any){
if(this._pages.containsKeyValue(classObj)){
this._pages.get(classObj).removeSelf();
this._pages.get(classObj).destroy();
this._pages.set(classObj, null);
this._pages.removeKeyValue(classObj);
}
}
closeAllPages(){
for(let pair of this._pages){
if(pair.value != null){
pair.value.removeSelf();
pair.value.destroy();
this._pages.set(pair.key, null);
}
}
}
closeAllDialogs(){
for(let pair of this._dialogs){
if(pair.value != null){
pair.value.close();
pair.value.destroy();
this._dialogs.set(pair.key, null);
}
}
}
private _dialogs:Dictionary<any, any> = new Dictionary<any, any>();
closeDialog(classObj:any){
if(this._dialogs.containsKeyValue(classObj)){
if ((this._dialogs.get(classObj) as Laya.Dialog).parent != null){
(this._dialogs.get(classObj) as Laya.Dialog).close();
// this._dialogs.get(classObj).destroy();
// this._dialogs.set(classObj, null);
}
this._dialogs.removeKeyValue(classObj);
}
}
openDialog(classObj:any, data?:any, closeAll?:boolean){
let dialog:Laya.Dialog = new classObj(data) as Laya.Dialog;
dialog.closeEffect = null;
if(!dialog) {
Logger.log("dialog dont exist.");
return null;
}
if(closeAll){
this.closeAllDialogs();
}
if(this._dialogs.containsKeyValue(classObj)){
Logger.log("dialog exist.");
dialog.popup();
return dialog;
}
dialog.popup();
// dialog.closeHandler = Handler.create(this, function(name){
// UIManager.getInstance().closeDialog(classObj);
// })
this._dialogs.addKeyValue(classObj, dialog);
return dialog;
}
getDialog(clsObj:any){
return this._dialogs.get(clsObj);
}
private _pages:Dictionary<any, any> = new Dictionary<any, any>();
getPage(clsObj:any){
return this._pages.get(clsObj);
}
openPage(classObj:any, clossAll?:boolean, parent?:Laya.Node):any{
let page:any = new classObj();
if(!page) {
Logger.log("page dont exist.");
return null;
}
if(clossAll){
this.closeAllPages();
}
if(this._pages.containsKeyValue(classObj)){
return this._pages.get(classObj);
}
if (parent) {
parent.addChild(page);
}else{
Laya.stage.addChild(page);
}
// 这里是约定逻辑,会同台调整root的大小,以至于适配
if(page["root"] != null){
let ratio = Laya.Browser.width/Laya.stage.width;
let oy:number = ((Laya.stage.height*ratio - Laya.Browser.height)/ratio);
page["root"].size(750, 1334-oy);
}
this._pages.addKeyValue(classObj, page);
return page;
}
}
这里需要额外注意一下root的处理。
对于一些常用UI例如Loading之类,我直接实用一个UICommon管理:
class UICommon{
private static _loadingPage:BackgroundPage
static createUI(){
if(this._loadingPage == null){
this._loadingPage = new BackgroundPage();
}
}
public static openLoading(){
this.createUI();
if(this._loadingPage.parent == null){
Laya.stage.addChild(this._loadingPage);
}
this._loadingPage.showLoading();
}
public static closeLoading(){
if(this._loadingPage == null) return;
if(this._loadingPage.parent){
this._loadingPage.setLoading('');
this._loadingPage.hideLoading();
this._loadingPage.removeSelf();
}
}
public static setLoadingText(progress:number){
if(this._loadingPage == null) return;
if(this._loadingPage.parent){
this._loadingPage.setLoading("当前进度:" + progress + "%");
}
}
public static showBackground(){
this.createUI();
if(!this._loadingPage.parent){
Laya.stage.addChild(this._loadingPage);
}
this._loadingPage.hideLoading();
}
}
这些都准备好了,我应该如何使用?
打开Loading我可以这样:
UICommon.openLoading();
打开普通界面:
// 登录界面
let page:LoginPage = UIManager.getInstance().openPage(LoginPage);
page.pow.play(); // 播放登录动画
打开对话框(模态层):
// 房间列表对话框
UIConfig.popupBgAlpha = 0.75;
let roomDialog:Laya.Dialog = UIManager.getInstance().openDialog(RoomDialog);
roomDialog.closeHandler = Handler.create(this, ()=>{
this.close();
});
这样满足了我们在游戏中最基本的UI需求,当然还有更加复杂的UI功能就需要对这些内容进行扩展。
终于写完了第二集,因为年会的原因,拖延了更新,后面会好好做调整,做到两天一更。