import NodePoolMgr from "../NodePool/NodePoolMgr"; // ListLayoutManager.ts const { ccclass, property } = cc._decorator; /** * 列表排列方式 */ export enum ListLayoutType { /**垂直排列 */ Vertical = 1, /**水平排列 */ Horizontal = 2, /**网格排列 */ Grid = 3 } /** * 网格布局方向 */ export enum GridDirection { /**水平优先 */ Horizontal = 1, /**垂直优先 */ Vertical = 2 } @ccclass export default class scrollViewList extends cc.Component { /**NodePool处理器 */ @property({ type: cc.Node, tooltip: "NodePool处理器节点" }) public nodePoolNode: cc.Node = null; /**列表项预制体 */ @property({ type: cc.Prefab, tooltip: "列表项预制体" }) public itemPrefab: cc.Prefab = null; /**排列方式 */ @property({ type: cc.Enum(ListLayoutType), tooltip: "排列方式" }) public layoutType: ListLayoutType = ListLayoutType.Vertical; /**网格布局方向 */ @property({ type: cc.Enum(GridDirection), tooltip: "网格布局方向", visible() { return this.layoutType === ListLayoutType.Grid; } }) public gridDirection: GridDirection = GridDirection.Horizontal; /**列表项之间X间隔 */ @property({ type: cc.Integer, tooltip: "列表项X间隔" }) public spacingX: number = 0; /**列表项之间Y间隔 */ @property({ type: cc.Integer, tooltip: "列表项Y间隔" }) public spacingY: number = 0; /**上间距 */ @property({ type: cc.Integer, tooltip: "上间距" }) public paddingTop: number = 0; /**下间距 */ @property({ type: cc.Integer, tooltip: "下间距" }) public paddingBottom: number = 0; /**左间距 */ @property({ type: cc.Integer, tooltip: "左间距" }) public paddingLeft: number = 0; /**右间距 */ @property({ type: cc.Integer, tooltip: "右间距" }) public paddingRight: number = 0; /**列表项宽度 */ @property({ type: cc.Integer, tooltip: "列表项宽度,-1表示使用预制体原始宽度" }) public itemWidth: number = -1; /**列表项高度 */ @property({ type: cc.Integer, tooltip: "列表项高度,-1表示使用预制体原始高度" }) public itemHeight: number = -1; /**是否自动调整content大小 */ @property({ tooltip: "是否自动调整content大小" }) public autoResizeContent: boolean = true; // 私有属性 private scrollView: cc.ScrollView = null; private content: cc.Node = null; private nodePoolMgr: NodePoolMgr = null; private dataList: any[] = []; private itemNodes: cc.Node[] = []; private itemWidthActual: number = 0; private itemHeightActual: number = 0; onLoad() { this.init(); } /** * 初始化组件 */ private init() { this.scrollView = this.node.getComponent(cc.ScrollView); if (!this.scrollView) { console.error("scrollViewList: ScrollView component not found"); return; } this.content = this.scrollView.content; if (!this.content) { console.error("scrollViewList: Content node not found"); return; } // 设置content锚点 this.content.anchorX = 0; this.content.anchorY = 1; // 获取NodePoolMgr组件 if (this.nodePoolNode) { this.nodePoolMgr = this.nodePoolNode.getComponent(NodePoolMgr); } if (!this.nodePoolMgr) { console.error("scrollViewList: NodePoolMgr component not found"); return; } } /** * 设置数据 * @param data 数据数组 */ public setData(data: any[]) { this.dataList = data ? [...data] : []; this.refreshContent(); } /** * 刷新内容显示 */ private refreshContent() { if (!this.content || !this.nodePoolMgr || !this.itemPrefab) { console.warn("scrollViewList: Content, NodePoolMgr or itemPrefab not ready"); return; } // 清空现有内容 this.clearContent(); if (this.dataList.length === 0) { return; } // 初始化实际宽高 this.initItemSize(); // 根据排列方式更新content大小 if (this.autoResizeContent) { this.resizeContent(); } // 创建列表项 this.createItems(); } /** * 初始化列表项实际尺寸 */ private initItemSize() { // 如果设置了自定义尺寸,则使用自定义尺寸 if (this.itemWidth > 0) { this.itemWidthActual = this.itemWidth; } else { // 否则使用预制体原始宽度 const tempNode = cc.instantiate(this.itemPrefab); this.itemWidthActual = tempNode.width; tempNode.destroy(); } if (this.itemHeight > 0) { this.itemHeightActual = this.itemHeight; } else { // 否则使用预制体原始高度 const tempNode = cc.instantiate(this.itemPrefab); this.itemHeightActual = tempNode.height; tempNode.destroy(); } } /** * 调整content大小 */ private resizeContent() { const dataLen = this.dataList.length; switch (this.layoutType) { case ListLayoutType.Vertical: this.content.width = this.content.parent.width; // 修正content高度计算:确保最后一个项能完全显示 // 计算公式:顶部边距 + 所有项高度 + 间隔总和 + 底部边距 if (dataLen > 0) { this.content.height = this.paddingTop + dataLen * this.itemHeightActual + Math.max(0, dataLen - 1) * this.spacingY + this.paddingBottom; } else { this.content.height = this.paddingTop + this.paddingBottom; } if (cc.sys.isMobile) { // 可以根据需要添加额外的底部填充 this.content.height += 20; // 添加20像素的额外空间 } break; case ListLayoutType.Horizontal: this.content.height = this.content.parent.height; // 修正content宽度计算 if (dataLen > 0) { this.content.width = this.paddingLeft + dataLen * this.itemWidthActual + Math.max(0, dataLen - 1) * this.spacingX + this.paddingRight; } else { this.content.width = this.paddingLeft + this.paddingRight; } break; case ListLayoutType.Grid: if (this.gridDirection === GridDirection.Horizontal) { // 水平优先网格 const rowCount = Math.ceil(dataLen / this.getGridColCount()); if (rowCount > 0) { this.content.height = this.paddingTop + rowCount * this.itemHeightActual + Math.max(0, rowCount - 1) * this.spacingY + this.paddingBottom; } else { this.content.height = this.paddingTop + this.paddingBottom; } this.content.width = this.content.parent.width; } else { // 垂直优先网格 const colCount = Math.ceil(dataLen / this.getGridRowCount()); if (colCount > 0) { this.content.width = this.paddingLeft + colCount * this.itemWidthActual + Math.max(0, colCount - 1) * this.spacingX + this.paddingRight; } else { this.content.width = this.paddingLeft + this.paddingRight; } this.content.height = this.content.parent.height; } break; } } /** * 获取网格列数 */ private getGridColCount(): number { if (this.layoutType !== ListLayoutType.Grid) return 1; const contentWidth = this.content.parent.width - this.paddingLeft - this.paddingRight; return Math.floor((contentWidth + this.spacingX) / (this.itemWidthActual + this.spacingX)); } /** * 获取网格行数 */ private getGridRowCount(): number { if (this.layoutType !== ListLayoutType.Grid) return 1; const contentHeight = this.content.parent.height - this.paddingTop - this.paddingBottom; return Math.floor((contentHeight + this.spacingY) / (this.itemHeightActual + this.spacingY)); } /** * 创建列表项 */ private createItems() { const dataLen = this.dataList.length; for (let i = 0; i < dataLen; i++) { // 调用NodePoolMgr时传入指定的预制体 const itemNode = this.nodePoolMgr.getItem(this.itemPrefab); if (!itemNode) { console.error("scrollViewList: Failed to get item from NodePoolMgr"); continue; } itemNode.parent = this.content; this.itemNodes.push(itemNode); // 设置位置 this.setPosition(itemNode, i); // 设置数据 const itemComponent = itemNode.getComponent('ListItem') || itemNode.getComponent(cc.Component); if (itemComponent && typeof itemComponent['updateItem'] === 'function') { itemComponent['updateItem'](this.dataList[i], i); } } } /** * 设置项的位置 * @param itemNode 项节点 * @param index 索引 */ private setPosition(itemNode: cc.Node, index: number) { // 确保节点尺寸正确 if (this.itemWidth > 0) { itemNode.width = this.itemWidth; } if (this.itemHeight > 0) { itemNode.height = this.itemHeight; } switch (this.layoutType) { case ListLayoutType.Vertical: // 垂直排列:每个项在垂直方向上按顺序排列 // 修正位置计算,确保能正确显示所有项 const y = - (this.paddingTop + itemNode.height / 2 + index * (itemNode.height + this.spacingY)); itemNode.setPosition(this.paddingLeft + itemNode.width / 2, y); break; case ListLayoutType.Horizontal: // 水平排列:每个项在水平方向上按顺序排列 const x = this.paddingLeft + itemNode.width / 2 + index * (itemNode.width + this.spacingX); itemNode.setPosition(x, -this.content.height + this.content.height / 2); break; case ListLayoutType.Grid: if (this.gridDirection === GridDirection.Horizontal) { const col = index % this.getGridColCount(); const row = Math.floor(index / this.getGridColCount()); const gridX = this.paddingLeft + itemNode.width / 2 + col * (itemNode.width + this.spacingX); const gridY = - (this.paddingTop + itemNode.height / 2 + row * (itemNode.height + this.spacingY)); itemNode.setPosition(gridX, gridY); } else { const row = index % this.getGridRowCount(); const col = Math.floor(index / this.getGridRowCount()); const gridX = this.paddingLeft + itemNode.width / 2 + col * (itemNode.width + this.spacingX); const gridY = - (this.paddingTop + itemNode.height / 2 + row * (itemNode.height + this.spacingY)); itemNode.setPosition(gridX, gridY); } break; } } /** * 清空内容 */ private clearContent() { // 回收所有节点 for (let i = 0; i < this.itemNodes.length; i++) { this.nodePoolMgr.putItem(this.itemNodes[i]); } this.itemNodes = []; this.content.removeAllChildren(); } /** * 添加数据项 * @param data 数据 * @param index 插入位置,默认添加到末尾 */ public addItem(data: any, index?: number) { if (index === undefined || index >= this.dataList.length) { this.dataList.push(data); } else { this.dataList.splice(index, 0, data); } this.refreshContent(); } /** * 删除数据项 * @param index 索引 */ public removeItem(index: number) { if (index >= 0 && index < this.dataList.length) { this.dataList.splice(index, 1); this.refreshContent(); } } /** * 更新数据项 * @param index 索引 * @param data 新数据 */ public updateItem(index: number, data: any) { if (index >= 0 && index < this.dataList.length) { this.dataList[index] = data; // 更新对应节点 if (index < this.itemNodes.length) { const itemComponent = this.itemNodes[index].getComponent('ListItem') || this.itemNodes[index].getComponent(cc.Component); if (itemComponent && typeof itemComponent['updateItem'] === 'function') { itemComponent['updateItem'](data, index); } } } } /** * 获取数据项 * @param index 索引 */ public getItemData(index: number): any { if (index >= 0 && index < this.dataList.length) { return this.dataList[index]; } return null; } /** * 获取数据长度 */ public getDataLength(): number { return this.dataList.length; } /** * 清空所有数据 */ public clearData() { this.dataList = []; this.refreshContent(); } /** * 销毁时清理资源 */ onDestroy() { this.clearContent(); } }