|
--[[说明:
|
|
用于创建Item列表的组件,支持三种创建方式:
|
|
)传入Item类名:使用字段item_class
|
|
)传入prefab资源名:使用字段prefab_ab_name和prefab_res_name
|
|
)对象池类型如:UIObjPool.UIType.AwardItem:使用字段obj_pool_type
|
|
)单个控件如:UIType.Label使用字段ui_factory_type
|
|
各字段使用说明:
|
|
data_list:子节点的信息列表
|
|
create_frequency:分时加载,即每隔这么多秒加载一个子节点
|
|
item_con:装载子节点的容器节点
|
|
scroll_view:滚动容器节点(带ScrollRect组件的那个),如果不是滚动容器的话此值设为和item_con一样即可
|
|
item_width:单个子节点的宽度, item_height为高度
|
|
get_item_width:如果你的子节点们不是同一大小,这时就需要传入该回调用于查询每个子节点的大小,get_item_height同理,设定后还需要指定reuse_item_num字段,因为子节点是动态的了,我帮不了你预先计算可视区域里最多可显示多少个子节点
|
|
space_x:子节点间的水平间隔, space_y为垂直间隔
|
|
start_x:子节点的初始偏移,start_y同理
|
|
show_col:显示的列数, 暂不支持控制行数
|
|
on_update_item:子节点刷新事件的回调
|
|
reuse_item_num:复用子节点的数量,为nil或大于0时开启,默认是开启的,一般不需要手动指定(除非你的子节点大小是动态的),默认情况下将只创建可视区域里可见的最少数量节点,当子节点超出可视区域时会复用它填补到可视区域
|
|
alignment:对齐方式,目前只支持左上角,水平居中,垂直居中,当非左上角布局时优化选项无效(即reuse_item_num为0),有空再兼容处理吧
|
|
child_names:仅当prefab_ab_name和prefab_res_name有值时才有效,指定子节点的子节点们的名字集
|
|
fitsize_scroll_view:是否把滚动容器的大小设为刚好容纳子节点们的大小
|
|
is_scroll_back_on_update:是否每次设置的时候重置滚动容器坐标
|
|
final_callback:延迟创建第一次创建完item之后是否需要回调
|
|
force_hide:强制使用SetActive,不走SetVisible的优化,对子节点有角度修改时使用
|
|
mask_end_to_first:屏蔽掉 滚动到末尾的时候会显示第一个节点的逻辑
|
|
]]--
|
|
UI.ItemListCreator = UI.ItemListCreator or BaseClass(UI.UIComponent)
|
|
local math_ceil = math.ceil
|
|
local math_min = math.min
|
|
local math_floor = math.floor
|
|
local math_abs = math.abs
|
|
--先声明私有函数
|
|
local GetUnVisibleRowColBySize, GetCurUnvisibleRowOrCol, UpdateContainerQuads, GetItemCreator
|
|
, BindScrollEvent, HandleScrollChange, GetAlignInfo, UpdateScrollViewSize, InitInfo
|
|
, UpdateItemsGrid, ResetItemsRealIndex, ResizeItemList
|
|
|
|
-- local PrintItems = function ( self )
|
|
-- local str = ""
|
|
-- for i=1,#self.item_list do
|
|
-- local item = self.item_list[i]
|
|
-- str = str..tostring(item._real_index_for_item_creator_)..", "
|
|
-- end
|
|
-- print('Cat:ItemListCreator.lua[PrintItems] :', str)
|
|
-- end
|
|
|
|
function UI.ItemListCreator:OnLoad()
|
|
self.item_list = {}
|
|
self.is_first_update = true
|
|
self.last_unvisible_row = 0
|
|
end
|
|
|
|
local InitInfo = function ( self, info )
|
|
self:HideAllItems()
|
|
if not info or not info.data_list or #info.data_list <= 0 then return false end
|
|
|
|
--item_width或item_height至少要有一个不为空
|
|
assert(info.item_width~=nil or info.item_height~=nil or info.get_item_width~=nil or info.get_item_height, "item_height or item_width or get_item_width or get_item_height is nil")
|
|
--item_con就是装子节点的,scroll_view就是带ScrollRect组件的transform组件
|
|
assert(info.item_con~=nil and info.scroll_view~=nil, "item_con and scroll_view cannot be nil")
|
|
info.is_scroll_view = info.item_con~=info.scroll_view
|
|
|
|
info.item_width = info.item_width or 0
|
|
info.item_height = info.item_height or 0
|
|
info.space_x = info.space_x or 0
|
|
info.space_y = info.space_y or 0
|
|
info.show_col = info.show_col or 0
|
|
info.start_x = info.start_x or 0
|
|
info.start_y = info.start_y or 0
|
|
info.is_enable = info.is_enable == nil and true or info.is_enable
|
|
info.add_width = info.add_width or 0--容器大小
|
|
info.add_height = info.add_height or 0--容器大小
|
|
|
|
|
|
self.offset_x = info.item_width + info.space_x
|
|
self.offset_y = info.item_height + info.space_y
|
|
local visual_width = GetSizeDeltaX(info.scroll_view)
|
|
local visual_height = GetSizeDeltaY(info.scroll_view)
|
|
self.visual_width = info.visual_width or visual_width
|
|
self.visual_height = info.visual_height or visual_height
|
|
if info.reuse_item_num == nil or info.reuse_item_num > 0 then
|
|
--默认开启优化选项
|
|
self.is_reuse_items = true
|
|
end
|
|
|
|
if not self.scroll_view_scr then
|
|
self.scroll_view_scr = info.scroll_view:GetComponent("ScrollRect")
|
|
if self.scroll_view_scr then
|
|
self.is_horizon_scroll = self.scroll_view_scr.horizontal
|
|
self.is_vertical_scroll = self.scroll_view_scr.vertical
|
|
end
|
|
end
|
|
self.info = info
|
|
return true
|
|
end
|
|
|
|
--根据传入的配置和滚动容器大小判断显示的方式:垂直,水平,格子
|
|
function UI.ItemListCreator:UpdateItems(info)
|
|
local is_init_ok = InitInfo(self, info)
|
|
if not is_init_ok then return end
|
|
|
|
local min_float = 0.0001
|
|
|
|
if info.item_width <= min_float and info.get_item_width==nil then
|
|
--你的滚动容器不是垂直滚动的,那应该就是水平滚动的吧,但是你没有配置item_width,显示肯定会有问题的
|
|
assert(self.is_vertical_scroll or not info.is_scroll_view, "are you sure show items with vert layout in unvert scroll view?")
|
|
--没配置节点宽度的肯定是垂直一行的排版
|
|
self.info.show_col = 1
|
|
self.show_type = "Vert"
|
|
UpdateItemsGrid(self)
|
|
elseif info.item_height <= min_float and info.get_item_height==nil then
|
|
--你的滚动容器不是水平滚动的,那应该就是垂直滚动的吧,但是你没有配置item_height,显示肯定会有问题的
|
|
assert(self.is_horizon_scroll or not info.is_scroll_view, "are you sure show items with horizon layout in unhorizon scroll view?")
|
|
--没配置节点高度的肯定是水平一行的排版
|
|
self.info.show_col = self.info.data_list and #self.info.data_list or 0
|
|
self.show_type = "Hori"
|
|
UpdateItemsGrid(self)
|
|
else
|
|
-- local visual_w = GetSizeDeltaX(info.scroll_view)
|
|
info.show_col = round((self.visual_width+info.space_x) / self.offset_x)
|
|
UpdateItemsGrid(self)
|
|
end
|
|
end
|
|
|
|
--水平加垂直布局,一般没特殊需求直接用UpdateItems这个接口就行了
|
|
UpdateItemsGrid = function ( self )
|
|
local info = self.info
|
|
if self.step_create_item_id then
|
|
--有可能再次调用本类的刷新函数时还没创建完上次调用刷新的子节点们,所以需要等创建完后再刷新一次
|
|
self.is_update_after_delay_create_items = true
|
|
return
|
|
end
|
|
--先刷新容器的大小坐标等信息
|
|
self.get_item_pos_xy_func = UpdateContainerQuads(self)
|
|
--获取子节点的创建函数
|
|
local creator = GetItemCreator(self)
|
|
|
|
--可视区域内最多可以显示多少个节点,尽量只创建少点节点,因为就算在滚动容器不可见的地方,节点也要占drawcall的
|
|
local max_visual_item_num = 0
|
|
if self.is_reuse_items then
|
|
max_visual_item_num = info.reuse_item_num or self:GetMaxVisualItemNum(info)
|
|
else
|
|
max_visual_item_num = #info.data_list
|
|
end
|
|
local min_item_num = math_min(#info.data_list, max_visual_item_num)
|
|
ResizeItemList(self, min_item_num)
|
|
ResetItemsRealIndex(self)
|
|
if info.create_frequency ~= nil and self.is_first_update then
|
|
self.cur_create_index = 1
|
|
self.max_create_num = min_item_num
|
|
if self.scroll_view_scr then
|
|
--延迟创建时不让滚动
|
|
self.scroll_view_scr.enabled = false
|
|
end
|
|
local step_create_item = function ( )
|
|
creator(self.cur_create_index, info.data_list[self.cur_create_index])
|
|
self.cur_create_index = self.cur_create_index + 1
|
|
if self.cur_create_index > self.max_create_num then
|
|
if self.step_create_item_id then
|
|
GlobalTimerQuest:CancelQuest(self.step_create_item_id)
|
|
self.step_create_item_id = nil
|
|
end
|
|
if self.scroll_view_scr and info.is_enable then
|
|
self.scroll_view_scr.enabled = true
|
|
end
|
|
if min_item_num < #info.data_list or info.force_bind_scroll_event then
|
|
BindScrollEvent(self)
|
|
end
|
|
if self.is_update_after_delay_create_items then
|
|
self.is_update_after_delay_create_items = false
|
|
UpdateItemsGrid(self)
|
|
end
|
|
if self.cache_scroll_to_item_info then
|
|
self:ScrollToItem(unpack(self.cache_scroll_to_item_info))
|
|
self.cache_scroll_to_item_info = nil
|
|
end
|
|
if info.final_callback and type(info.final_callback) == "function" then
|
|
info.final_callback()
|
|
info.final_callback = nil
|
|
end
|
|
end
|
|
end
|
|
if not self.step_create_item_id then
|
|
if info.create_num_per_time then
|
|
local origin_func = step_create_item
|
|
step_create_item = function()
|
|
for i=1,info.create_num_per_time do
|
|
if self.cur_create_index > self.max_create_num then
|
|
break
|
|
end
|
|
origin_func()
|
|
end
|
|
end
|
|
end
|
|
self.step_create_item_id = GlobalTimerQuest:AddPeriodQuest(step_create_item, info.create_frequency, -1)
|
|
end
|
|
step_create_item()
|
|
else
|
|
for i=1,min_item_num do
|
|
creator(i, info.data_list[i])
|
|
end
|
|
if min_item_num < #info.data_list or info.force_bind_scroll_event then
|
|
BindScrollEvent(self)
|
|
end
|
|
if info.final_callback and type(info.final_callback) == "function" then
|
|
info.final_callback()
|
|
info.final_callback = nil
|
|
end
|
|
end
|
|
self.is_first_update = false--首次刷新时才需要延时创建节点
|
|
end
|
|
|
|
--暂只支持垂直或水平居中和左上角,默认是左上角
|
|
GetAlignInfo = function ( self, items_sum_width, items_sum_height )
|
|
local item_align_x = 0--节点需要对齐的偏移
|
|
local item_align_y = 0
|
|
local con_align_x = 0--滚动容器(子节点的父节点)的对齐偏移
|
|
local con_align_y = 0
|
|
local new_con_size_w = items_sum_width
|
|
local new_con_size_h = items_sum_height
|
|
local alignment = self.info and self.info.alignment or UnityEngine.TextAnchor.UpperLeft
|
|
|
|
-- local visual_width, visual_height = GetSizeDeltaXY(self.info.scroll_view)
|
|
local visual_width, visual_height = self.visual_width, self.visual_height
|
|
local hori_align, vert_align = AlignTypeToStr(alignment)
|
|
if vert_align == "Upper" then
|
|
--不需要做什么
|
|
elseif vert_align == "Middle" then
|
|
--垂直居中
|
|
local half_y = -(visual_height-items_sum_height)/2
|
|
if new_con_size_h < visual_height then
|
|
--所有子节点的高度小于容器可视化高度时,子节点的父节点的高度要设为容器的可视化高度,同时所有子节点要往右移
|
|
new_con_size_h = visual_height
|
|
item_align_y = half_y
|
|
else
|
|
--子节点的父节点往上移就行了
|
|
con_align_y = half_y
|
|
end
|
|
--非左上角布局暂不支持优化
|
|
self.is_reuse_items = false
|
|
elseif vert_align == "Lower" then
|
|
--左下对齐
|
|
local align_y = -(visual_height-items_sum_height)
|
|
if new_con_size_h < visual_height then
|
|
--所有子节点的高度小于容器可视化高度时,子节点的父节点的高度要设为容器的可视化高度,同时所有子节点要往右移
|
|
new_con_size_h = visual_height
|
|
item_align_y = align_y
|
|
else
|
|
--子节点的父节点往上移就行了
|
|
con_align_y = align_y
|
|
end
|
|
--非左上角布局暂不支持优化
|
|
self.is_reuse_items = false
|
|
end
|
|
if hori_align == "Left" then
|
|
new_con_size_w = items_sum_width-visual_width
|
|
elseif hori_align == "Center" then
|
|
--水平居中
|
|
local half_x = (visual_width-items_sum_width)/2
|
|
if new_con_size_w < visual_width then
|
|
--所有子节点的宽度小于容器可视化宽度时,子节点的父节点的宽度要设为容器的可视化宽度,同时所有子节点要往右移
|
|
new_con_size_w = 0--因为item_con是水平拉伸的,所以sizeDelta设为0就是其父节点的宽度了
|
|
item_align_x = half_x
|
|
else
|
|
--如果子节点们可以填满父节点,那就不需要动了,留着下面的注释
|
|
-- con_align_x = half_x
|
|
--之所以要减去visual_width是因为滚动容器父节点的anchor是水平拉伸的
|
|
new_con_size_w = items_sum_width-visual_width
|
|
end
|
|
--非左上角布局暂不支持优化
|
|
self.is_reuse_items = false
|
|
elseif hori_align == "Right" then
|
|
--右对齐
|
|
local align_x = (visual_width-items_sum_width)
|
|
if new_con_size_w < visual_width then
|
|
--所有子节点的宽度小于容器可视化宽度时,子节点的父节点的宽度要设为容器的可视化宽度,同时所有子节点要往右移
|
|
new_con_size_w = 0--因为item_con是水平拉伸的,所以sizeDelta设为0就是其父节点的宽度了
|
|
item_align_x = align_x
|
|
else
|
|
--子节点的父节点往左移就行了
|
|
con_align_x = align_x
|
|
--之所以要减去visual_width是因为滚动容器父节点的anchor是水平拉伸的
|
|
new_con_size_w = items_sum_width-visual_width
|
|
end
|
|
--非左上角布局暂不支持优化
|
|
self.is_reuse_items = false
|
|
end
|
|
return item_align_x, item_align_y, con_align_x, con_align_y, new_con_size_w, new_con_size_h
|
|
end
|
|
|
|
UpdateScrollViewSize = function ( self, items_sum_width, items_sum_height )
|
|
local show_type = self.show_type or "Grid"
|
|
if self.info.fitsize_scroll_view and self.info.is_scroll_view then
|
|
if show_type == "Vert" then
|
|
SetSizeDeltaY(self.info.scroll_view, items_sum_height)
|
|
elseif show_type == "Hori" then
|
|
SetSizeDeltaX(self.info.scroll_view, items_sum_width)
|
|
else
|
|
SetSizeDelta(self.info.scroll_view, items_sum_width, items_sum_height)
|
|
end
|
|
end
|
|
end
|
|
--刷新容器的大小坐标等,并返回一函数以供获取子节点的坐标
|
|
UpdateContainerQuads = function ( self )
|
|
local info = self.info
|
|
local items_sum_width = info.start_x+self.offset_x*info.show_col-info.space_x
|
|
local items_sum_height = -info.start_y+math_ceil(#info.data_list/info.show_col)*self.offset_y-info.space_y
|
|
if self.is_horizon_scroll and info.get_item_width then
|
|
items_sum_width = 0
|
|
for i=1,#info.data_list do
|
|
local w = info.get_item_width(i)
|
|
items_sum_width = items_sum_width + w + self.info.space_x
|
|
end
|
|
items_sum_width = info.start_x + items_sum_width - self.info.space_x
|
|
elseif info.get_item_height then
|
|
items_sum_height = 0
|
|
for i=1,#info.data_list do
|
|
local h = info.get_item_height(i)
|
|
items_sum_height = items_sum_height + h + self.info.space_y
|
|
end
|
|
items_sum_height = -info.start_y + items_sum_height - self.info.space_y
|
|
end
|
|
UpdateScrollViewSize(self, items_sum_width, items_sum_height)
|
|
local item_align_x, item_align_y, con_align_x, con_align_y, new_con_size_w, new_con_size_h = GetAlignInfo(self, items_sum_width, items_sum_height)
|
|
if info.is_scroll_view then
|
|
local show_type = self.show_type or "Grid"
|
|
if show_type == "Vert" then
|
|
SetSizeDeltaY(info.item_con, new_con_size_h + self.info.add_height)
|
|
if self.is_first_update or info.is_scroll_back_on_update then
|
|
SetLocalPositionY(info.item_con, con_align_y)
|
|
end
|
|
elseif show_type == "Hori" then
|
|
SetSizeDeltaX(info.item_con, new_con_size_w)
|
|
if self.is_first_update or info.is_scroll_back_on_update then
|
|
SetLocalPositionX(info.item_con, con_align_x + self.info.add_width)
|
|
end
|
|
else
|
|
SetSizeDelta(info.item_con, new_con_size_w + self.info.add_width, new_con_size_h + self.info.add_height)
|
|
if self.is_first_update or info.is_scroll_back_on_update then
|
|
SetLocalPosition(info.item_con, con_align_x, con_align_y)
|
|
end
|
|
end
|
|
end
|
|
local get_item_pos_xy = function ( i )
|
|
local new_x = 0
|
|
local new_y = 0
|
|
if not info.get_item_width then
|
|
new_x = item_align_x+info.start_x+self.offset_x*((i-1)%info.show_col)
|
|
else
|
|
for tmp_i=1,i-1 do
|
|
local w = info.get_item_width(tmp_i)
|
|
new_x = new_x + w + self.info.space_x
|
|
end
|
|
if i>1 then
|
|
new_x = new_x - self.info.space_x
|
|
end
|
|
new_x = new_x + item_align_x + info.start_x
|
|
end
|
|
if not info.get_item_height then
|
|
new_y = item_align_y+info.start_y-self.offset_y*math_floor((i-1)/info.show_col)
|
|
else
|
|
if i == 1 then
|
|
new_y = item_align_y + info.start_y
|
|
else
|
|
for tmp_i=1,i-1 do
|
|
local h = info.get_item_height(tmp_i)
|
|
new_y = new_y - h
|
|
end
|
|
new_y = new_y - self.info.space_y*(i-1) + item_align_y + info.start_y
|
|
end
|
|
end
|
|
return new_x, new_y
|
|
end
|
|
return get_item_pos_xy
|
|
end
|
|
|
|
local update_item_for_creator = function ( self, item )
|
|
if self.info.on_update_item then
|
|
local real_index = item._real_index_for_item_creator_
|
|
local real_data = self.info.data_list[real_index]
|
|
self.info.on_update_item(item, real_index, real_data)
|
|
end
|
|
end
|
|
|
|
local on_update_prefab_item = function ( item, i, self )
|
|
update_item_for_creator(self, item)
|
|
end
|
|
|
|
--支持三种子节点的创建方式
|
|
GetItemCreator = function ( self )
|
|
local info = self.info
|
|
local creator = nil
|
|
if info.item_class ~= nil then
|
|
creator = function(i, v)
|
|
local item = self.item_list[i]
|
|
if not item then
|
|
item = info.item_class.New(info.item_con,nil,nil,info.init_value)
|
|
self.item_list[i] = item
|
|
item._real_index_for_item_creator_ = i
|
|
end
|
|
item:SetVisible(true,self.info.force_hide)
|
|
item:SetPosition(self.get_item_pos_xy_func(item._real_index_for_item_creator_))
|
|
update_item_for_creator(self, item)
|
|
end
|
|
elseif info.prefab_ab_name and info.prefab_res_name then
|
|
creator = function(i, v)
|
|
local item = self.item_list[i]
|
|
if not item then
|
|
local on_load_ok = function ( item )
|
|
GetChildren(item, info.child_names)
|
|
SetParent(item.transform, info.item_con)
|
|
end
|
|
item = lua_resM:LoadPrefabView(self, info.prefab_ab_name, info.prefab_res_name, on_load_ok)
|
|
self.item_list[i] = item
|
|
self.item_list[i]._real_index_for_item_creator_ = i
|
|
item.UpdateView = on_update_prefab_item
|
|
end
|
|
item:SetVisible(true,self.info.force_hide)
|
|
item:SetPosition(self.get_item_pos_xy_func(item._real_index_for_item_creator_))
|
|
item:SetData(nil, self)
|
|
end
|
|
elseif info.obj_pool_type then
|
|
creator = function(i, v)
|
|
local item = self.item_list[i]
|
|
if not item then
|
|
item = UIObjPool:getInstance():PopItem(info.obj_pool_type, info.item_con)
|
|
self.item_list[i] = item
|
|
item._real_index_for_item_creator_ = i
|
|
else
|
|
if info.obj_pool_type == UIObjPool.UIType.AwardItem then
|
|
item:ResetInfo()
|
|
end
|
|
end
|
|
item:SetVisible(true,self.info.force_hide)
|
|
item:SetPosition(self.get_item_pos_xy_func(item._real_index_for_item_creator_))
|
|
if item.SetItemSize and info.obj_pool_type == UIObjPool.UIType.AwardItem then
|
|
local item_size = info.item_width or info.item_height
|
|
item:SetItemSize(item_size, item_size)
|
|
end
|
|
update_item_for_creator(self, item)
|
|
end
|
|
self.destroy_pool_type = info.obj_pool_type
|
|
elseif info.ui_factory_type then
|
|
creator = function(i, v)
|
|
local item = self.item_list[i]
|
|
if not item then
|
|
local gameObj = UiFactory.createChild(info.item_con, info.ui_factory_type)
|
|
item = {
|
|
gameObject = gameObj,
|
|
transform = gameObj.transform,
|
|
is_loaded = true,
|
|
SetPosition = function(item, x, y)
|
|
SetLocalPosition(item.transform, x, y)
|
|
end,
|
|
SetVisible = function(item, is_show)
|
|
item.gameObject:SetActive(is_show)
|
|
end,
|
|
AddUIComponent = UIPartical.AddUIComponent,
|
|
RemoveUIComponent = UIPartical.RemoveUIComponent,
|
|
DeleteMe=LuaResManager.__DestroyPrefab,
|
|
}
|
|
item.transform.pivot = Vector2(0, 1)--作为子节点的话一般轴点都是左上角的啦
|
|
item.transform.anchorMin = Vector2(0, 1)
|
|
item.transform.anchorMax = Vector2(0, 1)
|
|
self.item_list[i] = item
|
|
item._real_index_for_item_creator_ = i
|
|
end
|
|
item:SetVisible(true)
|
|
item:SetPosition(self.get_item_pos_xy_func(item._real_index_for_item_creator_))
|
|
update_item_for_creator(self, item)
|
|
end
|
|
|
|
else
|
|
--没有指定创建节点的方式
|
|
assert(false, "has not specify create way!")
|
|
end
|
|
return creator
|
|
end
|
|
|
|
--获取unvisible_size的区域里有几个单位(滚动容器为垂直时就是行,水平滚动时就是列)子节点不可见
|
|
GetUnVisibleRowColBySize = function( self, unvisible_size )
|
|
local item_num = #self.info.data_list
|
|
local result = 0
|
|
if self.is_horizon_scroll then
|
|
if not self.info.get_item_width then
|
|
result = math_floor(unvisible_size / self.offset_x)
|
|
else
|
|
for i=1,item_num do
|
|
local w = self.info.get_item_width(i)
|
|
unvisible_size = unvisible_size - w - self.info.space_x
|
|
if unvisible_size < 0 then
|
|
break
|
|
else
|
|
result = result + 1
|
|
end
|
|
end
|
|
end
|
|
else
|
|
if not self.info.get_item_height then
|
|
result = math_floor(unvisible_size / self.offset_y)
|
|
else
|
|
for i=1,item_num do
|
|
local h = self.info.get_item_height(i)
|
|
unvisible_size = unvisible_size - h - self.info.space_y
|
|
if unvisible_size < 0 then
|
|
break
|
|
else
|
|
result = result + 1
|
|
end
|
|
end
|
|
end
|
|
end
|
|
return result
|
|
end
|
|
|
|
GetCurUnvisibleRowOrCol = function ( self )
|
|
local info = self.info
|
|
local cur_unvisible_row = 0
|
|
local move_num_per_operate = 0--每次操作的个数
|
|
if self.is_horizon_scroll then
|
|
local con_x = -GetLocalPositionX(info.item_con)
|
|
con_x = con_x - info.start_x
|
|
cur_unvisible_row = GetUnVisibleRowColBySize(self, con_x+info.space_x)
|
|
--水平布局下是逐个移动的
|
|
move_num_per_operate = 1
|
|
else
|
|
local con_y = GetLocalPositionY(info.item_con)
|
|
con_y = con_y + info.start_y
|
|
cur_unvisible_row = GetUnVisibleRowColBySize(self, con_y+info.space_y)
|
|
--垂直布局下是整行移动的
|
|
move_num_per_operate = info.show_col
|
|
end
|
|
if cur_unvisible_row < 0 then
|
|
cur_unvisible_row = 0
|
|
end
|
|
return cur_unvisible_row, move_num_per_operate
|
|
end
|
|
|
|
ResetItemsRealIndex = function ( self )
|
|
if not self.info then return end
|
|
local cur_unvisible_row, move_num_per_operate = GetCurUnvisibleRowOrCol(self)
|
|
self.last_unvisible_row = cur_unvisible_row
|
|
local last_index = cur_unvisible_row*move_num_per_operate
|
|
for i=1, #self.item_list do
|
|
local item = self.item_list[i]
|
|
local new_real_index = last_index + i
|
|
if new_real_index > #self.info.data_list then
|
|
last_index = -i+1
|
|
new_real_index = 1
|
|
end
|
|
item._real_index_for_item_creator_ = new_real_index
|
|
-- item.gameObject.name = "item_list_creator_"..new_real_index
|
|
end
|
|
end
|
|
|
|
--滚动容器滚动时,根据滚动距离判断有几行节点越出边界了,然后把那几行节点往底部或上部移
|
|
HandleScrollChange = function ( self )
|
|
local info = self.info
|
|
if not info.data_list then return end
|
|
local cur_unvisible_row, move_num_per_operate = GetCurUnvisibleRowOrCol(self)
|
|
local last_unvisible_row = self.last_unvisible_row
|
|
local new_unvisible_row = math_abs(last_unvisible_row-cur_unvisible_row)
|
|
self.last_unvisible_row = cur_unvisible_row
|
|
local all_data_num = #info.data_list
|
|
if cur_unvisible_row > last_unvisible_row then
|
|
local last_item = self.item_list[#self.item_list]
|
|
if not last_item then return end
|
|
|
|
local last_index = last_item._real_index_for_item_creator_
|
|
for i=1,new_unvisible_row*move_num_per_operate do
|
|
local item = table.remove(self.item_list, 1)
|
|
if item then
|
|
local is_end_to_first = false
|
|
local new_real_index = last_index + i
|
|
if new_real_index > all_data_num then
|
|
is_end_to_first = true
|
|
last_index = -i+1
|
|
new_real_index = 1
|
|
end
|
|
item._real_index_for_item_creator_ = new_real_index
|
|
-- item.gameObject.name = "item_list_creator_"..new_real_index
|
|
item:SetPosition(self.get_item_pos_xy_func(new_real_index))
|
|
if info.prefab_ab_name and info.prefab_res_name then
|
|
item:SetData(nil, self)
|
|
elseif info.on_update_item then
|
|
if is_end_to_first and self.info.mask_end_to_first then
|
|
--部分界面 刷新到最后一个的时候,不要更新第一个节点
|
|
else
|
|
info.on_update_item(item, new_real_index, info.data_list[new_real_index])
|
|
end
|
|
end
|
|
-- 把节点移到队尾
|
|
table.insert(self.item_list, item)
|
|
end
|
|
end
|
|
else
|
|
local first_item = self.item_list[1]
|
|
if not first_item then return end
|
|
|
|
local first_index = first_item._real_index_for_item_creator_
|
|
for i=1,new_unvisible_row*move_num_per_operate do
|
|
local item = table.remove(self.item_list, #self.item_list)
|
|
if item then
|
|
local new_real_index = first_index - i
|
|
if new_real_index <= 0 then
|
|
new_real_index = all_data_num
|
|
first_index = new_real_index+i-1
|
|
end
|
|
item._real_index_for_item_creator_ = new_real_index
|
|
-- item.gameObject.name = "item_list_creator_"..new_real_index
|
|
item:SetPosition(self.get_item_pos_xy_func(new_real_index))
|
|
if info.prefab_ab_name and info.prefab_res_name then
|
|
item:SetData(nil, self)
|
|
elseif info.on_update_item then
|
|
info.on_update_item(item, new_real_index, info.data_list[new_real_index])
|
|
end
|
|
end
|
|
--把节点移到队头
|
|
table.insert(self.item_list, 1, item)
|
|
end
|
|
end
|
|
-- PrintItems(self)
|
|
end
|
|
|
|
--为滚动容器绑定滚动事件,在滚动时刷新子节点的坐标,把超过边界不可见的节点移到可见区域
|
|
BindScrollEvent = function ( self )
|
|
if not self.had_bind_scroll and self.info.scroll_view ~= nil and self.scroll_view_scr ~= nil then
|
|
self.had_bind_scroll = true
|
|
local on_scroll_changed = function ( )
|
|
HandleScrollChange(self)
|
|
if self.info and self.info.on_scroll then
|
|
self.info.on_scroll(self.item_list)
|
|
end
|
|
end
|
|
self.scroll_view_scr.onValueChanged:RemoveAllListeners()
|
|
self.scroll_view_scr.onValueChanged:AddListener(on_scroll_changed)
|
|
|
|
if self.info.on_scroll_end then
|
|
AddDragEndEvent(self.info and self.info.scroll_view.gameObject, self.info.on_scroll_end)
|
|
end
|
|
end
|
|
HandleScrollChange(self)
|
|
end
|
|
|
|
function UI.ItemListCreator:StopScroll( )
|
|
if self.scroll_view_scr then
|
|
self.scroll_view_scr:StopMovement()
|
|
end
|
|
end
|
|
|
|
function UI.ItemListCreator:HideAllItems()
|
|
if self.step_create_item_id then
|
|
--正在创建子节点中,创建完后会再次调用本函数的
|
|
return
|
|
end
|
|
for i,v in ipairs(self.item_list) do
|
|
v:SetVisible(false,self.info.force_hide)
|
|
end
|
|
end
|
|
|
|
--滚动到指定子节点的位置上,默认在最左或最上,可以配合pos_offset设置偏移,默认使用动画,不想动画的话就传入animte_info为false
|
|
--scroll_end_call滚动完执行这个方法w
|
|
function UI.ItemListCreator:ScrollToItem(item_index, pos_offset, animte_info,scroll_end_call)
|
|
if not self.info or not self.info.item_con then return end
|
|
if self.step_create_item_id then
|
|
--还在延迟创建节点中,等创建完毕后再滚过去
|
|
self.cache_scroll_to_item_info = {item_index, pos_offset, animte_info}
|
|
return
|
|
end
|
|
pos_offset = pos_offset or 0
|
|
|
|
cc.ActionManager:getInstance():removeAllActionsFromTarget(self.info.item_con)
|
|
local duration = 0.3
|
|
if animte_info then
|
|
duration = animte_info.duration
|
|
end
|
|
if self.is_horizon_scroll then
|
|
local target_pos_x, _ = self.get_item_pos_xy_func(item_index)
|
|
target_pos_x = -target_pos_x+pos_offset
|
|
if target_pos_x > 0 then
|
|
target_pos_x = 0
|
|
end
|
|
local con_width = GetSizeDeltaX(self.info.item_con)
|
|
local max_move = math.min(0, -con_width)
|
|
if target_pos_x < max_move then
|
|
target_pos_x = max_move
|
|
end
|
|
if animte_info or animte_info==nil then
|
|
local _, origin_pos_y, origin_pos_z = GetLocalPosition(self.info.item_con)
|
|
local action = cc.MoveTo.createLocalType(duration, target_pos_x, origin_pos_y, origin_pos_z)
|
|
local function call_func( ... )
|
|
if scroll_end_call then
|
|
scroll_end_call()
|
|
end
|
|
HandleScrollChange(self)
|
|
end
|
|
local callfun = cc.CallFunc.New(call_func)
|
|
action = cc.Sequence.New(action,callfun)
|
|
cc.ActionManager:getInstance():addAction(action, self.info.item_con)
|
|
else
|
|
SetLocalPositionX(self.info.item_con, target_pos_x)
|
|
HandleScrollChange(self)
|
|
end
|
|
else
|
|
local _, target_pos_y = self.get_item_pos_xy_func(item_index)
|
|
target_pos_y = -target_pos_y+pos_offset
|
|
if target_pos_y < 0 then
|
|
target_pos_y = 0
|
|
end
|
|
local con_height = GetSizeDeltaY(self.info.item_con)
|
|
-- local visual_height = GetSizeDeltaY(self.info.scroll_view)
|
|
local max_move = math.max(0, con_height-self.visual_height)
|
|
if target_pos_y > max_move then
|
|
target_pos_y = max_move
|
|
end
|
|
if animte_info or animte_info==nil then
|
|
local origin_pos_x, _, origin_pos_z = GetLocalPosition(self.info.item_con)
|
|
local action = cc.MoveTo.createLocalType(duration, origin_pos_x, target_pos_y, origin_pos_z)
|
|
local function call_func( ... )
|
|
if scroll_end_call then
|
|
scroll_end_call()
|
|
end
|
|
HandleScrollChange(self)
|
|
end
|
|
local callfun = cc.CallFunc.New(call_func)
|
|
action = cc.Sequence.New(action,callfun)
|
|
cc.ActionManager:getInstance():addAction(action, self.info.item_con)
|
|
else
|
|
SetLocalPositionY(self.info.item_con, target_pos_y)
|
|
HandleScrollChange(self)
|
|
end
|
|
end
|
|
end
|
|
|
|
--强制更新下位置坐标
|
|
function UI.ItemListCreator:ForceScrollChange( )
|
|
HandleScrollChange(self)
|
|
end
|
|
|
|
--获取可视区域内最多可显示多少个子节点
|
|
function UI.ItemListCreator:GetMaxVisualItemNum(info)
|
|
local show_type = self.show_type or "Grid"
|
|
-- print('Cat:ItemListCreator.lua[520] show_type', show_type)
|
|
if show_type == "Vert" then
|
|
-- local visual_h = GetSizeDeltaY(info.scroll_view)
|
|
return round((self.visual_height+info.space_y) / self.offset_y)+1
|
|
elseif show_type == "Hori" then
|
|
-- local visual_w = GetSizeDeltaX(info.scroll_view)
|
|
return round((self.visual_width+info.space_x) / self.offset_x)+1
|
|
elseif show_type == "Grid" then
|
|
-- local visual_h = GetSizeDeltaY(info.scroll_view)
|
|
local max_row = round((self.visual_height+info.space_y) / self.offset_y)+1
|
|
return info.show_col * max_row
|
|
end
|
|
assert(false, "scroll visual size too small for show items")
|
|
return 0
|
|
end
|
|
|
|
function UI.ItemListCreator:Reset()
|
|
self:OnDestroy()
|
|
end
|
|
|
|
--遍历所有创建的节点
|
|
function UI.ItemListCreator:IterateItems(func)
|
|
if not func then return end
|
|
|
|
for i=1,#self.item_list do
|
|
local real_index = self.item_list[i]._real_index_for_item_creator_
|
|
local is_break = func(self.item_list[i], real_index, self.info and self.info.data_list and self.info.data_list[real_index])
|
|
if is_break then
|
|
break
|
|
end
|
|
end
|
|
end
|
|
|
|
--获取第几节点的坐标
|
|
function UI.ItemListCreator:GetItemPosXY(index)
|
|
if self.get_item_pos_xy_func then
|
|
return self.get_item_pos_xy_func(index)
|
|
end
|
|
print('Cat:ItemListCreator.lua[get wrong item pos!!!get_item_pos_xy_func is nil!!!]')
|
|
return nil
|
|
end
|
|
|
|
ResizeItemList = function( self, min_item_num )
|
|
if #self.item_list > min_item_num then
|
|
for i=min_item_num+1, #self.item_list do
|
|
if self.destroy_pool_type then
|
|
UIObjPool:getInstance():PushItem(self.destroy_pool_type, self.item_list[i])
|
|
else
|
|
self.item_list[i]:DeleteMe()
|
|
end
|
|
self.item_list[i] = nil
|
|
end
|
|
end
|
|
end
|
|
|
|
--关闭界面时清除创建的节点
|
|
function UI.ItemListCreator:OnDestroy()
|
|
self.had_bind_scroll = false
|
|
self.is_first_update = true
|
|
self.last_unvisible_row = 0
|
|
|
|
if self.scroll_view_scr then
|
|
self.scroll_view_scr:StopMovement()
|
|
end
|
|
if self.info and self.info.item_con and self.info.item_con ~= "null" then
|
|
cc.ActionManager:getInstance():removeAllActionsFromTarget(self.info.item_con)
|
|
SetLocalPosition(self.info.item_con, 0, 0)
|
|
end
|
|
|
|
if self.destroy_pool_type then
|
|
for i,item in pairs(self.item_list) do
|
|
UIObjPool:getInstance():PushItem(self.destroy_pool_type, item)
|
|
end
|
|
self.item_list = {}
|
|
else
|
|
for k,v in pairs(self.item_list) do
|
|
v:DeleteMe()
|
|
v = nil
|
|
end
|
|
self.item_list = {}
|
|
end
|
|
if self.scroll_view_scr and self.had_bind_scroll then
|
|
self.scroll_view_scr.onValueChanged:RemoveAllListeners()
|
|
self.scroll_view_scr = nil
|
|
end
|
|
if self.step_create_item_id then
|
|
GlobalTimerQuest:CancelQuest(self.step_create_item_id)
|
|
self.step_create_item_id = nil
|
|
end
|
|
end
|