mirror of
https://github.com/Leo501/CocosCreatorTutorial.git
synced 2026-05-12 09:05:55 +08:00
837 lines
28 KiB
TypeScript
837 lines
28 KiB
TypeScript
import { LayoutUtil } from "./layout_utils"
|
||
import { ListViewItem } from "./ListViewItem";
|
||
|
||
export class ListViewTs {
|
||
private scrollview: cc.ScrollView;
|
||
private mask: cc.Mask;
|
||
private content: cc.Node;
|
||
private item_tpl: cc.Node;
|
||
private item_pool: ListViewItem[];
|
||
|
||
private dir: number;
|
||
private width: number;
|
||
private height: number;
|
||
private original_width: number;
|
||
private original_height: number;
|
||
private gap_x: number;
|
||
private gap_y: number;
|
||
private padding_left: number;
|
||
private padding_right: number;
|
||
private padding_top: number;
|
||
private padding_bottom: number;
|
||
private item_anchorX: number;
|
||
private item_anchorY: number;
|
||
private row: number;
|
||
private col: number;
|
||
private item_width: number;
|
||
private item_height: number;
|
||
private content_width: number;
|
||
private content_height: number;
|
||
private item_class: string;
|
||
private cb_host: any;
|
||
private scroll_to_end_cb: () => void;
|
||
private auto_scrolling: boolean;
|
||
private packItems: PackItem[];
|
||
private start_index: number;
|
||
private stop_index: number;
|
||
private _selected_index: number = -1;
|
||
private renderDirty: boolean;
|
||
private timer: number;
|
||
|
||
constructor(params: ListViewParams) {
|
||
this.scrollview = params.scrollview;
|
||
this.mask = params.mask;
|
||
this.content = params.content;
|
||
this.item_tpl = params.item_tpl;
|
||
this.item_tpl.active = false;
|
||
this.item_width = this.item_tpl.width;
|
||
this.item_height = this.item_tpl.height;
|
||
this.dir = params.direction || ListViewDir.Vertical;
|
||
this.width = params.width || this.scrollview.node.width;
|
||
this.height = params.height || this.scrollview.node.height;
|
||
this.gap_x = params.gap_x || 0;
|
||
this.gap_y = params.gap_y || 0;
|
||
this.padding_left = params.padding_left || 0;
|
||
this.padding_right = params.padding_right || 0;
|
||
this.padding_top = params.padding_top || 0;
|
||
this.padding_bottom = params.padding_bottom || 0;
|
||
this.item_anchorX = params.item_anchorX != null ? params.item_anchorX : 0;
|
||
this.item_anchorY = params.item_anchorY != null ? params.item_anchorY : 1;
|
||
this.row = params.row || 1;
|
||
this.col = params.column || 1;
|
||
this.cb_host = params.cb_host;
|
||
this.scroll_to_end_cb = params.scroll_to_end_cb;
|
||
this.item_class = params.item_class;
|
||
this.auto_scrolling = params.auto_scrolling || false;
|
||
this.item_pool = [];
|
||
|
||
if (this.dir == ListViewDir.Vertical) {
|
||
const content_width = (this.item_width + this.gap_x) * this.col - this.gap_x + this.padding_left + this.padding_right;
|
||
if (content_width > this.width) {
|
||
cc.log("content_width > width, resize listview to content_width,", this.width, "->", content_width);
|
||
this.width = content_width;
|
||
}
|
||
this.set_content_size(this.width, 0);
|
||
}
|
||
else {
|
||
const content_height = (this.item_height + this.gap_y) * this.row - this.gap_y + this.padding_top + this.padding_bottom;
|
||
if (content_height > this.height) {
|
||
cc.log("content_height > height, resize listview to content_height,", this.height, "->", content_height);
|
||
this.height = content_height;
|
||
}
|
||
this.set_content_size(0, this.height);
|
||
}
|
||
this.original_width = this.width;
|
||
this.original_height = this.height;
|
||
this.mask.node.setContentSize(this.width, this.height);
|
||
this.scrollview.node.setContentSize(this.width, this.height);
|
||
this.scrollview.vertical = this.dir == ListViewDir.Vertical;
|
||
this.scrollview.horizontal = this.dir == ListViewDir.Horizontal;
|
||
this.scrollview.inertia = true;
|
||
this.scrollview.node.on("scrolling", this.on_scrolling, this);
|
||
this.scrollview.node.on("scroll-to-bottom", this.on_scroll_to_end, this);
|
||
this.scrollview.node.on("scroll-to-right", this.on_scroll_to_end, this);
|
||
this.scrollview.node.on(cc.Node.EventType.TOUCH_START, this.on_scroll_touch_start, this);
|
||
this.scrollview.node.on(cc.Node.EventType.TOUCH_END, this.on_scroll_touch_end, this);
|
||
this.scrollview.node.on(cc.Node.EventType.TOUCH_CANCEL, this.on_scroll_touch_cancel, this);
|
||
}
|
||
|
||
private _touchBeganPosition: cc.Vec2;
|
||
private _touchEndPosition: cc.Vec2;
|
||
private on_scroll_touch_start(event: cc.Event.EventTouch) {
|
||
this._touchBeganPosition = cc.v2(event.touch.getLocation().x, event.touch.getLocation().y);
|
||
}
|
||
|
||
private on_scroll_touch_cancel(event: cc.Event.EventTouch) {
|
||
this._touchEndPosition = event.touch.getLocation();
|
||
this.handle_release_logic();
|
||
}
|
||
|
||
private on_scroll_touch_end(event: cc.Event.EventTouch) {
|
||
this._touchEndPosition = event.touch.getLocation();
|
||
this.handle_release_logic();
|
||
}
|
||
|
||
private handle_release_logic() {
|
||
const touchPos = this._touchEndPosition;
|
||
const moveOffset = this._touchBeganPosition.sub(this._touchEndPosition);
|
||
const dragDirection = this.get_drag_direction(moveOffset);
|
||
if (dragDirection != 0) {
|
||
return;
|
||
}
|
||
if (!this.packItems || !this.packItems.length) {
|
||
return;
|
||
}
|
||
//无滑动的情况下点击
|
||
const touchPosInContent = this.content.convertToNodeSpaceAR(touchPos);
|
||
for (let i = this.start_index; i <= this.stop_index; i++) {
|
||
const packItem = this.packItems[i];
|
||
if (packItem && packItem.item && packItem.item.node.getBoundingBox().contains(touchPosInContent)) {
|
||
packItem.item.onTouchEnd(packItem.item.node.convertToNodeSpaceAR(touchPos), packItem.data, i);
|
||
break;
|
||
}
|
||
}
|
||
}
|
||
|
||
private get_drag_direction(moveOffset: cc.Vec2) {
|
||
if (this.dir === ListViewDir.Horizontal) {
|
||
if (Math.abs(moveOffset.x) < 3) { return 0; }
|
||
return (moveOffset.x > 0 ? 1 : -1);
|
||
}
|
||
else if (this.dir === ListViewDir.Vertical) {
|
||
// 由于滚动 Y 轴的原点在在右上角所以应该是小于 0
|
||
if (Math.abs(moveOffset.y) < 3) { return 0; }
|
||
return (moveOffset.y < 0 ? 1 : -1);
|
||
}
|
||
}
|
||
|
||
private on_scroll_to_end() {
|
||
if (this.scroll_to_end_cb) {
|
||
this.scroll_to_end_cb.call(this.cb_host);
|
||
}
|
||
}
|
||
|
||
private last_content_pos: number;
|
||
private on_scrolling() {
|
||
let pos: number;
|
||
let threshold: number;
|
||
if (this.dir == ListViewDir.Vertical) {
|
||
pos = this.content.y;
|
||
threshold = this.item_height;
|
||
}
|
||
else {
|
||
pos = this.content.x;
|
||
threshold = this.item_width;
|
||
}
|
||
if (this.last_content_pos != null && Math.abs(pos - this.last_content_pos) < threshold) {
|
||
return;
|
||
}
|
||
this.last_content_pos = pos;
|
||
this.render();
|
||
}
|
||
|
||
private render() {
|
||
if (!this.packItems || !this.packItems.length) {
|
||
return;
|
||
}
|
||
if (this.dir == ListViewDir.Vertical) {
|
||
let posy = this.content.y;
|
||
// cc.log("onscrolling, content posy=", posy);
|
||
if (posy < 0) {
|
||
posy = 0;
|
||
}
|
||
else if (posy > this.content_height - this.height) {
|
||
posy = this.content_height - this.height;
|
||
}
|
||
let viewport_start = -posy;
|
||
let viewport_stop = viewport_start - this.height;
|
||
// while(this.packItems[start].y - this.item_height > viewport_start)
|
||
// {
|
||
// start++;
|
||
// }
|
||
// while(this.packItems[stop].y < viewport_stop)
|
||
// {
|
||
// stop--;
|
||
// }
|
||
let start = this.indexFromOffset(viewport_start);
|
||
let stop = this.indexFromOffset(viewport_stop);
|
||
|
||
//expand viewport for better experience
|
||
start = Math.max(start - this.col, 0);
|
||
stop = Math.min(this.packItems.length - 1, stop + this.col);
|
||
if (start != this.start_index) {
|
||
this.start_index = start;
|
||
this.renderDirty = true;
|
||
}
|
||
if (stop != this.stop_index) {
|
||
this.stop_index = stop;
|
||
this.renderDirty = true;
|
||
}
|
||
}
|
||
else {
|
||
let posx = this.content.x;
|
||
// cc.log("onscrolling, content posx=", posx);
|
||
if (posx > 0) {
|
||
posx = 0;
|
||
}
|
||
else if (posx < this.width - this.content_width) {
|
||
posx = this.width - this.content_width;
|
||
}
|
||
let viewport_start = -posx;
|
||
let viewport_stop = viewport_start + this.width;
|
||
// while(this.packItems[start].x + this.item_width < viewport_start)
|
||
// {
|
||
// start++;
|
||
// }
|
||
// while(this.packItems[stop].x > viewport_stop)
|
||
// {
|
||
// stop--;
|
||
// }
|
||
let start = this.indexFromOffset(viewport_start);
|
||
let stop = this.indexFromOffset(viewport_stop);
|
||
|
||
//expand viewport for better experience
|
||
start = Math.max(start - this.row, 0);
|
||
stop = Math.min(this.packItems.length - 1, stop + this.row);
|
||
if (start != this.start_index) {
|
||
this.start_index = start;
|
||
this.renderDirty = true;
|
||
}
|
||
if (stop != this.stop_index) {
|
||
this.stop_index = stop;
|
||
this.renderDirty = true;
|
||
}
|
||
}
|
||
}
|
||
|
||
onUpdate() {
|
||
if (this.renderDirty && cc.isValid(this.scrollview.node)) {
|
||
cc.log("listView, render_from:", this.start_index, this.stop_index);
|
||
this.render_items();
|
||
this.renderDirty = false;
|
||
}
|
||
}
|
||
|
||
//不支持多行多列
|
||
private indexFromOffset(offset: number) {
|
||
let low = 0;
|
||
let high = 0;
|
||
let max_idx = 0;
|
||
high = max_idx = this.packItems.length - 1;
|
||
if (this.dir == ListViewDir.Vertical) {
|
||
while (high >= low) {
|
||
const index = low + Math.floor((high - low) / 2);
|
||
const itemStart = this.packItems[index].y;
|
||
const itemStop = index < max_idx ? this.packItems[index + 1].y : -this.content_height;
|
||
if (offset <= itemStart && offset >= itemStop) {
|
||
return index;
|
||
}
|
||
else if (offset > itemStart) {
|
||
high = index - 1;
|
||
}
|
||
else {
|
||
low = index + 1;
|
||
}
|
||
}
|
||
}
|
||
else {
|
||
while (high >= low) {
|
||
const index = low + Math.floor((high - low) / 2);
|
||
const itemStart = this.packItems[index].x;
|
||
const itemStop = index < max_idx ? this.packItems[index + 1].x : this.content_width;
|
||
if (offset >= itemStart && offset <= itemStop) {
|
||
return index;
|
||
}
|
||
else if (offset > itemStart) {
|
||
low = index + 1;
|
||
}
|
||
else {
|
||
high = index - 1;
|
||
}
|
||
}
|
||
}
|
||
return -1;
|
||
}
|
||
|
||
select_data(data) {
|
||
const idx = this.packItems.findIndex(item => item.data == data);
|
||
if (idx != -1) {
|
||
this.select_item(idx);
|
||
}
|
||
}
|
||
|
||
select_item(index: number) {
|
||
if (index == this._selected_index) {
|
||
return;
|
||
}
|
||
if (this._selected_index != -1) {
|
||
this.inner_select_item(this._selected_index, false);
|
||
}
|
||
this._selected_index = index;
|
||
this.inner_select_item(index, true);
|
||
}
|
||
|
||
private inner_select_item(index: number, is_select: boolean) {
|
||
let packItem: PackItem = this.packItems[index];
|
||
if (!packItem) {
|
||
cc.warn("inner_select_item index is out of range{", 0, this.packItems.length - 1, "}", index);
|
||
return;
|
||
}
|
||
packItem.is_select = is_select;
|
||
if (packItem.item) {
|
||
packItem.item.onSetSelect(is_select, index);
|
||
if (is_select) {
|
||
packItem.item.onSelected(packItem.data, index);
|
||
}
|
||
}
|
||
}
|
||
|
||
private spawn_item(index: number): ListViewItem {
|
||
let item: ListViewItem = this.item_pool.pop();
|
||
if (!item) {
|
||
item = cc.instantiate(this.item_tpl).getComponent(this.item_class) as ListViewItem;
|
||
item.node.active = true;
|
||
//仅仅改变父节点锚点,子元素位置不会随之变化
|
||
// item.node.setAnchorPoint(this.item_anchorX, this.item_anchorY);
|
||
LayoutUtil.set_pivot_smart(item.node, this.item_anchorX, this.item_anchorY);
|
||
item.onInit();
|
||
// cc.log("spawn_item", index);
|
||
}
|
||
item.node.parent = this.content;
|
||
return item;
|
||
}
|
||
|
||
private recycle_item(packItem: PackItem) {
|
||
const item = packItem.item;
|
||
if (item && cc.isValid(item.node)) {
|
||
item.onRecycle(packItem.data);
|
||
item.node.removeFromParent();
|
||
this.item_pool.push(item);
|
||
packItem.item = null;
|
||
}
|
||
}
|
||
|
||
private clear_items() {
|
||
if (this.packItems) {
|
||
this.packItems.forEach(packItem => {
|
||
this.recycle_item(packItem);
|
||
});
|
||
}
|
||
}
|
||
|
||
private render_items() {
|
||
let packItem: PackItem;
|
||
for (let i = 0; i < this.start_index; i++) {
|
||
packItem = this.packItems[i];
|
||
if (packItem.item) {
|
||
// cc.log("recycle_item", i);
|
||
this.recycle_item(packItem);
|
||
}
|
||
}
|
||
for (let i = this.packItems.length - 1; i > this.stop_index; i--) {
|
||
packItem = this.packItems[i];
|
||
if (packItem.item) {
|
||
// cc.log("recycle_item", i);
|
||
this.recycle_item(packItem);
|
||
}
|
||
}
|
||
for (let i = this.start_index; i <= this.stop_index; i++) {
|
||
packItem = this.packItems[i];
|
||
if (!packItem.item) {
|
||
// cc.log("render_item", i);
|
||
packItem.item = this.spawn_item(i);
|
||
packItem.item.onSetData(packItem.data, i);
|
||
packItem.item.onSetSelect(packItem.is_select, i);
|
||
if (packItem.is_select) {
|
||
packItem.item.onSelected(packItem.data, i);
|
||
}
|
||
}
|
||
//列表添加与删除时item位置会变化,因此每次render_items都要执行
|
||
// packItem.item.node.setPosition(packItem.x, packItem.y);
|
||
packItem.item.setLeftTop(packItem.x, packItem.y);
|
||
}
|
||
}
|
||
|
||
private pack_item(data: any): PackItem {
|
||
return { x: 0, y: 0, data: data, item: null, is_select: false };
|
||
}
|
||
|
||
private layout_items(start: number) {
|
||
// cc.log("layout_items, start=", start);
|
||
for (let index = start, stop = this.packItems.length; index < stop; index++) {
|
||
const packItem = this.packItems[index];
|
||
if (this.dir == ListViewDir.Vertical) {
|
||
[packItem.x, packItem.y] = LayoutUtil.vertical_layout(index, this.item_width, this.item_height, this.col, this.gap_x, this.gap_y, this.padding_left, this.padding_top);
|
||
}
|
||
else {
|
||
[packItem.x, packItem.y] = LayoutUtil.horizontal_layout(index, this.item_width, this.item_height, this.row, this.gap_x, this.gap_y, this.padding_left, this.padding_top);
|
||
}
|
||
}
|
||
}
|
||
|
||
private adjust_content() {
|
||
if (this.packItems.length <= 0) {
|
||
this.set_content_size(0, 0);
|
||
return;
|
||
}
|
||
const last_packItem = this.packItems[this.packItems.length - 1];
|
||
if (this.dir == ListViewDir.Vertical) {
|
||
const height = Math.max(this.height, this.item_height - last_packItem.y + this.padding_bottom);
|
||
this.set_content_size(this.content_width, height);
|
||
}
|
||
else {
|
||
const width = Math.max(this.width, last_packItem.x + this.item_width + this.padding_right);
|
||
this.set_content_size(width, this.content_height);
|
||
}
|
||
}
|
||
|
||
private set_content_size(width: number, height: number) {
|
||
if (this.content_width != width) {
|
||
this.content_width = width;
|
||
this.content.width = width;
|
||
}
|
||
if (this.content_height != height) {
|
||
this.content_height = height;
|
||
this.content.height = height;
|
||
}
|
||
// cc.log("ListView, set_content_size", width, height, this.content.width, this.content.height);
|
||
}
|
||
|
||
set_viewport(width?: number, height?: number) {
|
||
if (width == null) {
|
||
width = this.width;
|
||
}
|
||
else if (width > this.content_width) {
|
||
width = this.content_width;
|
||
}
|
||
|
||
if (height == null) {
|
||
height = this.height;
|
||
}
|
||
else if (height > this.content_height) {
|
||
height = this.content_height;
|
||
}
|
||
//设置遮罩区域尺寸
|
||
this.width = width;
|
||
this.height = height;
|
||
this.mask.node.setContentSize(width, height);
|
||
this.scrollview.node.setContentSize(width, height);
|
||
this.render();
|
||
}
|
||
|
||
renderAll(value: boolean) {
|
||
let width: number;
|
||
let height: number;
|
||
if (value) {
|
||
width = this.content_width;
|
||
height = this.content_height;
|
||
}
|
||
else {
|
||
width = this.original_width;
|
||
height = this.original_height;
|
||
}
|
||
this.set_viewport(width, height);
|
||
}
|
||
|
||
set_data(datas: any[]) {
|
||
if (this.packItems) {
|
||
this.clear_items();
|
||
this.packItems.length = 0;
|
||
}
|
||
else {
|
||
this.packItems = [];
|
||
}
|
||
datas.forEach(data => {
|
||
let packItem = this.pack_item(data);
|
||
this.packItems.push(packItem);
|
||
});
|
||
this.layout_items(0);
|
||
this.adjust_content();
|
||
this.start_index = -1;
|
||
this.stop_index = -1;
|
||
if (this.dir == ListViewDir.Vertical) {
|
||
this.content.y = 0;
|
||
}
|
||
else {
|
||
this.content.x = 0;
|
||
}
|
||
if (this.packItems.length > 0) {
|
||
this.render();
|
||
}
|
||
}
|
||
|
||
insert_data(index: number, ...datas: any[]) {
|
||
if (datas.length == 0) {
|
||
cc.log("nothing to insert");
|
||
return;
|
||
}
|
||
if (!this.packItems) {
|
||
this.packItems = [];
|
||
}
|
||
if (index < 0 || index > this.packItems.length) {
|
||
cc.warn("insert_data, invalid index", index);
|
||
return;
|
||
}
|
||
let is_append = index == this.packItems.length;
|
||
let packItems: PackItem[] = [];
|
||
datas.forEach(data => {
|
||
let packItem = this.pack_item(data);
|
||
packItems.push(packItem);
|
||
});
|
||
this.packItems.splice(index, 0, ...packItems);
|
||
this.layout_items(index);
|
||
this.adjust_content();
|
||
this.start_index = -1;
|
||
this.stop_index = -1;
|
||
|
||
if (this.auto_scrolling && is_append) {
|
||
this.scroll_to_end();
|
||
}
|
||
this.render();
|
||
}
|
||
|
||
remove_data(index: number, count: number = 1) {
|
||
if (!this.packItems) {
|
||
cc.log("call set_data before call this method");
|
||
return;
|
||
}
|
||
if (index < 0 || index >= this.packItems.length) {
|
||
cc.warn("remove_data, invalid index", index);
|
||
return;
|
||
}
|
||
if (count < 1) {
|
||
cc.log("nothing to remove");
|
||
return;
|
||
}
|
||
let old_length = this.packItems.length;
|
||
let del_items = this.packItems.splice(index, count);
|
||
//回收node
|
||
del_items.forEach(packItem => {
|
||
this.recycle_item(packItem);
|
||
});
|
||
|
||
//重新排序index后面的
|
||
if (index + count < old_length) {
|
||
this.layout_items(index);
|
||
}
|
||
this.adjust_content();
|
||
if (this.packItems.length > 0) {
|
||
this.start_index = -1;
|
||
this.stop_index = -1;
|
||
this.render();
|
||
}
|
||
}
|
||
|
||
append_data(...datas: any[]) {
|
||
if (!this.packItems) {
|
||
this.packItems = [];
|
||
}
|
||
this.insert_data(this.packItems.length, ...datas);
|
||
}
|
||
|
||
scroll_to(index: number, time = 0) {
|
||
if (!this.packItems) {
|
||
return;
|
||
}
|
||
const packItem = this.packItems[index];
|
||
if (!packItem) {
|
||
cc.log("scroll_to, index out of range");
|
||
return;
|
||
}
|
||
if (this.dir == ListViewDir.Vertical) {
|
||
const min_y = this.height - this.content_height;
|
||
if (min_y >= 0) {
|
||
cc.log("no need to scroll");
|
||
return;
|
||
}
|
||
let y = packItem.y;
|
||
if (y < min_y) {
|
||
y = min_y;
|
||
cc.log("content reach bottom");
|
||
}
|
||
const x = this.content.x;
|
||
if (time == 0) {
|
||
this.scrollview.setContentPosition(cc.v2(x, -y));
|
||
}
|
||
else {
|
||
this.scrollview.scrollToOffset(cc.v2(x, -y), time);
|
||
}
|
||
this.render();
|
||
}
|
||
else {
|
||
const max_x = this.content_width - this.width;
|
||
if (max_x <= 0) {
|
||
cc.log("no need to scroll");
|
||
return;
|
||
}
|
||
let x = packItem.x;
|
||
if (x > max_x) {
|
||
x = max_x;
|
||
cc.log("content reach right");
|
||
}
|
||
const y = this.content.y;
|
||
if (time == 0) {
|
||
this.scrollview.setContentPosition(cc.v2(-x, y));
|
||
}
|
||
else {
|
||
this.scrollview.scrollToOffset(cc.v2(-x, y), time);
|
||
}
|
||
this.render();
|
||
}
|
||
}
|
||
|
||
get_scroll_offset() {
|
||
const offset = this.scrollview.getScrollOffset();
|
||
if (this.dir == ListViewDir.Vertical) {
|
||
return offset.y;
|
||
}
|
||
else {
|
||
return offset.x;
|
||
}
|
||
}
|
||
|
||
scroll_to_offset(value: number, time = 0) {
|
||
if (this.dir == ListViewDir.Vertical) {
|
||
const x = this.content.x;
|
||
if (time == 0) {
|
||
this.scrollview.setContentPosition(cc.v2(x, value));
|
||
}
|
||
else {
|
||
this.scrollview.scrollToOffset(cc.v2(x, value), time);
|
||
}
|
||
this.render();
|
||
}
|
||
else {
|
||
const y = this.content.y;
|
||
if (time == 0) {
|
||
this.scrollview.setContentPosition(cc.v2(value, y));
|
||
}
|
||
else {
|
||
this.scrollview.scrollToOffset(cc.v2(value, y), time);
|
||
}
|
||
this.render();
|
||
}
|
||
}
|
||
|
||
scroll_to_end() {
|
||
if (this.dir == ListViewDir.Vertical) {
|
||
this.scrollview.scrollToBottom();
|
||
}
|
||
else {
|
||
this.scrollview.scrollToRight();
|
||
}
|
||
}
|
||
|
||
refresh_item(index: number, data: any) {
|
||
const packItem = this.get_pack_item(index);
|
||
if (!packItem) {
|
||
return;
|
||
}
|
||
const oldData = packItem.data;
|
||
packItem.data = data;
|
||
if (packItem.item) {
|
||
packItem.item.onRecycle(oldData);
|
||
packItem.item.onSetData(data, index);
|
||
}
|
||
}
|
||
|
||
reload_item(index: number) {
|
||
const packItem = this.get_pack_item(index);
|
||
if (packItem && packItem.item) {
|
||
packItem.item.onRecycle(packItem.data);
|
||
packItem.item.onSetData(packItem.data, index);
|
||
}
|
||
}
|
||
|
||
private get_pack_item(index: number) {
|
||
if (!this.packItems) {
|
||
cc.log("call set_data before call this method");
|
||
return null;
|
||
}
|
||
if (index < 0 || index >= this.packItems.length) {
|
||
cc.warn("get_pack_item, invalid index", index);
|
||
return null;
|
||
}
|
||
return this.packItems[index];
|
||
}
|
||
|
||
get_item(index: number) {
|
||
const packItem = this.get_pack_item(index);
|
||
return packItem ? packItem.item : null;
|
||
}
|
||
|
||
get_data(index: number) {
|
||
const packItem = this.get_pack_item(index);
|
||
return packItem ? packItem.data : null;
|
||
}
|
||
|
||
find_item(predicate: (data: any) => boolean) {
|
||
if (!this.packItems || !this.packItems.length) {
|
||
cc.log("call set_data before call this method");
|
||
return null;
|
||
}
|
||
for (let i = this.start_index; i <= this.stop_index; i++) {
|
||
const packItem = this.packItems[i];
|
||
if (predicate(packItem.data)) {
|
||
return packItem.item;
|
||
}
|
||
}
|
||
return null;
|
||
}
|
||
|
||
find_index(predicate: (data: any) => boolean) {
|
||
if (!this.packItems || !this.packItems.length) {
|
||
cc.log("call set_data before call this method");
|
||
return -1;
|
||
}
|
||
return this.packItems.findIndex(packItem => {
|
||
return predicate(packItem.data);
|
||
});
|
||
}
|
||
|
||
get renderedItems() {
|
||
const items: ListViewItem[] = [];
|
||
for (let i = this.start_index; i <= this.stop_index; i++) {
|
||
const packItem = this.packItems[i];
|
||
if (packItem && packItem.item) {
|
||
items.push(packItem.item);
|
||
}
|
||
}
|
||
return items;
|
||
}
|
||
|
||
get length() {
|
||
if (!this.packItems) {
|
||
cc.log("call set_data before call this method");
|
||
return 0;
|
||
}
|
||
return this.packItems.length;
|
||
}
|
||
|
||
destroy() {
|
||
this.clear_items();
|
||
this.item_pool.forEach(item => {
|
||
item.onUnInit();
|
||
item.node.destroy();
|
||
});
|
||
this.item_pool = null;
|
||
this.packItems = null;
|
||
|
||
this.renderDirty = null;
|
||
|
||
if (cc.isValid(this.scrollview.node)) {
|
||
this.scrollview.node.off("scrolling", this.on_scrolling, this);
|
||
this.scrollview.node.off("scroll-to-bottom", this.on_scroll_to_end, this);
|
||
this.scrollview.node.off("scroll-to-right", this.on_scroll_to_end, this);
|
||
this.scrollview.node.off(cc.Node.EventType.TOUCH_START, this.on_scroll_touch_start, this);
|
||
this.scrollview.node.off(cc.Node.EventType.TOUCH_END, this.on_scroll_touch_end, this);
|
||
this.scrollview.node.off(cc.Node.EventType.TOUCH_CANCEL, this.on_scroll_touch_cancel, this);
|
||
}
|
||
}
|
||
|
||
get selected_index(): number {
|
||
return this._selected_index;
|
||
}
|
||
|
||
get selected_data(): any {
|
||
let packItem: PackItem = this.packItems[this._selected_index];
|
||
if (packItem) {
|
||
return packItem.data;
|
||
}
|
||
return null;
|
||
}
|
||
|
||
set scrollable(value: boolean) {
|
||
if (this.dir == ListViewDir.Vertical) {
|
||
this.scrollview.vertical = value;
|
||
}
|
||
else {
|
||
this.scrollview.horizontal = value;
|
||
}
|
||
}
|
||
|
||
get startIndex() {
|
||
return this.start_index;
|
||
}
|
||
|
||
get stopIndex() {
|
||
return this.stop_index;
|
||
}
|
||
}
|
||
|
||
export enum ListViewDir {
|
||
Vertical = 1,
|
||
Horizontal = 2,
|
||
}
|
||
|
||
export type ListViewParams = {
|
||
scrollview: cc.ScrollView;
|
||
mask: cc.Mask;
|
||
content: cc.Node;
|
||
item_tpl: cc.Node;
|
||
item_class: string; //item对应的类型
|
||
direction?: ListViewDir;
|
||
width?: number;
|
||
height?: number;
|
||
gap_x?: number;
|
||
gap_y?: number;
|
||
padding_left?: number;
|
||
padding_right?: number;
|
||
padding_top?: number;
|
||
padding_bottom?: number;
|
||
item_anchorX?: number;
|
||
item_anchorY?: number;
|
||
row?: number; //水平方向排版时,垂直方向上的行数
|
||
column?: number; //垂直方向排版时,水平方向上的列数
|
||
cb_host?: any; //回调函数host
|
||
scroll_to_end_cb?: () => void; //滚动到尽头的回调
|
||
auto_scrolling?: boolean; //append时自动滚动到尽头
|
||
}
|
||
|
||
type PackItem = {
|
||
x: number;
|
||
y: number;
|
||
data: any;
|
||
is_select: boolean;
|
||
item: ListViewItem;
|
||
} |