mirror of
https://github.com/Leo501/CocosCreatorTutorial.git
synced 2026-06-09 00:02:40 +08:00
添加一个自定义ListView 实现, 高效复用机制以及便捷的使用接口
This commit is contained in:
67
ListViewJsDemo/.gitignore
vendored
Normal file
67
ListViewJsDemo/.gitignore
vendored
Normal file
@@ -0,0 +1,67 @@
|
||||
#/////////////////////////////////////////////////////////////////////////////
|
||||
# Fireball Projects
|
||||
#/////////////////////////////////////////////////////////////////////////////
|
||||
|
||||
library/
|
||||
temp/
|
||||
local/
|
||||
build/
|
||||
|
||||
#/////////////////////////////////////////////////////////////////////////////
|
||||
# Logs and databases
|
||||
#/////////////////////////////////////////////////////////////////////////////
|
||||
|
||||
*.log
|
||||
*.sql
|
||||
*.sqlite
|
||||
|
||||
#/////////////////////////////////////////////////////////////////////////////
|
||||
# files for debugger
|
||||
#/////////////////////////////////////////////////////////////////////////////
|
||||
|
||||
*.sln
|
||||
*.csproj
|
||||
*.pidb
|
||||
*.unityproj
|
||||
*.suo
|
||||
|
||||
#/////////////////////////////////////////////////////////////////////////////
|
||||
# OS generated files
|
||||
#/////////////////////////////////////////////////////////////////////////////
|
||||
|
||||
.DS_Store
|
||||
ehthumbs.db
|
||||
Thumbs.db
|
||||
|
||||
#/////////////////////////////////////////////////////////////////////////////
|
||||
# exvim files
|
||||
#/////////////////////////////////////////////////////////////////////////////
|
||||
|
||||
*UnityVS.meta
|
||||
*.err
|
||||
*.err.meta
|
||||
*.exvim
|
||||
*.exvim.meta
|
||||
*.vimentry
|
||||
*.vimentry.meta
|
||||
*.vimproject
|
||||
*.vimproject.meta
|
||||
.vimfiles.*/
|
||||
.exvim.*/
|
||||
quick_gen_project_*_autogen.bat
|
||||
quick_gen_project_*_autogen.bat.meta
|
||||
quick_gen_project_*_autogen.sh
|
||||
quick_gen_project_*_autogen.sh.meta
|
||||
.exvim.app
|
||||
|
||||
#/////////////////////////////////////////////////////////////////////////////
|
||||
# webstorm files
|
||||
#/////////////////////////////////////////////////////////////////////////////
|
||||
|
||||
.idea/
|
||||
|
||||
#//////////////////////////
|
||||
# VS Code
|
||||
#//////////////////////////
|
||||
|
||||
.vscode/
|
||||
3
ListViewJsDemo/README.md
Normal file
3
ListViewJsDemo/README.md
Normal file
@@ -0,0 +1,3 @@
|
||||
# ListView Demo
|
||||
一个自定义ListView 实现, 高效复用机制以及便捷的使用接口
|
||||
链接:http://forum.cocos.com/t/listview/67396
|
||||
232
ListViewJsDemo/assets/ListItem.prefab
Normal file
232
ListViewJsDemo/assets/ListItem.prefab
Normal file
@@ -0,0 +1,232 @@
|
||||
[
|
||||
{
|
||||
"__type__": "cc.Prefab",
|
||||
"_name": "",
|
||||
"_objFlags": 0,
|
||||
"_native": "",
|
||||
"data": {
|
||||
"__id__": 1
|
||||
},
|
||||
"optimizationPolicy": 0,
|
||||
"asyncLoadAssets": false
|
||||
},
|
||||
{
|
||||
"__type__": "cc.Node",
|
||||
"_name": "ListItem",
|
||||
"_objFlags": 0,
|
||||
"_parent": null,
|
||||
"_children": [
|
||||
{
|
||||
"__id__": 2
|
||||
}
|
||||
],
|
||||
"_active": true,
|
||||
"_level": 1,
|
||||
"_components": [
|
||||
{
|
||||
"__id__": 5
|
||||
},
|
||||
{
|
||||
"__id__": 6
|
||||
}
|
||||
],
|
||||
"_prefab": {
|
||||
"__id__": 7
|
||||
},
|
||||
"_opacity": 255,
|
||||
"_color": {
|
||||
"__type__": "cc.Color",
|
||||
"r": 119,
|
||||
"g": 119,
|
||||
"b": 119,
|
||||
"a": 255
|
||||
},
|
||||
"_contentSize": {
|
||||
"__type__": "cc.Size",
|
||||
"width": 500,
|
||||
"height": 100
|
||||
},
|
||||
"_anchorPoint": {
|
||||
"__type__": "cc.Vec2",
|
||||
"x": 0.5,
|
||||
"y": 0.5
|
||||
},
|
||||
"_position": {
|
||||
"__type__": "cc.Vec3",
|
||||
"x": 0,
|
||||
"y": 0,
|
||||
"z": 0
|
||||
},
|
||||
"_scale": {
|
||||
"__type__": "cc.Vec3",
|
||||
"x": 1,
|
||||
"y": 1,
|
||||
"z": 1
|
||||
},
|
||||
"_rotationX": 0,
|
||||
"_rotationY": 0,
|
||||
"_quat": {
|
||||
"__type__": "cc.Quat",
|
||||
"x": 0,
|
||||
"y": 0,
|
||||
"z": 0,
|
||||
"w": 1
|
||||
},
|
||||
"_skewX": 0,
|
||||
"_skewY": 0,
|
||||
"_zIndex": 0,
|
||||
"groupIndex": 0,
|
||||
"_id": ""
|
||||
},
|
||||
{
|
||||
"__type__": "cc.Node",
|
||||
"_name": "New Label",
|
||||
"_objFlags": 0,
|
||||
"_parent": {
|
||||
"__id__": 1
|
||||
},
|
||||
"_children": [],
|
||||
"_active": true,
|
||||
"_level": 3,
|
||||
"_components": [
|
||||
{
|
||||
"__id__": 3
|
||||
}
|
||||
],
|
||||
"_prefab": {
|
||||
"__id__": 4
|
||||
},
|
||||
"_opacity": 255,
|
||||
"_color": {
|
||||
"__type__": "cc.Color",
|
||||
"r": 255,
|
||||
"g": 255,
|
||||
"b": 255,
|
||||
"a": 255
|
||||
},
|
||||
"_contentSize": {
|
||||
"__type__": "cc.Size",
|
||||
"width": 22.25,
|
||||
"height": 40
|
||||
},
|
||||
"_anchorPoint": {
|
||||
"__type__": "cc.Vec2",
|
||||
"x": 0.5,
|
||||
"y": 0.5
|
||||
},
|
||||
"_position": {
|
||||
"__type__": "cc.Vec3",
|
||||
"x": 0,
|
||||
"y": 0,
|
||||
"z": 0
|
||||
},
|
||||
"_scale": {
|
||||
"__type__": "cc.Vec3",
|
||||
"x": 1,
|
||||
"y": 1,
|
||||
"z": 1
|
||||
},
|
||||
"_rotationX": 0,
|
||||
"_rotationY": 0,
|
||||
"_quat": {
|
||||
"__type__": "cc.Quat",
|
||||
"x": 0,
|
||||
"y": 0,
|
||||
"z": 0,
|
||||
"w": 1
|
||||
},
|
||||
"_skewX": 0,
|
||||
"_skewY": 0,
|
||||
"_zIndex": 0,
|
||||
"groupIndex": 0,
|
||||
"_id": ""
|
||||
},
|
||||
{
|
||||
"__type__": "cc.Label",
|
||||
"_name": "",
|
||||
"_objFlags": 0,
|
||||
"node": {
|
||||
"__id__": 2
|
||||
},
|
||||
"_enabled": true,
|
||||
"_srcBlendFactor": 1,
|
||||
"_dstBlendFactor": 771,
|
||||
"_useOriginalSize": false,
|
||||
"_string": "1",
|
||||
"_N$string": "1",
|
||||
"_fontSize": 40,
|
||||
"_lineHeight": 40,
|
||||
"_enableWrapText": true,
|
||||
"_N$file": null,
|
||||
"_isSystemFontUsed": true,
|
||||
"_spacingX": 0,
|
||||
"_N$horizontalAlign": 1,
|
||||
"_N$verticalAlign": 1,
|
||||
"_N$fontFamily": "Arial",
|
||||
"_N$overflow": 0,
|
||||
"_id": "0fTgkeyUtAlbUEzXQuVmWC"
|
||||
},
|
||||
{
|
||||
"__type__": "cc.PrefabInfo",
|
||||
"root": {
|
||||
"__id__": 1
|
||||
},
|
||||
"asset": {
|
||||
"__uuid__": "ee8a03a3-1f23-464b-abf1-1800df851e7e"
|
||||
},
|
||||
"fileId": "e4vYmRcZxFKJbFCJ7l482h",
|
||||
"sync": false
|
||||
},
|
||||
{
|
||||
"__type__": "cc.Sprite",
|
||||
"_name": "",
|
||||
"_objFlags": 0,
|
||||
"node": {
|
||||
"__id__": 1
|
||||
},
|
||||
"_enabled": true,
|
||||
"_srcBlendFactor": 770,
|
||||
"_dstBlendFactor": 771,
|
||||
"_spriteFrame": {
|
||||
"__uuid__": "a23235d1-15db-4b95-8439-a2e005bfff91"
|
||||
},
|
||||
"_type": 0,
|
||||
"_sizeMode": 0,
|
||||
"_fillType": 0,
|
||||
"_fillCenter": {
|
||||
"__type__": "cc.Vec2",
|
||||
"x": 0,
|
||||
"y": 0
|
||||
},
|
||||
"_fillStart": 0,
|
||||
"_fillRange": 0,
|
||||
"_isTrimmedMode": true,
|
||||
"_state": 0,
|
||||
"_atlas": null,
|
||||
"_id": "denu30qbJDfI60nITKiTWW"
|
||||
},
|
||||
{
|
||||
"__type__": "f139d5DxZVMbZa4VmNhI06c",
|
||||
"_name": "",
|
||||
"_objFlags": 0,
|
||||
"node": {
|
||||
"__id__": 1
|
||||
},
|
||||
"_enabled": true,
|
||||
"label": {
|
||||
"__id__": 3
|
||||
},
|
||||
"_id": "71I0xMGeFKrrdvrFDaEv0A"
|
||||
},
|
||||
{
|
||||
"__type__": "cc.PrefabInfo",
|
||||
"root": {
|
||||
"__id__": 1
|
||||
},
|
||||
"asset": {
|
||||
"__uuid__": "ee8a03a3-1f23-464b-abf1-1800df851e7e"
|
||||
},
|
||||
"fileId": "2fpXAiOq1H+b1tE/oO5pnV",
|
||||
"sync": false
|
||||
}
|
||||
]
|
||||
7
ListViewJsDemo/assets/ListItem.prefab.meta
Normal file
7
ListViewJsDemo/assets/ListItem.prefab.meta
Normal file
@@ -0,0 +1,7 @@
|
||||
{
|
||||
"ver": "1.0.0",
|
||||
"uuid": "ee8a03a3-1f23-464b-abf1-1800df851e7e",
|
||||
"optimizationPolicy": "AUTO",
|
||||
"asyncLoadAssets": false,
|
||||
"subMetas": {}
|
||||
}
|
||||
6
ListViewJsDemo/assets/Scene.meta
Normal file
6
ListViewJsDemo/assets/Scene.meta
Normal file
@@ -0,0 +1,6 @@
|
||||
{
|
||||
"ver": "1.0.1",
|
||||
"uuid": "29f52784-2fca-467b-92e7-8fd9ef8c57b7",
|
||||
"isGroup": false,
|
||||
"subMetas": {}
|
||||
}
|
||||
1009
ListViewJsDemo/assets/Scene/helloworld.fire
Normal file
1009
ListViewJsDemo/assets/Scene/helloworld.fire
Normal file
File diff suppressed because it is too large
Load Diff
7
ListViewJsDemo/assets/Scene/helloworld.fire.meta
Normal file
7
ListViewJsDemo/assets/Scene/helloworld.fire.meta
Normal file
@@ -0,0 +1,7 @@
|
||||
{
|
||||
"ver": "1.0.0",
|
||||
"uuid": "2d2f792f-a40c-49bb-a189-ed176a246e49",
|
||||
"asyncLoadAssets": false,
|
||||
"autoReleaseAssets": false,
|
||||
"subMetas": {}
|
||||
}
|
||||
6
ListViewJsDemo/assets/Script.meta
Normal file
6
ListViewJsDemo/assets/Script.meta
Normal file
@@ -0,0 +1,6 @@
|
||||
{
|
||||
"ver": "1.0.1",
|
||||
"uuid": "4734c20c-0db8-4eb2-92ea-e692f4d70934",
|
||||
"isGroup": false,
|
||||
"subMetas": {}
|
||||
}
|
||||
24
ListViewJsDemo/assets/Script/HelloWorld.js
Normal file
24
ListViewJsDemo/assets/Script/HelloWorld.js
Normal file
@@ -0,0 +1,24 @@
|
||||
import ListView, {AbsAdapter} from "./ListView";
|
||||
|
||||
const ListAdapter = require('./ListAdapter');
|
||||
|
||||
cc.Class({
|
||||
extends: cc.Component,
|
||||
|
||||
properties: {
|
||||
listView: {
|
||||
default: null,
|
||||
type: ListView
|
||||
},
|
||||
tipLabel: {
|
||||
type: cc.Label,
|
||||
default: null
|
||||
}
|
||||
},
|
||||
|
||||
start() {
|
||||
const adapter = new ListAdapter();
|
||||
adapter.setDataSet([1, 2, 3, 4, 5, 6, 7, 8, 89, 9, 12, 1243, 45, 564, 6756, 876, 7988, 789, 78987, 978, 45, 6732, 423, 42]);
|
||||
this.listView.setAdapter(adapter);
|
||||
}
|
||||
});
|
||||
9
ListViewJsDemo/assets/Script/HelloWorld.js.meta
Normal file
9
ListViewJsDemo/assets/Script/HelloWorld.js.meta
Normal file
@@ -0,0 +1,9 @@
|
||||
{
|
||||
"ver": "1.0.5",
|
||||
"uuid": "280c3aec-6492-4a9d-9f51-a9b00b570b4a",
|
||||
"isPlugin": false,
|
||||
"loadPluginInWeb": true,
|
||||
"loadPluginInNative": true,
|
||||
"loadPluginInEditor": false,
|
||||
"subMetas": {}
|
||||
}
|
||||
13
ListViewJsDemo/assets/Script/ListAdapter.js
Normal file
13
ListViewJsDemo/assets/Script/ListAdapter.js
Normal file
@@ -0,0 +1,13 @@
|
||||
import {AbsAdapter} from "./ListView";
|
||||
|
||||
const ListItem = require('./ListItem');
|
||||
|
||||
cc.Class({
|
||||
extends: AbsAdapter,
|
||||
updateView(item, posIndex) {
|
||||
let comp = item.getComponent(ListItem);
|
||||
if (comp) {
|
||||
comp.setData(this.getItem(posIndex));
|
||||
}
|
||||
}
|
||||
})
|
||||
9
ListViewJsDemo/assets/Script/ListAdapter.js.meta
Normal file
9
ListViewJsDemo/assets/Script/ListAdapter.js.meta
Normal file
@@ -0,0 +1,9 @@
|
||||
{
|
||||
"ver": "1.0.5",
|
||||
"uuid": "9ec2acad-d240-4e35-9106-069a09c2f73d",
|
||||
"isPlugin": false,
|
||||
"loadPluginInWeb": true,
|
||||
"loadPluginInNative": true,
|
||||
"loadPluginInEditor": false,
|
||||
"subMetas": {}
|
||||
}
|
||||
13
ListViewJsDemo/assets/Script/ListItem.js
Normal file
13
ListViewJsDemo/assets/Script/ListItem.js
Normal file
@@ -0,0 +1,13 @@
|
||||
cc.Class({
|
||||
extends: cc.Component,
|
||||
properties: {
|
||||
label: {
|
||||
default: null,
|
||||
type: cc.Label
|
||||
}
|
||||
},
|
||||
|
||||
setData(data) {
|
||||
this.label.string = `${data}`;
|
||||
}
|
||||
});
|
||||
9
ListViewJsDemo/assets/Script/ListItem.js.meta
Normal file
9
ListViewJsDemo/assets/Script/ListItem.js.meta
Normal file
@@ -0,0 +1,9 @@
|
||||
{
|
||||
"ver": "1.0.5",
|
||||
"uuid": "f139de43-c595-4c6d-96b8-566361234e9c",
|
||||
"isPlugin": false,
|
||||
"loadPluginInWeb": true,
|
||||
"loadPluginInNative": true,
|
||||
"loadPluginInEditor": false,
|
||||
"subMetas": {}
|
||||
}
|
||||
321
ListViewJsDemo/assets/Script/ListView.ts
Normal file
321
ListViewJsDemo/assets/Script/ListView.ts
Normal file
@@ -0,0 +1,321 @@
|
||||
const {ccclass, property} = cc._decorator;
|
||||
|
||||
@ccclass
|
||||
export default class ListView extends cc.Component {
|
||||
|
||||
@property(cc.Prefab)
|
||||
private itemTemplate: cc.Prefab = null;
|
||||
|
||||
@property
|
||||
private spacing: number = 1;
|
||||
|
||||
// 比可见元素多缓存3个, 缓存越多,快速滑动越流畅,但同时初始化越慢.
|
||||
@property
|
||||
private spawnCount: number = 2;
|
||||
|
||||
@property(cc.ScrollView)
|
||||
private scrollView: cc.ScrollView = null;
|
||||
|
||||
private content: cc.Node = null;
|
||||
|
||||
private adapter: AbsAdapter = null;
|
||||
|
||||
private readonly _items: cc.NodePool = new cc.NodePool();
|
||||
// 记录当前填充在树上的索引. 用来快速查找哪些位置缺少item了.
|
||||
private readonly _filledIds: { [key: number]: number } = {};
|
||||
|
||||
private horizontal: boolean = false;
|
||||
|
||||
// 初始时即计算item的高度.因为布局时要用到.
|
||||
private _itemHeight: number = 1;
|
||||
|
||||
private _itemWidth: number = 1;
|
||||
|
||||
private _itemsVisible: number = 1;
|
||||
|
||||
private lastStartIndex: number = -1;
|
||||
|
||||
private scrollTopNotifyed: boolean = false;
|
||||
private scrollBottomNotifyed: boolean = false;
|
||||
|
||||
private pullDownCallback: () => void = null;
|
||||
private pullUpCallback: () => void = null;
|
||||
|
||||
public onLoad() {
|
||||
if (this.scrollView) {
|
||||
this.content = this.scrollView.content;
|
||||
this.horizontal = this.scrollView.horizontal;
|
||||
if (this.horizontal) {
|
||||
this.scrollView.vertical = false
|
||||
this.content.anchorX = 0;
|
||||
this.content.x = this.content.parent.width * this.content.parent.anchorX;
|
||||
} else {
|
||||
this.scrollView.vertical = true;
|
||||
this.content.anchorY = 1;
|
||||
this.content.y = this.content.parent.height * this.content.parent.anchorY;
|
||||
}
|
||||
} else {
|
||||
console.error("ListView need a scrollView for showing.")
|
||||
}
|
||||
let itemOne = this._items.get() || cc.instantiate(this.itemTemplate);
|
||||
this._items.put(itemOne);
|
||||
this._itemHeight = itemOne.height || 10;
|
||||
this._itemWidth = itemOne.width || 10;
|
||||
|
||||
if (this.horizontal) {
|
||||
this._itemsVisible = Math.ceil(this.content.parent.width / this._itemWidth);
|
||||
} else {
|
||||
this._itemsVisible = Math.ceil(this.content.parent.height / this._itemHeight);
|
||||
}
|
||||
console.log("可见区域的item数量为:", this._itemsVisible);
|
||||
this.adjustEvent();
|
||||
}
|
||||
|
||||
public async setAdapter(adapter: AbsAdapter) {
|
||||
this.adapter = adapter;
|
||||
if (this.adapter == null) {
|
||||
console.warn("adapter 为空.")
|
||||
return
|
||||
}
|
||||
if (this.itemTemplate == null) {
|
||||
console.error("Listview 未设置待显示的Item模板.");
|
||||
return;
|
||||
}
|
||||
|
||||
this.notifyUpdate();
|
||||
}
|
||||
|
||||
public getItemIndex(height: number): number {
|
||||
return Math.floor(Math.abs(height / ((this._itemHeight + this.spacing))));
|
||||
}
|
||||
|
||||
public getPositionInView(item: cc.Node) {
|
||||
let worldPos = item.parent.convertToWorldSpaceAR(item.position);
|
||||
let viewPos = this.scrollView.node.convertToNodeSpaceAR(worldPos);
|
||||
return viewPos;
|
||||
}
|
||||
|
||||
// 数据变更了需要进行更新UI显示, 可只更新某一条.
|
||||
public notifyUpdate(updateIndex?: number[]) {
|
||||
if (this.adapter == null) {
|
||||
return;
|
||||
}
|
||||
if (updateIndex && updateIndex.length > 0) {
|
||||
updateIndex.forEach(i => {
|
||||
if (this._filledIds.hasOwnProperty(i)) {
|
||||
delete this._filledIds[i];
|
||||
}
|
||||
})
|
||||
} else {
|
||||
Object.keys(this._filledIds).forEach(key => {
|
||||
delete this._filledIds[key];
|
||||
})
|
||||
}
|
||||
this.lastStartIndex = -1;
|
||||
if (this.horizontal) {
|
||||
this.content.width = this.adapter.getCount() * (this._itemWidth + this.spacing) + this.spacing;
|
||||
} else {
|
||||
this.content.height = this.adapter.getCount() * (this._itemHeight + this.spacing) + this.spacing; // get total content height
|
||||
}
|
||||
this.scrollView.scrollToTop()
|
||||
}
|
||||
|
||||
public scrollToTop(anim: boolean = false) {
|
||||
this.scrollView.scrollToTop(anim ? 1 : 0);
|
||||
}
|
||||
|
||||
public scrollToBottom(anim: boolean = false) {
|
||||
this.scrollView.scrollToBottom(anim ? 1 : 0);
|
||||
}
|
||||
|
||||
public scrollToLeft(anim: boolean = false) {
|
||||
this.scrollView.scrollToLeft(anim ? 1 : 0);
|
||||
}
|
||||
|
||||
public scrollToRight(anim: boolean = false) {
|
||||
this.scrollView.scrollToRight(anim ? 1 : 0);
|
||||
}
|
||||
|
||||
// 下拉事件.
|
||||
public pullDown(callback: () => void, this$: any) {
|
||||
this.pullDownCallback = callback.bind(this$);
|
||||
}
|
||||
|
||||
// 上拉事件.
|
||||
public pullUp(callback: () => void, this$: any) {
|
||||
this.pullUpCallback = callback.bind(this$);
|
||||
}
|
||||
|
||||
protected update(dt) {
|
||||
const startIndex = this.checkNeedUpdate();
|
||||
if (startIndex >= 0) {
|
||||
this.updateView(startIndex);
|
||||
}
|
||||
}
|
||||
|
||||
// 向某位置添加一个item.
|
||||
private _layoutVertical(child: cc.Node, posIndex: number) {
|
||||
this.content.addChild(child);
|
||||
// 增加一个tag 属性用来存储child的位置索引.
|
||||
child["_tag"] = posIndex;
|
||||
this._filledIds[posIndex] = posIndex;
|
||||
child.setPosition(0, -child.height * (0.5 + posIndex) - this.spacing * (posIndex + 1));
|
||||
}
|
||||
|
||||
// 向某位置添加一个item.
|
||||
private _layoutHorizontal(child: cc.Node, posIndex: number) {
|
||||
this.content.addChild(child);
|
||||
// 增加一个tag 属性用来存储child的位置索引.
|
||||
child["_tag"] = posIndex;
|
||||
this._filledIds[posIndex] = posIndex;
|
||||
child.setPosition(-child.width * (0.5 + posIndex) - this.spacing * (posIndex + 1), 0);
|
||||
}
|
||||
|
||||
// 获取可回收item
|
||||
private getRecycleItems(beginIndex: number, endIndex: number): cc.Node[] {
|
||||
const children = this.content.children;
|
||||
const recycles = []
|
||||
children.forEach(item => {
|
||||
if (item["_tag"] < beginIndex || item["_tag"] > endIndex) {
|
||||
recycles.push(item);
|
||||
delete this._filledIds[item["_tag"]];
|
||||
}
|
||||
})
|
||||
return recycles;
|
||||
}
|
||||
|
||||
// 填充View.
|
||||
private updateView(startIndex) {
|
||||
let itemStartIndex = startIndex;
|
||||
// 比实际元素多3个.
|
||||
let itemEndIndex = itemStartIndex + this._itemsVisible + (this.spawnCount || 2);
|
||||
const totalCount = this.adapter.getCount();
|
||||
if (itemStartIndex >= totalCount) {
|
||||
return;
|
||||
}
|
||||
|
||||
if (itemEndIndex > totalCount) {
|
||||
itemEndIndex = totalCount;
|
||||
if (!this.scrollBottomNotifyed) {
|
||||
this.notifyScrollToBottom()
|
||||
this.scrollBottomNotifyed = true;
|
||||
}
|
||||
} else {
|
||||
this.scrollBottomNotifyed = false;
|
||||
}
|
||||
|
||||
// 回收需要回收的元素位置.向上少收一个.向下少收2两.
|
||||
const recyles = this.getRecycleItems(itemStartIndex - (this.spawnCount || 2), itemEndIndex);
|
||||
recyles.forEach(item => {
|
||||
this._items.put(item);
|
||||
})
|
||||
|
||||
// 查找需要更新的元素位置.
|
||||
const updates = this.findUpdateIndex(itemStartIndex, itemEndIndex)
|
||||
|
||||
// 更新相应位置.
|
||||
for (let index of updates) {
|
||||
let child = this.adapter._getView(this._items.get() || cc.instantiate(this.itemTemplate), index);
|
||||
this.horizontal ?
|
||||
this._layoutHorizontal(child, index) :
|
||||
this._layoutVertical(child, index);
|
||||
}
|
||||
}
|
||||
|
||||
// 检测是否需要更新UI.
|
||||
private checkNeedUpdate(): number {
|
||||
if (this.adapter == null) {
|
||||
return -1
|
||||
}
|
||||
|
||||
let scroll = this.horizontal ? (this.content.x - this.content.parent.width * this.content.parent.anchorX)
|
||||
: (this.content.y - this.content.parent.height * this.content.parent.anchorY);
|
||||
let itemStartIndex = Math.floor(scroll / ((this.horizontal ? this._itemWidth : this._itemHeight) + this.spacing));
|
||||
if (itemStartIndex < 0 && !this.scrollTopNotifyed) {
|
||||
this.notifyScrollToTop();
|
||||
this.scrollTopNotifyed = true;
|
||||
return itemStartIndex;
|
||||
}
|
||||
// 防止重复触发topNotify.仅当首item不可见后才能再次触发
|
||||
if (itemStartIndex > 0) {
|
||||
this.scrollTopNotifyed = false;
|
||||
}
|
||||
|
||||
if (this.lastStartIndex != itemStartIndex) {
|
||||
this.lastStartIndex = itemStartIndex;
|
||||
return itemStartIndex;
|
||||
}
|
||||
|
||||
return -1;
|
||||
}
|
||||
|
||||
// 查找需要补充的元素索引.
|
||||
private findUpdateIndex(itemStartIndex: number, itemEndIndex: number): number[] {
|
||||
const d = [];
|
||||
for (let i = itemStartIndex; i < itemEndIndex; i++) {
|
||||
if (this._filledIds.hasOwnProperty(i)) {
|
||||
continue;
|
||||
}
|
||||
d.push(i);
|
||||
}
|
||||
return d;
|
||||
}
|
||||
|
||||
private notifyScrollToTop() {
|
||||
if (!this.adapter || this.adapter.getCount() <= 0) {
|
||||
return;
|
||||
}
|
||||
if (this.pullDownCallback) {
|
||||
this.pullDownCallback();
|
||||
}
|
||||
}
|
||||
|
||||
private notifyScrollToBottom() {
|
||||
if (!this.adapter || this.adapter.getCount() <= 0) {
|
||||
return;
|
||||
}
|
||||
if (this.pullUpCallback) {
|
||||
this.pullUpCallback();
|
||||
}
|
||||
}
|
||||
|
||||
private adjustEvent() {
|
||||
this.content.on(this.isMobile() ? cc.Node.EventType.TOUCH_END : cc.Node.EventType.MOUSE_UP, () => {
|
||||
this.scrollTopNotifyed = false;
|
||||
this.scrollBottomNotifyed = false;
|
||||
}, this)
|
||||
this.content.on(this.isMobile() ? cc.Node.EventType.TOUCH_CANCEL : cc.Node.EventType.MOUSE_LEAVE, () => {
|
||||
this.scrollTopNotifyed = false;
|
||||
this.scrollBottomNotifyed = false;
|
||||
}, this);
|
||||
}
|
||||
|
||||
private isMobile(): boolean {
|
||||
return (cc.sys.isMobile || cc.sys.platform === cc.sys.WECHAT_GAME || cc.sys.platform === cc.sys.QQ_PLAY)
|
||||
}
|
||||
}
|
||||
|
||||
// 数据绑定的辅助适配器
|
||||
export abstract class AbsAdapter {
|
||||
|
||||
private dataSet: any[] = [];
|
||||
|
||||
public setDataSet(data: any[]) {
|
||||
this.dataSet = data;
|
||||
}
|
||||
|
||||
public getCount(): number {
|
||||
return this.dataSet.length;
|
||||
}
|
||||
|
||||
public getItem(posIndex: number): any {
|
||||
return this.dataSet[posIndex];
|
||||
}
|
||||
|
||||
public _getView(item: cc.Node, posIndex: number): cc.Node {
|
||||
this.updateView(item, posIndex);
|
||||
return item;
|
||||
}
|
||||
|
||||
public abstract updateView(item: cc.Node, posIndex: number);
|
||||
}
|
||||
9
ListViewJsDemo/assets/Script/ListView.ts.meta
Normal file
9
ListViewJsDemo/assets/Script/ListView.ts.meta
Normal file
@@ -0,0 +1,9 @@
|
||||
{
|
||||
"ver": "1.0.5",
|
||||
"uuid": "8a20b952-8dfd-4538-aba8-d30cc2a55f00",
|
||||
"isPlugin": false,
|
||||
"loadPluginInWeb": true,
|
||||
"loadPluginInNative": true,
|
||||
"loadPluginInEditor": false,
|
||||
"subMetas": {}
|
||||
}
|
||||
6
ListViewJsDemo/assets/Texture.meta
Normal file
6
ListViewJsDemo/assets/Texture.meta
Normal file
@@ -0,0 +1,6 @@
|
||||
{
|
||||
"ver": "1.0.1",
|
||||
"uuid": "7b81d4e8-ec84-4716-968d-500ac1d78a54",
|
||||
"isGroup": false,
|
||||
"subMetas": {}
|
||||
}
|
||||
BIN
ListViewJsDemo/assets/Texture/HelloWorld.png
Normal file
BIN
ListViewJsDemo/assets/Texture/HelloWorld.png
Normal file
Binary file not shown.
|
After Width: | Height: | Size: 37 KiB |
31
ListViewJsDemo/assets/Texture/HelloWorld.png.meta
Normal file
31
ListViewJsDemo/assets/Texture/HelloWorld.png.meta
Normal file
@@ -0,0 +1,31 @@
|
||||
{
|
||||
"ver": "2.2.0",
|
||||
"uuid": "6aa0aa6a-ebee-4155-a088-a687a6aadec4",
|
||||
"type": "sprite",
|
||||
"wrapMode": "clamp",
|
||||
"filterMode": "bilinear",
|
||||
"premultiplyAlpha": false,
|
||||
"subMetas": {
|
||||
"HelloWorld": {
|
||||
"ver": "1.0.3",
|
||||
"uuid": "31bc895a-c003-4566-a9f3-2e54ae1c17dc",
|
||||
"rawTextureUuid": "6aa0aa6a-ebee-4155-a088-a687a6aadec4",
|
||||
"trimType": "auto",
|
||||
"trimThreshold": 1,
|
||||
"rotated": false,
|
||||
"offsetX": 0,
|
||||
"offsetY": 0,
|
||||
"trimX": 0,
|
||||
"trimY": 0,
|
||||
"width": 195,
|
||||
"height": 270,
|
||||
"rawWidth": 195,
|
||||
"rawHeight": 270,
|
||||
"borderTop": 0,
|
||||
"borderBottom": 0,
|
||||
"borderLeft": 0,
|
||||
"borderRight": 0,
|
||||
"subMetas": {}
|
||||
}
|
||||
}
|
||||
}
|
||||
BIN
ListViewJsDemo/assets/Texture/singleColor.png
Normal file
BIN
ListViewJsDemo/assets/Texture/singleColor.png
Normal file
Binary file not shown.
|
After Width: | Height: | Size: 17 KiB |
31
ListViewJsDemo/assets/Texture/singleColor.png.meta
Normal file
31
ListViewJsDemo/assets/Texture/singleColor.png.meta
Normal file
@@ -0,0 +1,31 @@
|
||||
{
|
||||
"ver": "2.2.0",
|
||||
"uuid": "a8027877-d8d6-4645-97a0-52d4a0123dba",
|
||||
"type": "sprite",
|
||||
"wrapMode": "clamp",
|
||||
"filterMode": "bilinear",
|
||||
"premultiplyAlpha": false,
|
||||
"subMetas": {
|
||||
"singleColor": {
|
||||
"ver": "1.0.3",
|
||||
"uuid": "410fb916-8721-4663-bab8-34397391ace7",
|
||||
"rawTextureUuid": "a8027877-d8d6-4645-97a0-52d4a0123dba",
|
||||
"trimType": "auto",
|
||||
"trimThreshold": 1,
|
||||
"rotated": false,
|
||||
"offsetX": 0,
|
||||
"offsetY": 0,
|
||||
"trimX": 0,
|
||||
"trimY": 0,
|
||||
"width": 2,
|
||||
"height": 2,
|
||||
"rawWidth": 2,
|
||||
"rawHeight": 2,
|
||||
"borderTop": 0,
|
||||
"borderBottom": 0,
|
||||
"borderLeft": 0,
|
||||
"borderRight": 0,
|
||||
"subMetas": {}
|
||||
}
|
||||
}
|
||||
}
|
||||
7
ListViewJsDemo/assets/resources.meta
Normal file
7
ListViewJsDemo/assets/resources.meta
Normal file
@@ -0,0 +1,7 @@
|
||||
{
|
||||
"ver": "1.0.1",
|
||||
"uuid": "d6a6c9a3-1d26-4236-8c6e-26cbf483984b",
|
||||
"isSubpackage": false,
|
||||
"subpackageName": "",
|
||||
"subMetas": {}
|
||||
}
|
||||
7
ListViewJsDemo/assets/resources/i18n.meta
Normal file
7
ListViewJsDemo/assets/resources/i18n.meta
Normal file
@@ -0,0 +1,7 @@
|
||||
{
|
||||
"ver": "1.0.1",
|
||||
"uuid": "efe47830-b7d6-484d-9036-b37640b0d7bb",
|
||||
"isSubpackage": false,
|
||||
"subpackageName": "",
|
||||
"subMetas": {}
|
||||
}
|
||||
24149
ListViewJsDemo/creator.d.ts
vendored
Normal file
24149
ListViewJsDemo/creator.d.ts
vendored
Normal file
File diff suppressed because it is too large
Load Diff
15
ListViewJsDemo/jsconfig.json
Normal file
15
ListViewJsDemo/jsconfig.json
Normal file
@@ -0,0 +1,15 @@
|
||||
{
|
||||
"compilerOptions": {
|
||||
"target": "es6",
|
||||
"module": "commonjs",
|
||||
"experimentalDecorators": true
|
||||
},
|
||||
"exclude": [
|
||||
"node_modules",
|
||||
".vscode",
|
||||
"library",
|
||||
"local",
|
||||
"settings",
|
||||
"temp"
|
||||
]
|
||||
}
|
||||
4
ListViewJsDemo/project.json
Normal file
4
ListViewJsDemo/project.json
Normal file
@@ -0,0 +1,4 @@
|
||||
{
|
||||
"engine": "cocos2d-html5",
|
||||
"packages": "packages"
|
||||
}
|
||||
13
ListViewJsDemo/settings/builder.json
Normal file
13
ListViewJsDemo/settings/builder.json
Normal file
@@ -0,0 +1,13 @@
|
||||
{
|
||||
"excludeScenes": [],
|
||||
"orientation": {
|
||||
"landscapeLeft": true,
|
||||
"landscapeRight": true,
|
||||
"portrait": false,
|
||||
"upsideDown": false
|
||||
},
|
||||
"packageName": "org.cocos2d.helloworld",
|
||||
"startScene": "2d2f792f-a40c-49bb-a189-ed176a246e49",
|
||||
"title": "hello_world",
|
||||
"webOrientation": "auto"
|
||||
}
|
||||
7
ListViewJsDemo/settings/builder.panel.json
Normal file
7
ListViewJsDemo/settings/builder.panel.json
Normal file
@@ -0,0 +1,7 @@
|
||||
{
|
||||
"excludeScenes": [],
|
||||
"packageName": "org.cocos2d.helloworld",
|
||||
"platform": "web-mobile",
|
||||
"startScene": "2d2f792f-a40c-49bb-a189-ed176a246e49",
|
||||
"title": "HelloWorld"
|
||||
}
|
||||
4
ListViewJsDemo/settings/i18n.json
Normal file
4
ListViewJsDemo/settings/i18n.json
Normal file
@@ -0,0 +1,4 @@
|
||||
{
|
||||
"languages": [],
|
||||
"default_language": ""
|
||||
}
|
||||
28
ListViewJsDemo/settings/project.json
Normal file
28
ListViewJsDemo/settings/project.json
Normal file
@@ -0,0 +1,28 @@
|
||||
{
|
||||
"collision-matrix": [
|
||||
[
|
||||
true
|
||||
]
|
||||
],
|
||||
"excluded-modules": [],
|
||||
"group-list": [
|
||||
"default"
|
||||
],
|
||||
"start-scene": "current",
|
||||
"design-resolution-width": 960,
|
||||
"design-resolution-height": 640,
|
||||
"fit-width": false,
|
||||
"fit-height": true,
|
||||
"use-project-simulator-setting": false,
|
||||
"simulator-orientation": false,
|
||||
"use-customize-simulator": false,
|
||||
"simulator-resolution": {
|
||||
"width": 960,
|
||||
"height": 640
|
||||
},
|
||||
"cocos-analytics": {
|
||||
"enable": false,
|
||||
"appID": "13798",
|
||||
"appSecret": "959b3ac0037d0f3c2fdce94f8421a9b2"
|
||||
}
|
||||
}
|
||||
BIN
ListViewJsDemo/template-banner.png
Normal file
BIN
ListViewJsDemo/template-banner.png
Normal file
Binary file not shown.
|
After Width: | Height: | Size: 23 KiB |
5
ListViewJsDemo/template.json
Normal file
5
ListViewJsDemo/template.json
Normal file
@@ -0,0 +1,5 @@
|
||||
{
|
||||
"name": "TEMPLATES.helloworld.name",
|
||||
"desc": "TEMPLATES.helloworld.desc",
|
||||
"banner": "template-banner.png"
|
||||
}
|
||||
Reference in New Issue
Block a user