[LayaAir 1.0]Laya中Button等组件lose skin的问题
在UI界面给组件设置皮肤时遇到的问题,Button组件丢失了皮肤”lose skin wxlocal/enemy.png”无法正常显示,但是旁边的Image组件却能正常的展示。
设计模式中的两架飞机,上面那架是Image组件,下面那架为Button组件
实际画面展示的时候却只有上面那架飞机能显示出来
资源也都进行了预加载。
//程序入口
Laya.init(852, 480, Laya.WebGL);
let resArr = [
{url:"wxlocal/enemy.png",type:Laya.Loader.IMAGE},
{url:"wxlocal/bg2.jpg",type:Laya.Loader.IMAGE}
];
Laya.loader.load(resArr, Laya.Handler.create(null, onLoaded));
function onLoaded(): void {
Laya.URL.basePath ="https://xxx.com/";
//实例UI界面
var Main: MenuPageUI = new MenuPageUI();
Laya.stage.addChild(Main);
}
在我找原因的时候,我发现主要是和设置了基础路径有关,也发现了3种可以正常展示飞机的做法。
做法A:直接将Laya.URL.basePath ="https://xxx.com/";设置基础路径这一行删除。
做法B:将Laya.URL.basePath ="https://xxx.com/";设置基础路径这一行提前到 Laya.loader.load(resArr, Laya.Handler.create(null, onLoaded)); 之前。
做法C:在资源渲染完之后再设置基础路径。
根据官方的文档(官方本地资源和动态资源加载的文档),wxlocal(默认本地目录)目录被视为本地目录,即便使用了URL.basePath,对于包含在nativefiles白名单内的目录名或文件,都不会从网络动态加载,只会从本地加载。但相同的皮肤设置下,最终Image组件可以正常展示,而Button组件则会lose skin。
那么问题肯定是在两个组件之间存在的差异。
从官方文档中可以看到:Image 类是用于表示位图图像或绘制图形的显示对象。 Image和Clip组件是唯一支持异步加载的两个组件,比如img.skin = "abc/xxx.png",其他UI组件均不支持异步加载。
知道了差异再去看看在laya.ui.js中,具体的处理方式是如何的:
在laya.ui.js中找到Button设置皮肤的地方,发现它会去调用changeClips。
__getset(0,__proto,'skin',function(){
return this._skin;
},function(value){
if (this._skin !=value){
this._skin=value;
this.callLater(this.changeClips);
this._setStateChanged();
}
});
而在changeClips我们可以发现,img如果没有取到就会打印lose skin,并且直接返回。
__proto.changeClips=function(){
var img=Loader.getRes(this._skin);
if (!img){
console.log("lose skin",this._skin);
return;
};
var width=img.sourceWidth;
var height=img.sourceHeight / this._stateNum;
img.$_GID || (img.$_GID=Utils.getGID());
var key=img.$_GID+"-"+this._stateNum;
var clips=WeakObject.I.get(key);
if (!Utils.isOkTextureList(clips)){
clips=null;
}
if (clips)this._sources=clips;
else {
this._sources=;
if (this._stateNum===1){
this._sources.push(img);
}else {
for (var i=0;i < this._stateNum;i++){
this._sources.push(Texture.createFromTexture(img,0,height *i,width,height));
}
}
WeakObject.I.set(key,this._sources);
}
if (this._autoSize){
this._bitmap.width=this._width || width;
this._bitmap.height=this._height || height;
if (this._text){
this._text.width=this._bitmap.width;
this._text.height=this._bitmap.height;
}
}else {
this._text && (this._text.x=width);
}
}
再去看看laya.core.js中的getRes是如何运作的。
/**
*获取指定资源地址的资源。
*@param url 资源地址。
*@return 返回资源。
*/
__proto.getRes=function(url){
return Loader.getRes(url);
}
Loader.getRes:
Loader.getRes=function(url){
return Loader.loadedMap[URL.formatURL(url)];
}
URL.formatURL:
URL.formatURL=function(url,base){
if (!url)return "null path";
if (url.indexOf(":")> 0)return url;
if (URL.customFormat !=null)url=URL.customFormat(url,base);
var char1=url.charAt(0);
if (char1==="."){
return URL.formatRelativePath((base || URL.basePath)+url);
}else if (char1==='~'){
return URL.rootPath+url.substring(1);
}else if (char1==="d"){
if (url.indexOf("data:image")===0)return url;
}else if (char1==="/"){
return url;
}
console.log("最终返回的地址",(base || URL.basePath)+url);
return (base || URL.basePath)+url;
}
打印出最终返回的地址可以发现这是带着基础路径的地址,而我们缓存的地址为wxlocal/enemy.png,这并不是我们想要的结果。
因为这个地址中没有资源所以getRes中的Loader.loadedMap[URL.formatRUL(url)]得到的肯定也是null,皮肤资源也就设置失败了。
在来看看image的处理方式有什么不同。
__getset(0,__proto,'skin',function(){
return this._skin;
},function(value){
if (this._skin !=value){
this._skin=value;
if (value){
var source=Loader.getRes(value);
if (source){
this.source=source;
this.onCompResize();
}else Laya.loader.load(this._skin,Handler.create(this,this.setSource,[this._skin]),null,/*laya.net.Loader.IMAGE*/"image",1,true,this._group);
}else {
this.source=null;
}
}
});
和Button一样,getRes返回的也是null,但是Image多做了一件事Laya.loader.load。
__proto.load=function(url,complete,progress,type,priority,cache,group,ignoreCache){
var _$this=this;
(priority===void 0)&& (priority=1);
(cache===void 0)&& (cache=true);
(ignoreCache===void 0)&& (ignoreCache=false);
if ((url instanceof Array))return this._loadAssets(url,complete,progress,type,priority,cache,group);
var content=Loader.getRes(url);
if (content !=null){
Laya.timer.frameOnce(1,null,function(){
progress && progress.runWith(1);
complete && complete.runWith(content);
_$this._loaderCount || _$this.event(/*laya.events.Event.COMPLETE*/"complete");
});
}else {
var info=LoaderManager._resMap;
if (!info){
info=this._infoPool.length ? this._infoPool.pop():new ResInfo();
info.url=url;
info.type=type;
info.cache=cache;
info.group=group;
info.ignoreCache=ignoreCache;
complete && info.on(/*laya.events.Event.COMPLETE*/"complete",complete.caller,complete.method,complete.args);
progress && info.on(/*laya.events.Event.PROGRESS*/"progress",progress.caller,progress.method,progress.args);
LoaderManager._resMap=info;
priority=priority < this._maxPriority ? priority :this._maxPriority-1;
this._resInfos[priority].push(info);
this._next();
}else {
complete && info._createListener(/*laya.events.Event.COMPLETE*/"complete",complete.caller,complete.method,complete.args,false,false);
progress && info._createListener(/*laya.events.Event.PROGRESS*/"progress",progress.caller,progress.method,progress.args,false,false);
}
}
return this;
}
可以看到content和info都是取不到的,但是它会去生成一个info并存到LoaderManager._resMap中。
然后Laya.loader.load加载结束回调setSource,在回调中成功的设置皮肤。
总结一下,产生Image和Button这两个组件差异的主要原因有两个:
第一是在项目中加上基础路径后,formatURL在判断的时候对本地资源地址的处理加上了基础路径。
第二是Button不能像Image那样在资源未加载时,自动加载并缓存。
这时候再去看前面所说的三个做法:
做法A中,因为没有加基础路径,所以本地缓存的资源路径为:wxlocal/enemy.png,formatURL中的资源路径为:wxlocal/enemy.png
做法B中,因为在设置本地缓存路径前就加了基础路径,所以本地缓存的资源路径为:基础路径+wxlocal/enemy.png,formatURL中的资源路径为:基础路径+wxlocal/enemy.png
做法C中,因为在图片渲染之后在设置的基础路径,所以本地缓存的资源路径为:wxlocal/enemy.png,formatURL中的资源路径为:wxlocal/enemy.png
本质的解决方法:
其实主要的问题是第一个问题,第一个问题解决了,第二个问题也不会出现(第二点算是拓展Button组件功能吧)。
第一个问题我们可以在formatURL中加个判断,无论怎样只要是本地白名单中的资源,都不加基础路径直接返回。
这里我们在laya.wxmini.js中更改(不同的平台可以在不同的适配库中修改)。
在MiniAdpter.init中加入这两行:
MiniAdpter.init=function(isPosMsg,isSon){
MiniAdpter.EnvConfig.formatURL=URL["formatURL"];
URL["formatURL"]=MiniAdpter.formatURL;
}
并且自己重写formatURL
MiniAdpter.formatURL=function(url,base){
if (MiniFileMgr.isLocalNativeFile(url)){
if (URL.customFormat !=null) {
return URL.customFormat(url,base);
}
return url;
};
return MiniAdpter.EnvConfig.formatURL(url, base);
}
第二个问题我们可以在laya.ui.js中Button的处理皮肤地址时参考Image的处理方式来实现。
__getset(0,__proto,'skin',function(){
return this._skin;
},function(value){
if (this._skin !=value){
this._skin=value;
if(!Loader.getRes(this._skin)){
Laya.loader.load(this._skin,Handler.create(this,this.setSource,[this._skin]),null,/*laya.net.Loader.IMAGE*/"image");
}else{
this.setSource();
}
}
});
/**
*@private
*设置皮肤资源。
*/
__proto.setSource=function(url,img){
this.callLater(this.changeClips);
this._setStateChanged();
}
附件中有一个简单的demo,Laya版本为1.7.22。
设计模式中的两架飞机,上面那架是Image组件,下面那架为Button组件
实际画面展示的时候却只有上面那架飞机能显示出来
资源也都进行了预加载。
//程序入口
Laya.init(852, 480, Laya.WebGL);
let resArr = [
{url:"wxlocal/enemy.png",type:Laya.Loader.IMAGE},
{url:"wxlocal/bg2.jpg",type:Laya.Loader.IMAGE}
];
Laya.loader.load(resArr, Laya.Handler.create(null, onLoaded));
function onLoaded(): void {
Laya.URL.basePath ="https://xxx.com/";
//实例UI界面
var Main: MenuPageUI = new MenuPageUI();
Laya.stage.addChild(Main);
}
在我找原因的时候,我发现主要是和设置了基础路径有关,也发现了3种可以正常展示飞机的做法。
做法A:直接将Laya.URL.basePath ="https://xxx.com/";设置基础路径这一行删除。
做法B:将Laya.URL.basePath ="https://xxx.com/";设置基础路径这一行提前到 Laya.loader.load(resArr, Laya.Handler.create(null, onLoaded)); 之前。
做法C:在资源渲染完之后再设置基础路径。
根据官方的文档(官方本地资源和动态资源加载的文档),wxlocal(默认本地目录)目录被视为本地目录,即便使用了URL.basePath,对于包含在nativefiles白名单内的目录名或文件,都不会从网络动态加载,只会从本地加载。但相同的皮肤设置下,最终Image组件可以正常展示,而Button组件则会lose skin。
那么问题肯定是在两个组件之间存在的差异。
从官方文档中可以看到:Image 类是用于表示位图图像或绘制图形的显示对象。 Image和Clip组件是唯一支持异步加载的两个组件,比如img.skin = "abc/xxx.png",其他UI组件均不支持异步加载。
知道了差异再去看看在laya.ui.js中,具体的处理方式是如何的:
在laya.ui.js中找到Button设置皮肤的地方,发现它会去调用changeClips。
__getset(0,__proto,'skin',function(){
return this._skin;
},function(value){
if (this._skin !=value){
this._skin=value;
this.callLater(this.changeClips);
this._setStateChanged();
}
});
而在changeClips我们可以发现,img如果没有取到就会打印lose skin,并且直接返回。
__proto.changeClips=function(){
var img=Loader.getRes(this._skin);
if (!img){
console.log("lose skin",this._skin);
return;
};
var width=img.sourceWidth;
var height=img.sourceHeight / this._stateNum;
img.$_GID || (img.$_GID=Utils.getGID());
var key=img.$_GID+"-"+this._stateNum;
var clips=WeakObject.I.get(key);
if (!Utils.isOkTextureList(clips)){
clips=null;
}
if (clips)this._sources=clips;
else {
this._sources=;
if (this._stateNum===1){
this._sources.push(img);
}else {
for (var i=0;i < this._stateNum;i++){
this._sources.push(Texture.createFromTexture(img,0,height *i,width,height));
}
}
WeakObject.I.set(key,this._sources);
}
if (this._autoSize){
this._bitmap.width=this._width || width;
this._bitmap.height=this._height || height;
if (this._text){
this._text.width=this._bitmap.width;
this._text.height=this._bitmap.height;
}
}else {
this._text && (this._text.x=width);
}
}
再去看看laya.core.js中的getRes是如何运作的。
/**
*获取指定资源地址的资源。
*@param url 资源地址。
*@return 返回资源。
*/
__proto.getRes=function(url){
return Loader.getRes(url);
}
Loader.getRes:
Loader.getRes=function(url){
return Loader.loadedMap[URL.formatURL(url)];
}
URL.formatURL:
URL.formatURL=function(url,base){
if (!url)return "null path";
if (url.indexOf(":")> 0)return url;
if (URL.customFormat !=null)url=URL.customFormat(url,base);
var char1=url.charAt(0);
if (char1==="."){
return URL.formatRelativePath((base || URL.basePath)+url);
}else if (char1==='~'){
return URL.rootPath+url.substring(1);
}else if (char1==="d"){
if (url.indexOf("data:image")===0)return url;
}else if (char1==="/"){
return url;
}
console.log("最终返回的地址",(base || URL.basePath)+url);
return (base || URL.basePath)+url;
}
打印出最终返回的地址可以发现这是带着基础路径的地址,而我们缓存的地址为wxlocal/enemy.png,这并不是我们想要的结果。
因为这个地址中没有资源所以getRes中的Loader.loadedMap[URL.formatRUL(url)]得到的肯定也是null,皮肤资源也就设置失败了。
在来看看image的处理方式有什么不同。
__getset(0,__proto,'skin',function(){
return this._skin;
},function(value){
if (this._skin !=value){
this._skin=value;
if (value){
var source=Loader.getRes(value);
if (source){
this.source=source;
this.onCompResize();
}else Laya.loader.load(this._skin,Handler.create(this,this.setSource,[this._skin]),null,/*laya.net.Loader.IMAGE*/"image",1,true,this._group);
}else {
this.source=null;
}
}
});
和Button一样,getRes返回的也是null,但是Image多做了一件事Laya.loader.load。
__proto.load=function(url,complete,progress,type,priority,cache,group,ignoreCache){
var _$this=this;
(priority===void 0)&& (priority=1);
(cache===void 0)&& (cache=true);
(ignoreCache===void 0)&& (ignoreCache=false);
if ((url instanceof Array))return this._loadAssets(url,complete,progress,type,priority,cache,group);
var content=Loader.getRes(url);
if (content !=null){
Laya.timer.frameOnce(1,null,function(){
progress && progress.runWith(1);
complete && complete.runWith(content);
_$this._loaderCount || _$this.event(/*laya.events.Event.COMPLETE*/"complete");
});
}else {
var info=LoaderManager._resMap;
if (!info){
info=this._infoPool.length ? this._infoPool.pop():new ResInfo();
info.url=url;
info.type=type;
info.cache=cache;
info.group=group;
info.ignoreCache=ignoreCache;
complete && info.on(/*laya.events.Event.COMPLETE*/"complete",complete.caller,complete.method,complete.args);
progress && info.on(/*laya.events.Event.PROGRESS*/"progress",progress.caller,progress.method,progress.args);
LoaderManager._resMap=info;
priority=priority < this._maxPriority ? priority :this._maxPriority-1;
this._resInfos[priority].push(info);
this._next();
}else {
complete && info._createListener(/*laya.events.Event.COMPLETE*/"complete",complete.caller,complete.method,complete.args,false,false);
progress && info._createListener(/*laya.events.Event.PROGRESS*/"progress",progress.caller,progress.method,progress.args,false,false);
}
}
return this;
}
可以看到content和info都是取不到的,但是它会去生成一个info并存到LoaderManager._resMap中。
然后Laya.loader.load加载结束回调setSource,在回调中成功的设置皮肤。
总结一下,产生Image和Button这两个组件差异的主要原因有两个:
第一是在项目中加上基础路径后,formatURL在判断的时候对本地资源地址的处理加上了基础路径。
第二是Button不能像Image那样在资源未加载时,自动加载并缓存。
这时候再去看前面所说的三个做法:
做法A中,因为没有加基础路径,所以本地缓存的资源路径为:wxlocal/enemy.png,formatURL中的资源路径为:wxlocal/enemy.png
做法B中,因为在设置本地缓存路径前就加了基础路径,所以本地缓存的资源路径为:基础路径+wxlocal/enemy.png,formatURL中的资源路径为:基础路径+wxlocal/enemy.png
做法C中,因为在图片渲染之后在设置的基础路径,所以本地缓存的资源路径为:wxlocal/enemy.png,formatURL中的资源路径为:wxlocal/enemy.png
本质的解决方法:
其实主要的问题是第一个问题,第一个问题解决了,第二个问题也不会出现(第二点算是拓展Button组件功能吧)。
第一个问题我们可以在formatURL中加个判断,无论怎样只要是本地白名单中的资源,都不加基础路径直接返回。
这里我们在laya.wxmini.js中更改(不同的平台可以在不同的适配库中修改)。
在MiniAdpter.init中加入这两行:
MiniAdpter.init=function(isPosMsg,isSon){
MiniAdpter.EnvConfig.formatURL=URL["formatURL"];
URL["formatURL"]=MiniAdpter.formatURL;
}
并且自己重写formatURL
MiniAdpter.formatURL=function(url,base){
if (MiniFileMgr.isLocalNativeFile(url)){
if (URL.customFormat !=null) {
return URL.customFormat(url,base);
}
return url;
};
return MiniAdpter.EnvConfig.formatURL(url, base);
}
第二个问题我们可以在laya.ui.js中Button的处理皮肤地址时参考Image的处理方式来实现。
__getset(0,__proto,'skin',function(){
return this._skin;
},function(value){
if (this._skin !=value){
this._skin=value;
if(!Loader.getRes(this._skin)){
Laya.loader.load(this._skin,Handler.create(this,this.setSource,[this._skin]),null,/*laya.net.Loader.IMAGE*/"image");
}else{
this.setSource();
}
}
});
/**
*@private
*设置皮肤资源。
*/
__proto.setSource=function(url,img){
this.callLater(this.changeClips);
this._setStateChanged();
}
附件中有一个简单的demo,Laya版本为1.7.22。
没有找到相关结果
已邀请:
要回复问题请先登录
0 个回复