UI框架的功能: 方便快捷的对UI界面进行管理:如快速打开与关闭一个界面,防止相同界面多开等等。
核心代码有2个脚本,一个UIMgr,一个UIBase。 UIMgr的功能是管理UI界面。 UIBase的功能是:所有界面都要继承自UIBase,这样界面就可以直接方便地调用父类的方法,更好地复用代码。管理特殊前缀开头的节点名称,只有使用了特定字符串开头的节点才会被添加到UI管理的数据结构里。
UIMgr里面用到了一个SceneMgr,可以在我的上一篇文章里找到有它的介绍,UIMgr的场景方法和SceneMgr是一样的,同样是一个单例。
import SceneMgr from "./SceneMgr";
export default class UIMgr extends Laya.Script
{
private uiPrefabMap=new Map<string,Laya.Prefab>();
public static Instance: UIMgr;
constructor()
{
super();
UIMgr.Instance=this;
}
public GetPrefabPath(uiPrefabName:string):string
{
return "prefab/"+uiPrefabName+".prefab";
}
public FindTargetUI<T extends Laya.UIComponent>(targetName:string,parent:Laya.Node):T
{
if(!parent){
return null;
}
return parent.getChildByName(targetName)as T;
}
public DestroyUI(uiName:string)
{
let sc2d=SceneMgr.Instance.GetCurSc2D();
if(sc2d){
let ui=sc2d.getChildByName(uiName)as Laya.UIComponent;
if(ui){
ui.destroy(true);
}
}
}
public SetUIVisible(uiName:string,visible:boolean)
{
let sc2d=SceneMgr.Instance.GetCurSc2D();
if(sc2d){
let ui=sc2d.getChildByName(uiName)as Laya.UIComponent;
if(ui){
ui.visible=visible;
}
}
}
public OpenUI(uiCtlerScript:any,callback:Function=(param:any)=>{},parent?:Laya.Node,isOnlyOne=true)
{
let uiName=uiCtlerScript.name;
if(!parent){
parent=SceneMgr.Instance.GetCurSc2D();
}
if(!parent){
return;
}
if(parent.getChildByName(uiName)){
if(isOnlyOne==true){
return;
}
}
if(this.uiPrefabMap.has(uiName))
{
let uiPrefab=this.uiPrefabMap.get(uiName) as Laya.Prefab;
this.OpenUICommonOp(uiPrefab,uiCtlerScript,parent,callback);
}
else
{
this.LoadUIPrefab(uiName,(uiPrefab:Laya.Prefab)=>
{
this.OpenUICommonOp(uiPrefab,uiCtlerScript,parent,callback);
});
}
}
public ClearRes(uiCtlerScript:any)
{
let k=uiCtlerScript.name;
if(!this.uiPrefabMap.has(k))
return;
this.uiPrefabMap.delete(k);
Laya.LoaderManager.prototype.clearRes(this.GetPrefabPath(k));
Laya.Resource.destroyUnusedResources();
}
private LoadUIPrefab(uiName:string,callback:Function=(uiPrefab:any)=>{})
{
let uiPath=this.GetPrefabPath(uiName);
Laya.loader.load(uiPath,Laya.Handler.create(this,(uiPrefab:Laya.Prefab)=>
{
if(!uiPrefab){
console.error("不存在目标预制体",uiName);
return;
}
this.uiPrefabMap.set(uiName,uiPrefab);
callback(uiPrefab);
}));
}
private OpenUICommonOp(uiPrefab:Laya.Prefab,uiCtlerScript:any,parent:Laya.Node,callback:Function=(ui:any)=>{})
{
let uiName=uiCtlerScript.name;
let ui=uiPrefab.create()as Laya.UIComponent;
parent.addChild(ui);
ui.name=uiName;
this.AddSrcToNode(uiCtlerScript,ui);
callback(ui);
}
private AddSrcToNode(src:any,targetNode:Laya.Node)
{
if(targetNode.getComponent(src))
return;
targetNode.addComponent(src);
}
public LogUIMapInfo()
{
if(this.uiPrefabMap.size==0||!this.uiPrefabMap==null)
{
console.log("UI预制体资源Map为空");
return;
}
for (let [key, value] of this.uiPrefabMap)
{
console.log("key:",key,"===","value:",value);
}
}
}
UIBase脚本: 当UI界面里的节点使用了prefixArray里的前缀命名时(如:imgTest,txtPlayerName等等),此节点会被添加到allUIDic里,在脚本里可以通过名称获取目标节点。prefixArray里可以自定义添加或删除你的节点前缀。
export default class UIBase extends Laya.Script {
public allUIDic: Map<string, Laya.Node> = new Map<string, Laya.Node>();
private prefixArray: string[] = ["img", "btn", "txt", "list", "box","hsld","ti"];
constructor() {
super();
}
onAwake() {
this.SetAllUINodesDic();
}
public CloseUI() {
this.owner.destroy(true);
}
private GetNodeByMap<T extends Laya.Node>(nodeName:string,map:Map<string,Laya.Node>):T
{
if(!map.has(nodeName)){
return null;
}
return map.get(nodeName) as T;
}
public AddBtnEvent(btName: string, callback: Function,needPlayClickSound=true,clickSoundPath?:string) {
let bt: Laya.Button = this.GetBtn(btName);
if (!bt) return;
bt.on(Laya.Event.CLICK,this,callback)
}
public GetTxt(txtName: string): Laya.Text {
return this.GetNodeByMap<Laya.Text>(txtName, this.allUIDic);
}
public GetImg(imgName: string): Laya.Image {
return this.GetNodeByMap<Laya.Image>(imgName, this.allUIDic);
}
public GetBtn(btnName: string): Laya.Button {
return this.GetNodeByMap<Laya.Button>(btnName, this.allUIDic);
}
public GetList(listName: string): Laya.List {
return this.GetNodeByMap<Laya.List>(listName, this.allUIDic);
}
public GetBox(boxName: string): Laya.Box {
return this.GetNodeByMap<Laya.Box>(boxName, this.allUIDic);
}
public GetHSlider(name: string): Laya.HSlider {
return this.GetNodeByMap<Laya.HSlider>(name, this.allUIDic);
}
public GetTextInput(name: string): Laya.TextInput {
return this.GetNodeByMap<Laya.TextInput>(name, this.allUIDic);
}
public GetUIByT<T extends Laya.UIComponent>(uiName: string): T {
return this.GetNodeByMap<T>(uiName, this.allUIDic);
}
public SetVisible<T extends Laya.UIComponent>(uiName: string,visible:boolean)
{
this.GetUIByT<T>(uiName).visible=visible;
}
public SetText(uiName: string,content:string)
{
this.GetTxt(uiName).text=content;
}
public SetImgSkin(imgName:string,skin:string)
{
this.GetImg(imgName).skin=skin;
}
public SetBtnSkin(btnName:string,skin:string)
{
this.GetImg(btnName).skin=skin;
}
private CheckNeedAddToDic(uiName: string) {
for (let i = 0; i < this.prefixArray.length; i++) {
if (uiName.startsWith(this.prefixArray[i])) {
return true;
}
}
return false;
}
public SetAllUINodesDic() {
this.allUIDic = this.GetAllChildrenMap(this.owner);
}
private GetChildNodesArray(target: Laya.Node): Laya.Node[] {
let nodeArray: Laya.Node[] = [];
for (let i = 0; i < target.numChildren; i++) {
let node = target.getChildAt(i);
if (node) {
nodeArray.push(node);
}
}
return nodeArray;
}
private FindAndGetAllChildren(parentNode: Laya.Node, outNodesArray: Laya.Node[]): Laya.Node[] {
if (parentNode.numChildren > 0) {
let nodeArray = this.GetChildNodesArray(parentNode);
nodeArray.forEach(node => {
if (this.CheckNeedAddToDic(node.name) == true) {
outNodesArray.push(node);
}
if (this.GetChildNodesArray(node).length > 0) {
this.FindAndGetAllChildren(node, outNodesArray);
}
else {
return outNodesArray;
}
});
}
return null;
}
private GetAllChildrenArray(parentNode: Laya.Node): Laya.Node[] {
let allChildrenArray: Laya.Node[] = [];
this.FindAndGetAllChildren(parentNode, allChildrenArray);
return allChildrenArray;
}
private GetAllChildrenMap(parentNode: Laya.Node): Map<string, Laya.Node> {
let allChildrenArray = this.GetAllChildrenArray(parentNode);
let map = new Map();
for (let i = 0; i < allChildrenArray.length; i++) {
if (!map.has(allChildrenArray[i].name)) {
map.set(allChildrenArray[i].name, (allChildrenArray[i]));
}
}
return map;
}
public LogUIMap() {
if (this.allUIDic.size== 0||!this.allUIDic){
console.log("UI预制体资源Map为空");
return;
}
console.log("UI节点个数:", this.allUIDic.size);
for (let [key, value] of this.allUIDic) {
console.log("key:", key, "===", "value:", value);
}
}
}
UI框架使用方法:
在创建UI界面前先要说一下场景的创建,我创建场景使用的是View而不是Scene,因为Scene不好做分辨率适配。 场景创建完成后要把View场景的设计分辨率和四个边距点设置为0。我使用的是1136x640的分辨率。 UI界面制作的流程:
比如现在要添加一个名称为TestUIPanel的界面。 我一般创建一个新UI界面是这样做的: 首先创建一个Image名称叫做:TestUIPanel 把Image的skin设置为空,四个边距点设置为0,因为这个Image会作为UI预制体的底板。 如果你的UI需要有黑色半透明背景,可以添加一个Box组件,然后设置它的颜色和透明度,但是4个边距点也要设置为0。 然后在下面添加了一个按钮,点击可以关闭界面。 最后就是保存此UI界面为预制体既可。 Laya默认的UI预制体是在这个目录下的 我加载时使用的也是默认目录。 双击打开UI预制体: 可以点击设置页面调整UI预制体的默认场景大小。 目前UI界面就创建完成了。
接下来是编写界面的控制和打开的代码: 创建一个和界面名称一样的脚本,这里必须要保证界面名称和脚本名称一致,因为我是通过把脚本转为字符串去读取的UI预制体。 接着把创建好的脚本继承自UIBase,如果要使用onAwake方法必须调用一下基类的super().onAwake或是执行一下this.SetAllUINodesDic();方法。
接下来就是UI的逻辑代码: 打开此界面: 打开界面的方法有四个参数: @param uiCtlerScript — 控制此界面的脚本类 @param callback — 加载完回调(Laya.UIComponent)=>{} @param parent — 父级节点,不传则使用当前2d场景作为父节点 @param isOnlyOne — 是否只能存在一个,false时可以打开多个相同界面
运行运行: 点击关闭按钮: 以上就是此框架的大概内容,代码也比较简单,可以自己具体看一下。
|