import UIComponent = Laya.UIComponent; import Sprite = Laya.Sprite; import Pool = Laya.Pool; import View = Laya.View; import SScript from './SScript'; import { GameUtils } from '../core/GameUtils'; import SLayout from './SLayout'; /** * 挂载该附加的组件将具备根据dataProvider属性值批量增减子节点的功能,子节点必须具备一个名为"dataSource"的set方法来接收来自PrefabFactory的数据并对内部视图进行更新 */ export default class PrefabFactory extends SScript{ /** @prop {name:userPool, tips:"是否在创建/删除Prefab时启用对象池", type:bool, default:false} */ userPool:boolean = false; /** @prop {name:prefabClass, tips:"批量生产的子节点类名", type:Class} */ prefabClass:string; protected $presetProperties: {[propertyKey:string]:any}; /** 宿主上挂载的SLayout脚本 */ protected $SLayout:SLayout; private _presetProperties:string; private _dataProvider:any[] = []; private _dataFilter:(data)=>boolean; private _dataMap:(data)=>any; constructor(){ super(); } onAwake(){ this.$SLayout = this.owner.getComponent(SLayout); this.refresh(); } refresh(){ this.removeAll(false); if(this._dataProvider) { for(let i of this._dataProvider) { this.$produce(i); } } this.$onProduceFinish(); } push(i, changeDataProvider:boolean=true){ let doPush = (i)=>{ if(this._dataFilter && !this._dataFilter(i))return; if(changeDataProvider){ if(!this._dataProvider){ this._dataProvider = [i]; } else{ this._dataProvider.push(i); } } this.$produce(i); } if(_.isArray(i)) { i.forEach(doPush); } else { doPush(i); } this.$onProduceFinish(); } pop(changeDataProvider:boolean=true):View{ let numChildren = this.owner.numChildren; if(numChildren > 0) { let removedNode = this.$SLayout ? this.$SLayout.children[numChildren - 1] : this.owner.getChildAt(numChildren - 1); return this.remove(removedNode as View, changeDataProvider); } } /** * 移除一个子节点 * @param target 子节点 * @param changeDataProvider 是否从dataProvider中删除子节点所使用的data。默认为true */ remove(target:View, changeDataProvider=true):View{ if(!target || target.parent != this.owner)return; if(this.userPool && this.$canRecycle(target)) { Pool.recover(this.prefabClass, target); } if(changeDataProvider) { let data = target.dataSource; let index = this._dataProvider.indexOf(data); if(index != -1)this._dataProvider.splice(index, 1); } target.removeSelf(); return target; } /** * 根据一个子节点所使用的data来删除它 * @param data * @param changeDataProvider 是否从dataProvider中删除子节点所使用的data。默认为true */ removeByData(data, changeDataProvider=true):View{ if(!data)return; let n = this.owner.numChildren; for(let i = 0; i < n; i++) { let c = this.owner.getChildAt(i) as View; let cd = c.dataSource; if(cd == data || ((typeof cd['isEqual'] == 'function') && cd['isEqual'](data))) { return this.remove(c, changeDataProvider); } } } /** * 移除全部子节点 * @param changeDataProvider 是否从dataProvider中删除子节点所使用的data。默认为true */ removeAll(changeDataProvider=true):void{ while(this.owner.numChildren > 0) { this.remove(this.owner.getChildAt(0) as View, changeDataProvider); } } /** * 遍历所有Prefab * @param loopFuc 遍历所用方法 * @param reverse 是否逆序遍历 */ prefabForEach(loopFuc:(p:Sprite, index:number) => any, reverse=false):any{ if(!this.owner)return; return GameUtils.aryForEach(this.owner._children, loopFuc, reverse); } /** 设置一个预设属性 */ setPresetProperty(v){ this.$presetProperties = GameUtils.objAssign(this.$presetProperties ? this.$presetProperties : {}, v); this.prefabForEach((prefab:Sprite)=>{ for(let p in this.$presetProperties) { prefab[p] = this.$presetProperties[p]; } }) } /** * 生产一个Prefab */ protected $produce(data){ let r; if(this.prefabClass) { if(this.userPool) { r = Pool.getItem(this.prefabClass); } if(!r) { r = GameUtils.getClassInstanceByName(this.prefabClass); } for(let p in this.$presetProperties) { r[p] = this.$presetProperties[p]; } this.owner.addChild(r); r.dataSource = data; } return r; } /** * refresh方法调用结束后会执行的方法 */ protected $onRefreshFinish(){ this.owner.event('ownerRefreshFinish'); } /** * 此方法用于判断一个子节点在移除后是否可被回收循环使用 */ protected $canRecycle(target){ if(!this.prefabClass)return false; return target instanceof GameUtils.getClassConstructorByName(this.prefabClass); } /** 创建完Prefab后执行的回调方法 */ protected $onProduceFinish(){ //当宿主是LayoutBox子类型时需要等待其排列好内部子节点后再触发$onRefreshFinish回调 if(this.$SLayout) { this.owner.once(Laya.Event.RESIZE, this, this.$onRefreshFinish); } else { this.$onRefreshFinish(); } } protected $filterData(data){ } /** 数据源。该数据源的push方法会被设置为PrefabFactory.prototype.push */ set dataProvider(v:any[]){ if(this._dataProvider != v) { if(this._dataMap && v)v = v.map(this._dataMap); this._dataProvider = v; if(this._dataProvider && this._dataFilter) { this._dataProvider = this._dataProvider.filter(this._dataFilter); } this.refresh(); } } get dataProvider(){ return this._dataProvider; } /** @prop {name:presetProperties, tips:"生产出的子节点所具有的初始属性", type:String} */ get presetProperties() { return this._presetProperties; } set presetProperties(v:string) { if(this._presetProperties != v){ this._presetProperties = v; try{ this.$presetProperties = JSON.parse(this.presetProperties); } catch(e){ this.$presetProperties = {}; } } } /** 数据过滤器,设置它可以过滤掉一些不允许添加进来的数据 */ get dataFilter() { return this._dataFilter; } set dataFilter(v:any) { if(this._dataFilter != v){ this._dataFilter = v; if(this._dataFilter) { this.prefabForEach((prefab:View, index:number)=>{ if(!this._dataFilter(prefab.dataSource)) { this.remove(prefab); } }); } } } /** 设置它可以将dataProvider中获得的数据进行一些处理 */ get dataMap() { return this._dataMap; } set dataMap(v:any) { if(this._dataMap != v){ this._dataMap = v; if(this._dataMap) { this.prefabForEach((prefab:View, index:number)=>{ prefab.dataSource = this._dataMap(prefab.dataSource); }); } } } }