mirror of
https://github.com/568071718/creator-collection-view
synced 2025-04-23 17:48:41 +00:00
Compare commits
5 Commits
Author | SHA1 | Date | |
---|---|---|---|
|
a7834bcf15 | ||
|
58c81f9573 | ||
|
c139707e62 | ||
|
c771065674 | ||
|
618102e232 |
@ -299,6 +299,24 @@ class _scroll_view extends ScrollView {
|
||||
}
|
||||
}
|
||||
|
||||
class _yx_node_pool extends NodePool {
|
||||
getAtIdx(indexPath: YXIndexPath, ...args: any[]): Node | null {
|
||||
const nodes: Node[] = this['_pool']
|
||||
for (let index = 0; index < nodes.length; index++) {
|
||||
const obj = nodes[index];
|
||||
let comp = obj.getComponent(_yx_node_element_comp)
|
||||
if (comp && comp.attributes.indexPath.equals(indexPath)) {
|
||||
nodes.splice(index, 1)
|
||||
// @ts-ignore
|
||||
const handler = this.poolHandlerComp ? obj.getComponent(this.poolHandlerComp) : null;
|
||||
if (handler && handler.reuse) { handler.reuse(arguments); }
|
||||
return obj
|
||||
}
|
||||
}
|
||||
return null
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* 节点的布局属性
|
||||
*/
|
||||
@ -642,7 +660,7 @@ export class YXCollectionView extends Component {
|
||||
registerCell(identifier: string, maker: () => Node, poolComp: (new (...args: any[]) => YXCollectionViewCell) | string | null = null) {
|
||||
let elementCategory: typeof YXLayoutAttributes.prototype.elementCategory = 'Cell'
|
||||
identifier = elementCategory + identifier
|
||||
let pool = new NodePool(poolComp)
|
||||
let pool = new _yx_node_pool(poolComp)
|
||||
this.pools.set(identifier, pool)
|
||||
this.makers.set(identifier, maker)
|
||||
}
|
||||
@ -653,7 +671,7 @@ export class YXCollectionView extends Component {
|
||||
registerSupplementary(identifier: string, maker: () => Node, poolComp: (new (...args: any[]) => YXCollectionViewCell) | string | null = null) {
|
||||
let elementCategory: typeof YXLayoutAttributes.prototype.elementCategory = 'Supplementary'
|
||||
identifier = elementCategory + identifier
|
||||
let pool = new NodePool(poolComp)
|
||||
let pool = new _yx_node_pool(poolComp)
|
||||
this.pools.set(identifier, pool)
|
||||
this.makers.set(identifier, maker)
|
||||
}
|
||||
@ -671,19 +689,21 @@ export class YXCollectionView extends Component {
|
||||
/**
|
||||
* 通过标识符从重用池里取出一个可用的 cell 节点
|
||||
* @param identifier 注册时候的标识符
|
||||
* @param indexPath 可选,尝试通过 indexPath 获取刷新之前的节点 (尽可能的保证刷新前和刷新后这个位置仍然是同一个节点),避免节点复用导致的刷新闪烁问题
|
||||
*/
|
||||
dequeueReusableCell(identifier: string): Node {
|
||||
return this._dequeueReusableElement(identifier, 'Cell')
|
||||
dequeueReusableCell(identifier: string, indexPath: YXIndexPath = null): Node {
|
||||
return this._dequeueReusableElement(identifier, 'Cell', indexPath)
|
||||
}
|
||||
|
||||
/**
|
||||
* 通过标识符从重用池里取出一个可用的 supplementary 节点
|
||||
* @param identifier 注册时候的标识符
|
||||
* @param indexPath 可选,尝试通过 indexPath 获取刷新之前的节点 (尽可能的保证刷新前和刷新后这个位置仍然是同一个节点),避免节点复用导致的刷新闪烁问题
|
||||
*/
|
||||
dequeueReusableSupplementary(identifier: string): Node {
|
||||
return this._dequeueReusableElement(identifier, 'Supplementary')
|
||||
dequeueReusableSupplementary(identifier: string, indexPath: YXIndexPath = null): Node {
|
||||
return this._dequeueReusableElement(identifier, 'Supplementary', indexPath)
|
||||
}
|
||||
private _dequeueReusableElement(identifier: string, elementCategory: typeof YXLayoutAttributes.prototype.elementCategory) {
|
||||
private _dequeueReusableElement(identifier: string, elementCategory: typeof YXLayoutAttributes.prototype.elementCategory, indexPath: YXIndexPath = null) {
|
||||
identifier = elementCategory + identifier
|
||||
let pool = this.pools.get(identifier)
|
||||
if (pool == null) {
|
||||
@ -691,6 +711,11 @@ export class YXCollectionView extends Component {
|
||||
}
|
||||
let result: Node = null
|
||||
|
||||
// 尝试从重用池获取 (牺牲一点性能,尝试通过 indexPath 获取对应的节点,防止刷新闪烁的问题)
|
||||
if (result == null && indexPath && pool instanceof _yx_node_pool) {
|
||||
result = pool.getAtIdx(indexPath)
|
||||
}
|
||||
|
||||
// 尝试从重用池获取
|
||||
if (result == null) {
|
||||
result = pool.get()
|
||||
|
140
list-3x/assets/lib/grid-layout.ts
Normal file
140
list-3x/assets/lib/grid-layout.ts
Normal file
@ -0,0 +1,140 @@
|
||||
import { math, UITransform, warn } from "cc";
|
||||
import { YXCollectionView, YXIndexPath, YXLayout, YXLayoutAttributes } from "./yx-collection-view";
|
||||
|
||||
export class GridLayout extends YXLayout {
|
||||
|
||||
/**
|
||||
* 节点大小
|
||||
*/
|
||||
itemSize: math.Size = new math.Size(100, 100)
|
||||
|
||||
/**
|
||||
* 垂直间距
|
||||
*/
|
||||
horizontalSpacing: number = 0
|
||||
|
||||
/**
|
||||
* 水平间距
|
||||
*/
|
||||
verticalSpacing: number = 0
|
||||
|
||||
/**
|
||||
* 整体对齐方式
|
||||
* 0靠左 1居中 2靠右
|
||||
*/
|
||||
alignment: number = 1
|
||||
|
||||
/**
|
||||
* 获取每行最多可以容纳多少个节点
|
||||
*/
|
||||
protected getMaxItemsPerRow(collectionView: YXCollectionView): number {
|
||||
if (this._maxItemsPerRow == null) {
|
||||
let num = 1
|
||||
const width = collectionView.node.getComponent(UITransform).contentSize.width
|
||||
while ((num * this.itemSize.width + (num - 1) * this.horizontalSpacing) <= width) { num++ }
|
||||
num = Math.max(1, num - 1)
|
||||
this._maxItemsPerRow = num
|
||||
}
|
||||
return this._maxItemsPerRow
|
||||
}
|
||||
protected _maxItemsPerRow: number = null
|
||||
|
||||
prepare(collectionView: YXCollectionView): void {
|
||||
if (collectionView.scrollDirection === YXCollectionView.ScrollDirection.VERTICAL) {
|
||||
this._prepare_vertical(collectionView)
|
||||
return
|
||||
}
|
||||
if (collectionView.scrollDirection === YXCollectionView.ScrollDirection.HORIZONTAL) {
|
||||
warn(`GridLayout 仅支持垂直方向排列`)
|
||||
this._prepare_vertical(collectionView)
|
||||
return
|
||||
}
|
||||
}
|
||||
|
||||
protected _prepare_vertical(collectionView: YXCollectionView) {
|
||||
collectionView.scrollView.horizontal = false
|
||||
collectionView.scrollView.vertical = true
|
||||
|
||||
let attrs: YXLayoutAttributes[] = []
|
||||
let contentSize = collectionView.node.getComponent(UITransform).contentSize.clone()
|
||||
|
||||
// 容器宽度
|
||||
const width = contentSize.width
|
||||
|
||||
// 计算每行最多可以放多少个节点
|
||||
this._maxItemsPerRow = null
|
||||
let num = this.getMaxItemsPerRow(collectionView)
|
||||
|
||||
// 根据设置的对齐方式计算左边距
|
||||
let left = 0
|
||||
if (this.alignment == 1) {
|
||||
let maxWidth = (num * this.itemSize.width + (num - 1) * this.horizontalSpacing) // 每行节点总宽度
|
||||
left = (width - maxWidth) * 0.5
|
||||
}
|
||||
if (this.alignment == 2) {
|
||||
let maxWidth = (num * this.itemSize.width + (num - 1) * this.horizontalSpacing) // 每行节点总宽度
|
||||
left = width - maxWidth
|
||||
}
|
||||
|
||||
const numberOfSections = collectionView.getNumberOfSections()
|
||||
if (numberOfSections > 1) { warn(`GridLayout 暂时不支持分区模式`) }
|
||||
|
||||
const numberOfItems = collectionView.getNumberOfItems(0)
|
||||
for (let index = 0; index < numberOfItems; index++) {
|
||||
|
||||
// 计算这个节点是第几行
|
||||
let row = Math.floor(index / num)
|
||||
|
||||
// 计算这个节点是第几列
|
||||
let column = index % num
|
||||
|
||||
// 计算节点 origin
|
||||
let x = left + (this.itemSize.width + this.horizontalSpacing) * column
|
||||
let y = (this.itemSize.height + this.verticalSpacing) * row
|
||||
|
||||
let attr = YXLayoutAttributes.layoutAttributesForCell(new YXIndexPath(0, index))
|
||||
attr.frame.x = x
|
||||
attr.frame.y = y
|
||||
attr.frame.width = this.itemSize.width
|
||||
attr.frame.height = this.itemSize.height
|
||||
attrs.push(attr)
|
||||
|
||||
// 更新内容高度
|
||||
contentSize.height = Math.max(contentSize.height, attr.frame.yMax)
|
||||
}
|
||||
|
||||
this.attributes = attrs
|
||||
this.contentSize = contentSize
|
||||
}
|
||||
|
||||
initOffset(collectionView: YXCollectionView): void {
|
||||
collectionView.scrollView.scrollToTop()
|
||||
}
|
||||
|
||||
layoutAttributesForElementsInRect(rect: math.Rect, collectionView: YXCollectionView): YXLayoutAttributes[] {
|
||||
return this.visibleElementsInRect(rect, collectionView)
|
||||
}
|
||||
|
||||
/**
|
||||
* 抽出来一个方法用来优化列表性能
|
||||
* 在优化之前,可以先看一下 @see YXLayout.layoutAttributesForElementsInRect 关于返回值的说明
|
||||
*/
|
||||
protected visibleElementsInRect(rect: math.Rect, collectionView: YXCollectionView) {
|
||||
if (this.attributes.length <= 100) { return this.attributes } // 少量数据就不查了,直接返回全部
|
||||
|
||||
// 根据当前范围直接计算出一个区间
|
||||
const startRow = Math.floor(rect.y / (this.itemSize.height + this.verticalSpacing))
|
||||
const endRow = Math.ceil(rect.yMax / (this.itemSize.height + this.verticalSpacing))
|
||||
|
||||
// 计算每行最多可以放多少个节点
|
||||
let num = this.getMaxItemsPerRow(collectionView)
|
||||
|
||||
// 计算索引区间
|
||||
const startIdx = startRow * num
|
||||
const endIdx = endRow * num
|
||||
|
||||
// 只返回区间节点的布局属性
|
||||
return this.attributes.slice(startIdx, endIdx)
|
||||
}
|
||||
}
|
||||
|
9
list-3x/assets/lib/grid-layout.ts.meta
Normal file
9
list-3x/assets/lib/grid-layout.ts.meta
Normal file
@ -0,0 +1,9 @@
|
||||
{
|
||||
"ver": "4.0.23",
|
||||
"importer": "typescript",
|
||||
"imported": true,
|
||||
"uuid": "02a96aac-8c52-4201-b8af-c7ed49aae6d4",
|
||||
"files": [],
|
||||
"subMetas": {},
|
||||
"userData": {}
|
||||
}
|
@ -296,6 +296,24 @@ class _scroll_view extends ScrollView {
|
||||
}
|
||||
}
|
||||
|
||||
class _yx_node_pool extends NodePool {
|
||||
getAtIdx(indexPath: YXIndexPath, ...args: any[]): Node | null {
|
||||
const nodes: Node[] = this['_pool']
|
||||
for (let index = 0; index < nodes.length; index++) {
|
||||
const obj = nodes[index];
|
||||
let comp = obj.getComponent(_yx_node_element_comp)
|
||||
if (comp && comp.attributes.indexPath.equals(indexPath)) {
|
||||
nodes.splice(index, 1)
|
||||
// @ts-ignore
|
||||
const handler = this.poolHandlerComp ? obj.getComponent(this.poolHandlerComp) : null;
|
||||
if (handler && handler.reuse) { handler.reuse(arguments); }
|
||||
return obj
|
||||
}
|
||||
}
|
||||
return null
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* 节点的布局属性
|
||||
*/
|
||||
@ -641,7 +659,7 @@ export class YXCollectionView extends Component {
|
||||
registerCell(identifier: string, maker: () => Node, poolComp: (new (...args: any[]) => YXCollectionViewCell) | string | null = null) {
|
||||
let elementCategory: typeof YXLayoutAttributes.prototype.elementCategory = 'Cell'
|
||||
identifier = elementCategory + identifier
|
||||
let pool = new NodePool(poolComp)
|
||||
let pool = new _yx_node_pool(poolComp)
|
||||
this.pools.set(identifier, pool)
|
||||
this.makers.set(identifier, maker)
|
||||
}
|
||||
@ -652,7 +670,7 @@ export class YXCollectionView extends Component {
|
||||
registerSupplementary(identifier: string, maker: () => Node, poolComp: (new (...args: any[]) => YXCollectionViewCell) | string | null = null) {
|
||||
let elementCategory: typeof YXLayoutAttributes.prototype.elementCategory = 'Supplementary'
|
||||
identifier = elementCategory + identifier
|
||||
let pool = new NodePool(poolComp)
|
||||
let pool = new _yx_node_pool(poolComp)
|
||||
this.pools.set(identifier, pool)
|
||||
this.makers.set(identifier, maker)
|
||||
}
|
||||
@ -670,19 +688,21 @@ export class YXCollectionView extends Component {
|
||||
/**
|
||||
* 通过标识符从重用池里取出一个可用的 cell 节点
|
||||
* @param identifier 注册时候的标识符
|
||||
* @param indexPath 可选,尝试通过 indexPath 获取刷新之前的节点 (尽可能的保证刷新前和刷新后这个位置仍然是同一个节点),避免节点复用导致的刷新闪烁问题
|
||||
*/
|
||||
dequeueReusableCell(identifier: string): Node {
|
||||
return this._dequeueReusableElement(identifier, 'Cell')
|
||||
dequeueReusableCell(identifier: string, indexPath: YXIndexPath = null): Node {
|
||||
return this._dequeueReusableElement(identifier, 'Cell', indexPath)
|
||||
}
|
||||
|
||||
/**
|
||||
* 通过标识符从重用池里取出一个可用的 supplementary 节点
|
||||
* @param identifier 注册时候的标识符
|
||||
* @param indexPath 可选,尝试通过 indexPath 获取刷新之前的节点 (尽可能的保证刷新前和刷新后这个位置仍然是同一个节点),避免节点复用导致的刷新闪烁问题
|
||||
*/
|
||||
dequeueReusableSupplementary(identifier: string): Node {
|
||||
return this._dequeueReusableElement(identifier, 'Supplementary')
|
||||
dequeueReusableSupplementary(identifier: string, indexPath: YXIndexPath = null): Node {
|
||||
return this._dequeueReusableElement(identifier, 'Supplementary', indexPath)
|
||||
}
|
||||
private _dequeueReusableElement(identifier: string, elementCategory: typeof YXLayoutAttributes.prototype.elementCategory) {
|
||||
private _dequeueReusableElement(identifier: string, elementCategory: typeof YXLayoutAttributes.prototype.elementCategory, indexPath: YXIndexPath = null) {
|
||||
identifier = elementCategory + identifier
|
||||
let pool = this.pools.get(identifier)
|
||||
if (pool == null) {
|
||||
@ -690,6 +710,11 @@ export class YXCollectionView extends Component {
|
||||
}
|
||||
let result: Node = null
|
||||
|
||||
// 尝试从重用池获取 (牺牲一点性能,尝试通过 indexPath 获取对应的节点,防止刷新闪烁的问题)
|
||||
if (result == null && indexPath && pool instanceof _yx_node_pool) {
|
||||
result = pool.getAtIdx(indexPath)
|
||||
}
|
||||
|
||||
// 尝试从重用池获取
|
||||
if (result == null) {
|
||||
result = pool.get()
|
||||
|
Loading…
x
Reference in New Issue
Block a user