先要实现一个能够循环滚动的list
需要解决的问题
- 滚动的方式怎么做?监听 touchmove来 实时改position?
- 如何实现循环滚动?
先要实现一个能够循环滚动的list
需要解决的问题
扩展一下引擎的scrollview即可
import { Node, ScrollView, v3, _decorator, UITransform, isValid } from ‘cc’;
import { BaseView } from ‘./…/…/rxjs/cc3/BaseView’;
import { CCBindScrollView } from ‘…/…/rxjs/cc3/BindScrollView’;
import { forkJoin, Observable, of } from ‘rxjs’;
import { optimizeScrollView } from ‘./OptimizeScrollView’;
const { ccclass, property } = _decorator;
/**
*/
@ccclass
export default class LeanDynamicScrollView extends BaseView {
@property(ScrollView)
scrollView: ScrollView = null;
@property(Node)
contentNode: Node = null;
@property
itemSize: number = 0;
@property
headLength: number = 0;
@property
tailLength: number = 0;
@property
feelingOptimized: boolean = true;
currentItems: Record<number, Node> = {};
isGenerated: boolean = false;
onLoadView() {
this.feelingOptimized && optimizeScrollView(this.scrollView);
}
onDestroyView() {
}
useObserves() {
return [
CCBindScrollView(this.scrollView).subscribe(_ => {
if (_.state == 'scrolling') this.scrollViewScrolling();
})
]
}
scrollViewScrolling() {
if (!this.isOpen) return;
// 检查该回收和新显示的节点
let _tempCurrentRow: Record<number, Node> = {};
for (let key in this.currentItems) {
if (this.idxNeedShow(Number(key))) {
_tempCurrentRow[key] = this.currentItems[key];
} else {
this.recycleRow(Number(key));
}
}
this.currentItems = _tempCurrentRow;
this.traverseNeedShow(idx => {
if (this.currentItems[idx] == null) {
this.genRow(idx);
}
});
}
isOpen: boolean = false;
// 开启列表,开始生成项
openScrollView() {
if (!this.isGenerated) {
this.generateRows();
this.isOpen = true;
}
}
// 关闭列表,删除列表项(注意是异步可观察)
closeScrollViewObservable() {
this.isOpen = false;
return this.recycleAllObservable();
}
// 继承后覆盖,给出列表总大小
getDataCount() {
return 1;
}
// 对象池相关,需要重写。回收container所有的item节点
putAllChildren(container: Node) {
container.destroyAllChildren();
}
// 对象池相关,需要重写。从池中获得一个Item节点
getItemNode(): Node {
return new Node();
}
// 对象池相关,需要重写。向池中返还一个Item节点
putItemNode(node: Node) {
node.destroy();
}
generateRows() {
this.traverseNeedShow((idx, order) => {
this.genRow(idx, order);
});
if (this.scrollView.vertical && !this.scrollView.horizontal) {
let contentHeight = this.headLength + this.itemSize * this.getDataCount() + this.tailLength;
this.contentNode.getComponent(UITransform).height = contentHeight;
} else if (!this.scrollView.vertical && this.scrollView.horizontal) {
let contentWidth = this.headLength + this.itemSize * this.getDataCount() + this.tailLength;
this.contentNode.getComponent(UITransform).width = contentWidth;
} else {
this.warn('不支持同时两个轴向的运动');
}
this.isGenerated = true;
}
getFirstNeedShowProgress() {
if (this.scrollView.vertical && !this.scrollView.horizontal) {
let start_p = this.scrollView.getComponent(UITransform).height / 2 + this.headLength;
let diff = this.contentNode.position.y - start_p;
let idx = Math.floor(diff / this.itemSize);
return idx;
} else if (!this.scrollView.vertical && this.scrollView.horizontal) {
let start_p = -this.scrollView.getComponent(UITransform).width / 2 - this.headLength;
let diff = -this.contentNode.position.x + start_p;
let idx = Math.floor(diff / this.itemSize);
return idx;
}
return null;
}
getLastNeedShowProgress() {
if (this.scrollView.vertical && !this.scrollView.horizontal) {
let end_p = -this.scrollView.getComponent(UITransform).height / 2 + this.headLength;
let diff = this.contentNode.position.y - end_p;
let idx = Math.floor(diff / this.itemSize);
return idx;
} else if (!this.scrollView.vertical && this.scrollView.horizontal) {
let end_p = this.scrollView.getComponent(UITransform).width / 2 - this.headLength;
let diff = -this.contentNode.position.x + end_p;
let idx = Math.floor(diff / this.itemSize);
return idx;
}
}
/** 遍历需要显示的节点的progress */
traverseNeedShow(call: (idx: number, order: number) => void) {
let firstIdx = this.getFirstNeedShowProgress();
let lastIdx = this.getLastNeedShowProgress();
let order = 0;
for (let idx = firstIdx; idx <= lastIdx; idx++) {
if (idx >= 0 && idx < this.getDataCount()) {
call(idx, order);
order++;
}
}
}
traverseRNeedShow(call: (idx: number, order: number) => void) {
let firstIdx = this.getFirstNeedShowProgress();
let lastIdx = this.getLastNeedShowProgress();
let order = 0;
for (let idx = lastIdx; idx >= firstIdx; idx--) {
if (idx >= 0 && idx < this.getDataCount()) {
call(idx, order);
order++;
}
}
}
idxNeedShow(idx: number): boolean {
let firstIdx = this.getFirstNeedShowProgress();
let lastIdx = this.getLastNeedShowProgress();
if (idx >= firstIdx && idx <= lastIdx) {
if (idx >= 0 && idx <= this.getDataCount()) {
return true;
}
}
return false;
}
// 项的中点对齐
yPosByProgress(idx: number) {
return -this.headLength - (idx + .5) * this.itemSize;
}
// 项的中点对齐
xPosByProgress(idx: number) {
return this.headLength + (idx + .5) * this.itemSize;
}
refreshShowed() {
this.traverseNeedShow(idx => {
if (this.currentItems[idx] == null) {
this.genRow(idx);
} else {
this.refreshRow(idx);
}
});
}
clear() {
this.putAllChildren(this.contentNode);
this.currentItems = {};
this.isGenerated = false;
}
genRow(idx: number, order: number = 0) {
let node = this.getItemNode();
if (this.scrollView.vertical && !this.scrollView.horizontal) {
node.setPosition(v3(0, this.yPosByProgress(idx), 0));
} else if (!this.scrollView.vertical && this.scrollView.horizontal) {
node.setPosition(v3(this.xPosByProgress(idx), 0, 0));
}
node.setParent(this.contentNode);
this.currentItems[idx] = node;
this.refreshRow(idx);
this.itemIn(idx, order);
}
recycleRow(idx: number) {
let node = this.currentItems[idx];
delete this.currentItems[idx];
this.putItemNode(node);
}
recycleRowObservable(idx: number, order: number) {
return new Observable(_ => {
this.itemOut(idx, order, () => {
if (isValid(this?.node) && this.currentItems != null) { // challenge 页面项动画的加强 MrZ 28751 TypeError
let node = this.currentItems[idx]; // err 这里可能为空 frontjs
delete this.currentItems[idx];
this.putItemNode(node);
_.next(null);
_.complete();
}
});
});
}
recycleAllObservable() {
let observables: Observable<any>[] = [];
this.traverseRNeedShow((idx, order) => {
if (this.currentItems[idx] == null) {
} else {
observables.push(this.recycleRowObservable(idx, order));
}
});
if (observables.length == 0) return of(null);
return forkJoin(observables);
}
itemIn(idx: number, order: number) { }
itemOut(idx: number, order: number, recycleCallback: () => void) { recycleCallback(); }
// 继承后覆盖这个方法,刷新item
refreshRow(idx: number) {
}
}
先赞后看 
代码看的有点头疼,有大概的思路么?比如说用 循环滚动的大概思路是啥啊?
1.scrollview里面content的layout关上,要靠自己计算位置,item直接放到正确的位置上
2.计算需要显示的范围,当在范围内时从池中拿出来放上,不在范围内时,放入池中
3.放上时刷新