scv2.lua (可能还需完善)

本文介绍了一种基于Lua实现的横向滑动模块设计方法,详细阐述了如何通过动态调整元素位置来实现流畅的滑动效果,并提供了丰富的示例代码。

摘要生成于 C知道 ,由 DeepSeek-R1 满血版支持, 前往体验 >

local scroll2 = {}  --横向滑动,只考虑一行的情况,如果一列有2个item,那么事先把这2个合为一个,在模块中的set_an_item稍微复杂一点
local MARGIN_BETWEEN
local MARGIN_LEFT
local ITEM_Y
local THRESHOLD -- 与innerX做比较


function scroll2:set_margin(marginB, marginL) --如果要设置则在create之前调用
    ccprint("set_margin 2 enter")
    MARGIN_BETWEEN = marginB or 0
    MARGIN_LEFT = marginL or 0
end




--[[
modName: 模块名
reuseLastRes : 是否重用上次的资源,比如同一个页面中不同标签的item都是一样,则可以复用上次的
refScv: 横向 scroll view
refItem: item
refData: 数据table, 用 #refData可获取长度
startItem: 希望从第几个item开始显示, 默认为 1 ,程序会根据情况调整至合理位置
]]
function scroll2:create( modName, reuseLastRes, refScv, refItem, refData, startItem)--一个页面中打开一个标签使用一次,上次的scv作废,但下面有方法还原状态
    ccprint("create 2 enter  ")
    if not reuseLastRes then
        self:onExit() --主要为释放上一次的资源
        self.item = refItem
        self.item:ignoreAnchorPointForPosition(false)
        self.item:setAnchorPoint(0, 0)
        self.item:retain()
    else    -- 以防出错,先把handle和listener关掉
        ccprint("Re use 2 ...")
        if self.handle then
            cc.Director:getInstance():getScheduler():unscheduleScriptEntry(self.handle)
            self.handle = nil
        end
        if self.listener1 then
--            self.modSelf._core:getEventDispatcher():removeEventListener(self.listener1)
            cc.Director:getInstance():getEventDispatcher():removeEventListener(self.listener1)
            self.listener1 = nil
        end
        if self.item_touch_enabled then --重用了但点击事件注销,目前这么写吧
            self.item_touch_enabled = false
        end
    end
    self.modName = modName
    self.modSelf = _G[modName].getInstance()
    self.scv = refScv
    self.inner= self.scv:getInnerContainer()
    self.data = refData
    self.item_width = self.item:getContentSize().width
    self.item_height = self.item:getContentSize().height
    self.scv_width = self.scv:getContentSize().width
    self.scv_height = self.scv:getContentSize().height
--    self.scv:setInnerContainerSize(cc.size(self.scv_width, self.scv_height)) --见垂直滑动 scv.lua的注释
    self.inner:setPosition(0, 0)    --垂直滑动不用这句,由上句就能自动还原至原点
    THRESHOLD = -(MARGIN_LEFT + self.item_width / 2 + MARGIN_BETWEEN + self.item_width)
    ITEM_Y = (self.scv_height - self.item_height) / 2
    ITEM_Y = math.max(0, ITEM_Y)
    self.pre_iHead = self.iHead  --当reuseLastRes 为 true的情形这句才有效
    self.iHead = startItem or 1
    --self:set_new_iHead_iTail() -- self:reuse_pre_rows() 内有矫正
    self.pre_iHead = self.pre_iHead or self.iHead 
    
    self:reuse_pre_items()   --这个函数有复用,删除,新增综合功能
    self:head_tail_head_tail()
    
    self.delX = 0   -- monitor()内会率先用到
    local function monitor()
        self:monitor()
    end
    self.handle = cc.Director:getInstance():getScheduler():scheduleScriptFunc(monitor, 0, false)
    
    local function onTouchBegan(touch, event)
        self.delX = 0
        return true
    end
    local function onTouchMoved(touch, event)
        self.delX = touch:getLocation().x - touch:getPreviousLocation().x
    end
    local function onTouchEnded(touch, event) 
        if self.delX == 0 then
            if self.item_touch_enabled and self.iTail ~= 0 then --不为空
                local touchX = touch:getLocation().x
                local touchY = touch:getLocation().y
                local item_i, modX, modY = self:get_the_item_touched(touchX,touchY)
                ccprint("item_i  " .. touchX .. '  ' .. touchY .. '  ' .. tostring(item_i))
                if item_i then
                    self.modSelf:touch_an_item(self['item' .. item_i], item_i, modX, modY) -- item, i
                end
            end
        end
    end


    self.listener1 = cc.EventListenerTouchOneByOne:create()
    self.listener1:registerScriptHandler(onTouchBegan,cc.Handler.EVENT_TOUCH_BEGAN )
    self.listener1:registerScriptHandler(onTouchMoved,cc.Handler.EVENT_TOUCH_MOVED )
    self.listener1:registerScriptHandler(onTouchEnded,cc.Handler.EVENT_TOUCH_ENDED )
--    self.modSelf._core:getEventDispatcher():addEventListenerWithFixedPriority(self.listener1, -1)
    cc.Director:getInstance():getEventDispatcher():addEventListenerWithFixedPriority(self.listener1, -1)
--    self.modSelf._core:getEventDispatcher():addEventListenerWithSceneGraphPriority(self.listener1, self.modSelf._core) --这种注册有点问题
end


function scroll2:jump_to( i) -- jump to i'th item
    self.pre_iHead = self.iHead
    self.iHead = i
    self:reuse_pre_items()
    self:head_tail_head_tail()
    if #self.data - i + 1 < self.visible_items then -- 为了能完全显示最后一个item
        self.inner:setPosition(self.scv_width - (2 * MARGIN_LEFT + (self.iTail - self.iHead + 1) * (self.item_width + MARGIN_BETWEEN) - MARGIN_BETWEEN), 0)
    end
end


function scroll2:refresh() --外部刷新
    ccprint("refresh 2 enter")   
    self.pre_iHead = self.iHead
    self.pre_iTail = self.iTail
    self.pre_data_len = #self.data   
    self.pre_innerX = self.inner:getPositionX()
    self:reuse_pre_items()
    self:tail_tail_head_head()
    if self.pre_data_len >= #self.data then --item数减少或不变
        if self.iTail == self.pre_iTail then
            self.inner:setPosition(self.pre_innerX, 0)
        else
            local x = 2 * MARGIN_LEFT + (self.iTail - self.iHead + 1) * (self.item_width + MARGIN_BETWEEN) - MARGIN_BETWEEN
            x = math.max(self.scv_width, x)
            self.inner:setPosition(self.scv_width - x, 0)
        end
    else            --item数增多
        self.inner:setPosition(self.pre_innerX, 0)
    end
end


function scroll2:get_current_state()  --离开标签时获取信息 “info”
    local innerX = self.inner:getPositionX() --有两种情况要调整位置
    if self.iHead == 1 then
        if innerX > 0 then
            innerX = 0
        end
    end
    if self.iTail == #self.data then
        local x = 2 * MARGIN_LEFT + (self.iTail - self.iHead + 1) * (self.item_width + MARGIN_BETWEEN) - MARGIN_BETWEEN
        x = math.max(self.scv_width, x)
        if innerX < self.scv_width - x then
            innerX = self.scv_width - x
        end
    end
    return {self.iHead, self.iTail, innerX}
end


function scroll2:set_current_state(state) --再次回到标签,先调用set_MARGIN_BETWEEN, create,再用参数“info”调
    ccprint("set_current_state 2 enter")
    self.pre_iHead = self.iHead  -- 考虑到之前的标签会有items个数变化
    self.iHead = state[1]
    self:reuse_pre_items()
    self:tail_tail_head_head()
    if self.iTail - self.iHead == state[2] - state[1] and self.iHead == state[1] then
        self.inner:setPosition(state[3], 0)
    else
        local x = 2 * MARGIN_LEFT + (self.iTail - self.iHead + 1) * (self.item_width + MARGIN_BETWEEN) - MARGIN_BETWEEN
        x = math.max(self.scv_width, x)
        self.inner:setPosition(self.scv_width - x, 0)
    end
end


function scroll2:register_item_touch()
    self.item_touch_enabled = true
end


function scroll2:unregister_item_touch()
    self.item_touch_enabled = false
end


function scroll2:onExit()
    ccprint("onExit 2 enter")
    if self.handle then
        cc.Director:getInstance():getScheduler():unscheduleScriptEntry(self.handle)
        self.handle = nil
    end
    if self.listener1 then
--        self.modSelf._core:getEventDispatcher():removeEventListener(self.listener1)
        cc.Director:getInstance():getEventDispatcher():removeEventListener(self.listener1)
        self.listener1 = nil
    end
    if self.item then
        self.item:release()
        self.item = nil
    end
    if self.item_touch_enabled then
        self.item_touch_enabled = false
    end
    if self.iHead then
        for i = self.iHead, self.iTail do
            self['item' .. i]:removeFromParent()
            self['item' .. i] = nil
        end
        self.iHead = nil
    end
end


----------------------------------------------------------------------------
------------------------------以上函数外部可调用-----------------------------
----------------------------------------------------------------------------


function scroll2:get_the_item_touched(x, y) --注意前提:缩放均为 100% ,scv的父源点和世界源点重合
    local scv_x = self.scv:getPositionX()
    local scv_y = self.scv:getPositionY()
    local inner_x = self.inner:getPositionX()
    local inner_y = self.inner:getPositionY()
    local head_x = self['item' .. self.iHead]:getPositionX()
    local head_y = self['item' .. self.iHead]:getPositionY()
    x = x - scv_x
    y = y - scv_y
    x = x - inner_x
    y = y - inner_y
    x = x - head_x
    y = y - head_y
    --x = x * -1    -- notice
    --y = y * -1
    x = x + MARGIN_BETWEEN / 2
    local num_x = math.ceil(x / (self.item_width + MARGIN_BETWEEN))
    if num_x < 1 or self.iHead + num_x - 1 > #self.data then
        return false
    end
    if y < 0 or y > self.item_height then
        return false
    end
    local item_i = self.iHead + num_x - 1
    local modX = x % (self.item_width + MARGIN_BETWEEN)
    local modY = y % self.item_height
    return item_i, modX, modY
end


function scroll2:set_an_item( item, i)
    self.modSelf:set_an_item(item, i) 
end




function scroll2:reuse_pre_items()  -- 包括复用已有的item资源和删除多余资源和创建新资源
    local j = self.pre_iHead  -- the previous index of head row
    while self['item' .. j] do -- 必须重命名,不然会错,比如从第1item(1,2,3)跳转至第2item(2,3,4),这样1,2,3 和 2,3,4有交错 
        self['item_pre' .. j] = self['item' .. j]
        self['item' .. j] = nil
        j = j + 1
    end
    self:set_new_iHead_iTail()
    local width = 2 * MARGIN_LEFT + (self.iTail - self.iHead + 1) * (self.item_width + MARGIN_BETWEEN) - MARGIN_BETWEEN --现在所需的inner宽度
    width = math.max(self.scv_width, width)
    self.inner:setContentSize(cc.size(width, self.scv_height))
    self.inner:setPosition(0, 0) -- 水平滑动要加上这句
    local i = self.iHead  -- the current index of head item
          j = self.pre_iHead  -- the previous index of head item
    local posX = MARGIN_LEFT
    while i <= self.iTail do  
        if not self['item_pre' .. j] then
            self['item_pre' .. j] = self.item:clone()
            self.inner:addChild(self['item_pre' .. j])
        end
        self['item_pre' .. j]:setPosition(posX, ITEM_Y)
        self['item' .. i] = self['item_pre' .. j]
        self['item_pre' .. j] = nil
        self:set_an_item(self['item' .. i], i)
        posX = posX + self.item_width + MARGIN_BETWEEN
        i = i + 1
        j = j + 1
     end
     while self['item_pre' .. j] do  --把多余的释放并设为nil
        self['item_pre' .. j]:removeFromParent()
        self['item_pre' .. j] = nil
        j = j + 1
     end
end


function scroll2:head_tail_head_tail()
    self.count = 0
    if self.count < 2 then
        self:one_more_item_to_head()
    end
    if self.count < 2 then
        self:one_more_item_to_tail()
    end
    if self.count < 2 then
        self:one_more_item_to_head()
    end
    if self.count < 2 then
        self:one_more_item_to_tail()
    end
end


function scroll2:tail_tail_head_head()
    self.count = 0
    if self.count < 2 then
        self:one_more_item_to_tail()
    end
    if self.count < 2 then
        self:one_more_item_to_tail()
    end
    if self.count < 2 then
        self:one_more_item_to_head()
    end
    if self.count < 2 then
        self:one_more_item_to_head()
    end
end


function scroll2:one_more_item_to_head()
    if self.iHead == 1 then
        return
    end
    self.count = self.count + 1
    local width = self.inner:getContentSize().width
    width = width + (self.item_width + MARGIN_BETWEEN)
    self.inner:setContentSize(cc.size(width, self.scv_height)) --横向scv中和setInnerContainerSize延伸方向相同
    self['item' .. self.iHead - 1] = self.item:clone()
    self['item' .. self.iHead - 1]:setPosition(MARGIN_LEFT, ITEM_Y)
    self.inner:addChild(self['item' .. self.iHead - 1])
    local posX = MARGIN_LEFT
    for i = self.iHead, self.iTail do
        posX = posX + self.item_width + MARGIN_BETWEEN
        self['item' .. i]:setPosition(posX, ITEM_Y)
    end
    local innerX = self.inner:getPositionX()    --self.inner:getPositionX()不一定是0,比如两次往头部加
    innerX = innerX - (self.item_width + MARGIN_BETWEEN)
    self.inner:setPosition(innerX, 0)
    self.iHead = self.iHead - 1
    self:set_an_item(self['item' .. self.iHead], self.iHead)
end


function scroll2:one_more_item_to_tail()
    if self.iTail == #self.data then
        return
    end
    self.count = self.count + 1
    local width = self.inner:getContentSize().width
    width = width + (self.item_width + MARGIN_BETWEEN)
    self.inner:setContentSize(cc.size(width, self.scv_height))
    self['item' .. self.iTail + 1] = self.item:clone()
    local x = MARGIN_LEFT + (self.iTail - self.iHead + 1) * (self.item_width + MARGIN_BETWEEN) --这里容易写错 ,写成 self.iTail - self.iHead + 1 - 1
    self['item' .. self.iTail + 1]:setPosition(x, ITEM_Y)
    self.inner:addChild(self['item' .. self.iTail + 1])
    self.iTail = self.iTail + 1
    self:set_an_item(self['item' .. self.iTail], self.iTail)
end




--当删除或添加items后应该调整之前的头尾位置
function scroll2:set_new_iHead_iTail()
    self.visible_items = math.ceil(self.scv_width / (self.item_width + MARGIN_BETWEEN)) --可视域item数
    self.iTail = self.iHead + self.visible_items - 1
    self.iTail = math.min(#self.data, self.iTail)
    self.iHead = self.iTail - self.visible_items + 1
    self.iHead = math.max(1, self.iHead)
end


function scroll2:monitor()
    local innerX = self.inner:getPositionX()
    if self.delX < 0 and innerX < THRESHOLD then --把头item移至尾后
        if self.iTail == #self.data then
            return
        end
        local posX = MARGIN_LEFT
        for i = self.iHead + 1, self.iTail do
            self['item' .. i]:setPosition(posX, ITEM_Y)
            posX = posX + (self.item_width + MARGIN_BETWEEN)
        end
        self.inner:setPosition(innerX + (self.item_width + MARGIN_BETWEEN), 0)
        self['item' .. self.iHead]:setPosition(MARGIN_LEFT + (self.iTail - self.iHead + 1 - 1) * (self.item_width + MARGIN_BETWEEN), ITEM_Y)
        self['item' .. self.iTail + 1] = self['item' .. self.iHead]
        self['item' .. self.iHead] = nil
        self.iHead = self.iHead + 1
        self.iTail = self.iTail + 1
        self:set_an_item(self['item' .. self.iTail], self.iTail)
        if self.modSelf.item_change_i_to_j then
           self.modSelf:item_change_i_to_j(self['item'.. self.iTail], self.iHead - 1, self.iTail) --从第i个变成第j个
        end
    end
    if self.delX > 0 and innerX > THRESHOLD + self.item_width + MARGIN_BETWEEN  then   --这里的第二个判断不能为上面第二个判断的补集,否则滑动会出现空白
        if self.iHead == 1 then
            return
        end
        local posX = MARGIN_LEFT
        for i = self.iHead, self.iTail - 1 do
            posX = posX + (self.item_width + MARGIN_BETWEEN)
            self['item' .. i]:setPosition(posX, ITEM_Y)
        end
        self.inner:setPosition(innerX - (self.item_width + MARGIN_BETWEEN), 0)
        self['item' .. self.iTail]:setPosition(MARGIN_LEFT, ITEM_Y)
        self['item' .. self.iHead - 1] = self['item' .. self.iTail]
        self['item' .. self.iTail] = nil
        self.iHead = self.iHead - 1
        self.iTail = self.iTail - 1
        self:set_an_item(self['item' .. self.iHead], self.iHead)
        if self.modSelf.item_change_i_to_j then
           self.modSelf:item_change_i_to_j(self['item'.. self.iHead], self.iTail + 1, self.iHead) --从第i个变成第j个
        end
    end
end


_G['scroll2'] = scroll2
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值