mirror of
https://github.com/caochao/cocos_creator_proj_base.git
synced 2026-05-06 15:20:42 +08:00
1003 lines
30 KiB
TypeScript
1003 lines
30 KiB
TypeScript
import { LayoutUtil } from "./layout_utils"
|
||
import { ListViewItem } from "./listviewitem";
|
||
import { TimerMgr } from "../timer/timer_mgr";
|
||
import { gen_handler } from "../util";
|
||
|
||
export class ListView
|
||
{
|
||
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:new() => ListViewItem;
|
||
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);
|
||
this.timer = TimerMgr.getInst().add_updater(gen_handler(this.onUpdate, this), "listView render timer");
|
||
// cc.log("constructor", this.mask.width, this.mask.height, this.scrollview.node.width, this.scrollview.node.height, this.content.width, this.content.height);
|
||
}
|
||
|
||
private _touchBeganPosition:cc.Vec2;
|
||
private _touchEndPosition:cc.Vec2;
|
||
private on_scroll_touch_start(event:cc.Event.EventTouch)
|
||
{
|
||
this._touchBeganPosition = event.touch.getLocation();
|
||
}
|
||
|
||
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;
|
||
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).addComponent(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;
|
||
|
||
if(this.timer)
|
||
{
|
||
TimerMgr.getInst().remove(this.timer);
|
||
this.timer = 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,
|
||
}
|
||
|
||
type ListViewParams = {
|
||
scrollview:cc.ScrollView;
|
||
mask:cc.Mask;
|
||
content:cc.Node;
|
||
item_tpl:cc.Node;
|
||
item_class:new() => ListViewItem; //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;
|
||
}
|