• cocos2dx-lua使用UIListView制作二级折叠菜单


    折叠菜单,用过jquery accordion的同学都知道是啥玩艺儿~,图片效果就是介样:

    cocos2dx不带有此控件,因此我们动手来实现一个。

    原理很简单,展开的时候往listview里insertCustomItem,收起的时候从listview里removeItem。下面给出一个功能丰富的折叠菜单控件。
    先看控件主类:

    --[[
        二级折叠菜单组
    
        构造:
        local groupData = {
            {
                title = '分类1',
                items = {
                    { id = 1, count = 20, lv = 19 },
                    { id = 2, count = 21, lv = 21 },
                    { id = 3, count = 22, lv = 23 },
                }
            },
            {
                title = '图腾',
                items = {
                    { id = 500001, count = 32, lv = 19 },
                    { id = 500002, count = 21, lv = 65 },
                    { id = 500003, count = 22, lv = 27 },
                }
            },
            {
                title = '武器',
                items = {
                    { id = 101002, count = 20, lv = 45 },
                    { id = 101003, count = 21, lv = 34 },
                }
            },
        }
        self._goodsCateList = gm.Common.UIMenuList.new( cc.size( 310, 510 ), groupData )
    
        -- 必须设置标题类型和内容类型,重写可改变样式
        self._goodsCateList:setClass( gm.Market.MarketMenuTitle, gm.Market.MarketMenuItem )
        
        -- 可选参数,是否可同时展开多个分组,默认false
        self._goodsCateList.showMulti = true
    
        -- 可选参数,是否默认选中组中第一个item
        self._goodsCateList.autoSelectFirstItem = true
    
        -- 可选参数,选中的title索引,默认为1
        self._goodsCateList.defaultGroup = 1
    
        -- 可选参数,选中的item索引,默认为1
        self._goodsCateList.defaultItem = 1
    
        加入舞台:
        self._goodsCateList:addToParent( self._bg, { x = 0, y = 0 } )
    
        @author cc
    --]]
    
    gm = gm or {}
    rm = rm or {} 
    
    local gm = gm
    local rm = rm 
    
    gm.Common                       = gm.Common or {}
    gm.Common.UIMenuList            = class( "Common.UIMenuList" )
    gm.Common.UIMenuList._name      = "Common.UIMenuList"
    
    local g = gm.Common.UIMenuList
    
    ---------------------------------------------------------------------------------
    --                                                                             --
    --                          以下定义私有部分                                   --
    --                                                                             --
    ---------------------------------------------------------------------------------
    
    local function log( ... )
        print( '>>>>gm.Common.UIMenuList<<<<:', ...)
    end
    
    -- 点击标题项
    function g:_onTouchTitle( sender )
        local titleInst = sender._inst
    
        -- 收缩点击项
        if titleInst.selected then
            self:_unSelectTitleInst( titleInst )
            return
        end
    
        -- 如果同时只展开一个分组,则隐藏上次展开的
        if not self._showMulti and self._selectedTitle then
            self:_unSelectTitleInst( self._selectedTitle )
        end
    
        -- 展开点击项
        local index = self._listView:getIndex( sender )
        local group = titleInst.data
        local itemInst
        for itemIdx, itemData in ipairs( group.items ) do
            itemInst = self:_createItemInst( itemData )
            self._listView:insertCustomItem( itemInst.ui, index + itemIdx )
    
            -- 将第一项设置为当前选中的内容项
            if self._autoSelectFirstItem and itemIdx == 1 then
                self.selectedItem = itemInst
            end 
        end
    
        -- 设置当前选中的标题项
        self.selectedTitle = titleInst
    end
    
    -- 点击内容项
    function g:_onTouchItem( sender, eventType )
        if eventType == ccui.TouchEventType.ended then
            self.selectedItem = sender._inst
        end
        if self._itemClickFunc then
            self._itemClickFunc( sender, eventType )
        end
    end
    
    -- 收起标题项对应的分组
    function g:_unSelectTitleInst( titleInst )
        local index = self._listView:getIndex( titleInst.ui )
        local group = titleInst.data
        local itemInst
    
        for i = 1, #group.items do
    
            -- 析构删除项
            itemInst = self._listView:getItem( index + 1 )._inst
            itemInst:finalize()
    
            -- 选中项被删除
            if self._selectedItem and self._selectedItem == itemInst then
                self._selectedItem = nil
            end
    
            self._listView:removeItem( index + 1 )
        end
        titleInst.selected = false
    
        if titleInst == self._selectedTitle then
            self._selectedTitle = nil
        end
    end
    
    -- 创建标题项
    function g:_createTitleInst( data )
        if not self._titleCls then
            log( '没有设置标题类型!' )
            return
        end
    
        local titleInst = self._titleCls.new( )
        titleInst.data = data
        titleInst.ui._inst = titleInst
        makeTouchHandle( self, titleInst.ui, self._onTouchTitle )
        return titleInst
    end
    
    -- 创建内容项
    function g:_createItemInst( data )
        if not self._itemCls then
            log( '没有设置内容类型!' )
            return
        end
    
        local itemInst = self._itemCls.new( )
        itemInst.data = data
        itemInst.ui._inst = itemInst
        makeTouchHandle2( self, itemInst.ui, self._onTouchItem )
        return itemInst
    end
    
    -- 初始化
    function g:_initialize( )
        self._listView = ccui.ListView:create()
        self._listView:setBounceEnabled( true )
        self._listView:setDirection( ccui.ScrollViewDir.vertical )
        self._listView:setSize( self._size )
    
        if not self._groupData then
            log( '分组数据为空!' )
            return
        end
        
        local titleInst, itemInst
        self._titleInstList = {}
    
        for groupIdx, group in ipairs( self._groupData ) do
    
            titleInst = self:_createTitleInst( group )
            self._listView:pushBackCustomItem( titleInst.ui )
            table.insert( self._titleInstList, titleInst )
    
            -- 展开默认分组
            if self._defaultGroupIdx and groupIdx == self._defaultGroupIdx then
                self.selectedTitle = titleInst
                
                for itemIdx, itemData in ipairs( group.items ) do
    
                    itemInst = self:_createItemInst( itemData )
                    self._listView:pushBackCustomItem( itemInst.ui )
    
                    -- 选中默认项
                    if self._defaultItemIdx and itemIdx == self._defaultItemIdx then
                        self.selectedItem = itemInst
                    end
                end
            end
        end
    end
    
    ---------------------------------------------------------------------------------
    --                                                                               --
    --                        以下定义set, get 部分函数                               --
    --                                                                               --
    ---------------------------------------------------------------------------------
    
    function g:_getUi( )
        return self._listView
    end
    
    -- 是否同时展开多个组
    function g:_setShowMulti( value )
        self._showMulti = value
    end
    
    -- 获取数据
    function g:_getGroupData( )
        return self._groupData
    end
    
    function g:_setGroupData( value )
        self._groupData = value
    end
    
    -- 设置默认展开的分组索引
    -- @i value
    function g:_setDefaultGroup( value )
        self._defaultGroupIdx = value
    end
    
    -- 设置默认选中的item索引
    --@i value 
    function g:_setDefaultItem( value )
        self._defaultItemIdx = value
    end
    
    -- 获取选中的标题项
    function g:_getSelectedTitle( )
        return self._selectedTitle
    end
    
    function g:_setSelectedTitle( value )
        value.selected = true
        self._selectedTitle = value
        rm.BindManager.propertyChanged( self, "selectedTitle" )
    end
    
    -- 获取选中的内容项
    function g:_getSelectedItem( )
        return self._selectedItem
    end
    
    function g:_setSelectedItem( value )
        if self._selectedItem then
            self._selectedItem.selected = false 
        end
        value.selected = true
        self._selectedItem = value
        rm.BindManager.propertyChanged( self, "selectedItem" )
    end
    
    -- 设置点击item项的回调函数
    function g:_setItemClickFunc( value )
        self._itemClickFunc = value
    end
    
    -- 设置是否自动选中组中第一个item
    function g:_setAutoSelectFirstItem( value )
        self._autoSelectFirstItem = value
    end
    
    ---------------------------------------------------------------------------------
    --                                                                             --
    --                          以下定义公共部分                                   --
    --                                                                             --
    ---------------------------------------------------------------------------------
    
    -- 构造
    -- @t size cc.size类型
    -- @t groupData 分组数据,结构如{ { title = tData, items = { iData1, iData2 } }, ... }
    function g:ctor( size, groupData )
        self._size = size or cc.size( 200, 200 )
        self._groupData = groupData
        self._titleInstList = nil
        self._defaultGroupIdx = nil
        self._defaultItemIdx = nil
        self._selectedTitle = nil
        self._selectedItem = nil
        self._titleCls = nil
        self._itemCls = nil
        self._showMulti = false
        self._autoSelectFirstItem = false
        self._itemClickFunc = nil
    end
    
    -- 设置标题项和内容项类型
    -- @t titleClass 继承自gm.Common.UIMenuItem,重写可改变样式
    -- @t itemClass  继承自gm.Common.UIMenuItem,重写可改变样式
    function g:setClass( titleClass, itemClass )
        self._titleCls = titleClass
        self._itemCls = itemClass
    end
    
    -- 添加到舞台
    -- @widget parent 父显示对象
    -- @t        pos    位置,{ x = , y = }
    -- @i        zOrder 层级
    function g:addToParent( parent, pos, zOrder )
        if not parent then
            log( '父显示对象不可为空!' )
            return
        end
        self:_initialize()
        zOrder = zOrder or 1
        pos = pos or cc.p( 0, 0 )
        self._listView:setPosition( pos )
        parent:addChild( self._listView, zOrder )
    end
    
    -- 刷新当前展开的分组,只可在互斥(showMulti = false)模式下使用
    -- @t group 数据,结构如{ title = tData, items = { iData1, iData2 } }
    function g:refreshSelectedTitle( group )
        if not self._selectedTitle then return end
        self:refreshTitle( self._selectedTitle, group )
    end
    
    -- 刷新指定位置的分组
    -- @i titleIndex     分组索引
    -- @t group         组数据
    function g:refreshTitleAtIndex( titleIndex, group )
        local title = self._titleInstList( titleIndex )
        self:refreshTitle( title, group )
    end
    
    -- 刷新指定分组
    -- @t             某个分组
    -- @t group     组数据
    function g:refreshTitle( title, group )
        local index = self._listView:getIndex( title.ui )
        local prevNumItems = title.selected and #title.data.items or 0
        local currNumItems = #group.items
        local deltaNum = prevNumItems - currNumItems
        local itemInst
    
        -- 设置title数据
        title:_setData( group )
        self._groupData[ table.indexOf( self._titleInstList, title ) ] = group
    
        -- title没展开,以下无需执行
        if not title.selected then return end
    
        -- 设置item数据
        for itemIdx, itemData in ipairs( group.items ) do
            if itemIdx <= prevNumItems then
                itemInst = self._listView:getItem( index + itemIdx )._inst    
                itemInst:_setData( itemData )
            else
                itemInst = self:_createItemInst( itemData )
                self._listView:insertCustomItem( itemInst.ui, index + itemIdx )
            end
        end
    
        -- 旧数量比当前数量多,需要删除多余的item
        if deltaNum > 0 then
            for i = 1, deltaNum do
                itemInst = self._listView:getItem( index + currNumItems + 1 )._inst
                itemInst:finalize()
    
                -- 选中项被删除
                if self._selectedItem and self._selectedItem == itemInst then
                    self._selectedItem = nil
                end
    
                self._listView:removeItem( index + currNumItems + 1 )
            end
        end
    end
    
    -- 刷新整个控件
    -- @t 控件数据,结构如构造函数同名参数所示
    function g:refresh( groupData )
        self._groupData = groupData
        if not self._groupData then
            log( '分组数据为空!' )
            return
        end
    
        local prevNumTitles = #self._titleInstList
        local currNumTitles = #groupData
        local deltaNum = prevNumTitles - currNumTitles
        local titleInst
        
        -- 刷新所有分组
        for groupIdx, group in ipairs( groupData ) do
            if groupIdx <= prevNumTitles then 
                titleInst = self._titleInstList[ groupIdx ]
                self:refreshTitle( titleInst, group )
            else
                titleInst = self:_createTitleInst( group )
                self._listView:pushBackCustomItem( titleInst.ui )
                table.insert( self._titleInstList, titleInst )
            end
        end
        
        -- 新分组比旧分组少,需要删除多余的title
        if deltaNum > 0 then
            local numListItems = #self._listView:getItems()
    
            -- 清空全部
            if currNumTitles == 0 then
                for i = numListItems - 1, 0, -1 do
                    titleInst = self._listView:getItem( i )._inst
                    titleInst:finalize()
                end
                self._listView:removeAllItems()
                self._titleInstList = {}
                self._selectedTitle = nil
                self._selectedItem = nil
                return
            end
    
            local lastTitleInst = self._titleInstList[ currNumTitles ]
            local index = self._listView:getIndex( lastTitleInst.ui ) + ( lastTitleInst.selected and #lastTitleInst.data.items or 0 )
    
            for i = numListItems - 1, index + 1, -1 do
                -- 析构被删除的title
                titleInst = self._listView:getItem( i )._inst
                titleInst:finalize()
    
                -- 选中title被删除
                if self._selectedTitle and self._selectedTitle == titleInst then
                    self._selectedTitle = nil
                end
    
                -- 选中项被删除
                if self._selectedItem and self._selectedItem == titleInst then
                    self._selectedItem = nil
                end
    
                self._listView:removeItem( i )
            end
    
            -- 从title列表中移除
            for i = prevNumTitles, currNumTitles + 1, -1 do
                table.remove( self._titleInstList, i )
            end
        end
    end
    
    -- 析构
    function g:finalize()
        if self._listView then
            self._listView:removeFromParent()
            self._listView = nil
        end
        self._titleInstList = nil
        self._groupData = nil
        self._itemClickFunc = nil
    end

     我们把折叠菜单抽象成标题项(title)和内容项(item),title即指标题项,展开标题项显示出来的内容项叫item。title和item均从uimenuitem派生而来,这们做的好处是我们可以方便的更改折叠菜单标题项和内容项的样式。

    下面来看看uimenuitem.lua:

    --[[
        折叠菜单项
    
        @author cc
    --]]
    
    gm = gm or {}
    rm = rm or {} 
    
    local gm = gm
    local rm = rm 
    
    gm.Common                       = gm.Common or {}
    gm.Common.UIMenuItem            = class( "Common.UIMenuItem" )
    gm.Common.UIMenuItem._name      = "Common.UIMenuItem"
    
    local g = gm.Common.UIMenuItem
    
    ---------------------------------------------------------------------------------
    --                                                                             --
    --                          以下定义私有部分                                   --
    --                                                                             --
    ---------------------------------------------------------------------------------
    
    
    ---------------------------------------------------------------------------------
    --                                                                               --
    --                        以下定义set, get 部分函数                               --
    --                                                                               --
    ---------------------------------------------------------------------------------
    
    -- 获取显示对象
    function g:_getUi( )
        return self._ui
    end
    
    function g:_getData( )
        return self._data
    end
    
    -- 设置数据
    function g:_setData( value )
        self._data = value
    end
    
    function g:_getSelected( )
        return self._selected
    end
    
    -- 设置选中状态
    function g:_setSelected( value )
        self._selected = value
    end
    
    ---------------------------------------------------------------------------------
    --                                                                             --
    --                          以下定义公共部分                                   --
    --                                                                             --
    ---------------------------------------------------------------------------------
    
    -- 构造
    function g:ctor( )
        self._selected = false
        self._data = nil
    
        self:initialize()
    end
    
    -- 初始化
    function g:initialize( )
        self._ui = nil
    end
    
    -- 析构
    function g:finalize( )
        if self._ui then
            self._ui:removeFromParent( )
            self._ui = nil
        end
    end

    下面举个简单的使用例子,

    local groupData = {
            {
                title = { cate = 1, cate_desc = '技能书' },
                items = {
                    {
                        sub_cate = 1, sub_cate_desc = '一级技能书',
                    },
                    {
                        sub_cate = 2, sub_cate_desc = '二级技能书',
                    },
                    {
                        sub_cate = 3, sub_cate_desc = '三级技能书',
                    }
                }
            },
            {
                title = { cate = 2, cate_desc = '宝石' },
                items = {
                    {
                        sub_cate = 1, sub_cate_desc = '攻击宝石',
                    },
                    {
                        sub_cate = 2, sub_cate_desc = '暴击宝石',
                    }
                }
            },
            {
                title = { cate = 3, cate_desc = '月石' },
                items = {
                    {
                        sub_cate = 1, sub_cate_desc = '很好的月石',
                    }
                }
            },
        }
    
        self._goodsCateList = gm.Common.UIMenuList.new( cc.size( 204, 482 ), groupData )
        self._goodsCateList:setClass( gm.Market.MarketMenuTitle, gm.Market.MarketMenuItem )
        self._goodsCateList.showMulti = true
        self._goodsCateList.defaultGroup = 1
        self._goodsCateList.defaultItem = 1
        self._goodsCateList:addToParent( self._bg, cc.p( 15, 24 ) )

    这里方便演示手写了分类数据,但一般情况下是用遍历生成的。通过上面这段代码,我们生成了一个折叠菜单,效果即本文首部的演示图片。

    这里用到的两个类gm.Market.MarketMenuTitle和gm.Market.MarketMenuItem即是用来自定义折叠菜单样式的,代码也一块贴出:

    --[[
        市场折叠菜单标题项
    
        @author cc
    --]]
    
    gm = gm or {}
    rm = rm or {} 
    
    local gm = gm
    local rm = rm 
    
    gm.Market                               = gm.Market or {}
    gm.Market.MarketMenuTitle            = class( "Market.MarketMenuTitle", gm.Common.UIMenuItem )
    gm.Market.MarketMenuTitle._name      = "Market.MarketMenuTitle"
    
    local g = gm.Market.MarketMenuTitle
    
    ---------------------------------------------------------------------------------
    --                                                                             --
    --                          以下定义私有部分                                   --
    --                                                                             --
    ---------------------------------------------------------------------------------
    
    
    ---------------------------------------------------------------------------------
    --                                                                               --
    --                        以下定义set, get 部分函数                               --
    --                                                                               --
    ---------------------------------------------------------------------------------
    
    -- 设置数据
    function g:_setData( value )
        self._data = value
    
        self._labelName:setText( value.title.cate_desc )
    end
    
    -- 设置选中状态
    function g:_setSelected( value )
        self._selected = value
    
        local texture = value and 'tab_btn_4.png' or 'tab_btn_3.png'
        self._imgBg:loadTexture( texture, ccui.TextureResType.plistType )
    end
    
    ---------------------------------------------------------------------------------
    --                                                                             --
    --                          以下定义公共部分                                   --
    --                                                                             --
    ---------------------------------------------------------------------------------
    
    -- -- 构造
    -- function g:ctor( )
    --     g.super.ctor( self )
    -- end
    
    -- 初始化
    function g:initialize( )
        g.super.initialize( self )
        self._ui = ccs.GUIReader:getInstance():widgetFromJsonFile('ui/new_market_menu_title.json')
    
        self._labelName = self._ui:getChildByName('LabName')
        self._imgBg = self._ui:getChildByName('ImgBg')
    end
    
    -- -- 析构
    -- function g:finalize( )
    --     g.super.finalize( self )
    -- end
    --[[
        市场折叠菜单内容项
    
        @author cc
    --]]
    
    gm = gm or {}
    rm = rm or {} 
    
    local gm = gm
    local rm = rm 
    
    gm.Market                               = gm.Market or {}
    gm.Market.MarketMenuItem            = class( "Market.MarketMenuItem", gm.Common.UIMenuItem )
    gm.Market.MarketMenuItem._name      = "Market.MarketMenuItem"
    
    local g = gm.Market.MarketMenuItem
    
    ---------------------------------------------------------------------------------
    --                                                                             --
    --                          以下定义私有部分                                   --
    --                                                                             --
    ---------------------------------------------------------------------------------
    
    
    ---------------------------------------------------------------------------------
    --                                                                               --
    --                        以下定义set, get 部分函数                               --
    --                                                                               --
    ---------------------------------------------------------------------------------
    
    -- 设置数据
    function g:_setData( value )
        self._data = value
    
        self._labelName:setText( value.sub_cate_desc )
    end
    
    -- 设置选中状态
    function g:_setSelected( value )
        self._selected = value
    
        self._imgSelected:setVisible( value )
    end
    
    ---------------------------------------------------------------------------------
    --                                                                             --
    --                          以下定义公共部分                                   --
    --                                                                             --
    ---------------------------------------------------------------------------------
    
    -- 构造
    function g:ctor( )
        g.super.ctor( self )
    end
    
    -- 初始化
    function g:initialize( )
        g.super.initialize( self )
        self._ui = ccs.GUIReader:getInstance():widgetFromJsonFile('ui/new_market_menu_item.json')
    
        self._labelName = self._ui:getChildByName('LabName')
        self._imgSelected = self._ui:getChildByName('ImgSelected')
    end
    
    -- 析构
    function g:finalize( )
        g.super.finalize( self )
    end
  • 相关阅读:
    每日站立会议02
    每日站立会议01
    构建之法阅读笔记02
    学习进度条06
    js获取当前时间戳
    带三角凸的框
    js或者jq的tab切换
    处理浏览器兼容 各个浏览器的标识 hack
    规定CSS的属性仅在IE下生效 在非IE浏览器下不生效
    滚动到某个点 返回顶部字样出来 点击返回顶部
  • 原文地址:https://www.cnblogs.com/tudas/p/cocos2dx_perform_a_accordion_with_uilistview.html
Copyright © 2020-2023  润新知