cocos2dx-lua使用UIListView製做二級摺疊菜單

摺疊菜單,用過jquery accordion的同窗都知道是啥玩藝兒~,圖片效果就是介樣:jquery

cocos2dx不帶有此控件,所以咱們動手來實現一個。json

原理很簡單,展開的時候往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派生而來,這們作的好處是咱們能夠方便的更改摺疊菜單標題項和內容項的樣式。ui

下面來看看uimenuitem.lua: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
複製代碼

下面舉個簡單的使用例子,spa

複製代碼
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 ) )
複製代碼

這裏方便演示手寫了分類數據,但通常狀況下是用遍歷生成的。經過上面這段代碼,咱們生成了一個摺疊菜單,效果即本文首部的演示圖片。3d

這裏用到的兩個類gm.Market.MarketMenuTitle和gm.Market.MarketMenuItem便是用來自定義摺疊菜單樣式的,代碼也一塊貼出:code

複製代碼
--[[
    市場摺疊菜單標題項

    @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

摺疊菜單,用過jquery accordion的同窗都知道是啥玩藝兒~,圖片效果就是介樣:對象

cocos2dx不帶有此控件,所以咱們動手來實現一個。blog

原理很簡單,展開的時候往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
相關文章
相關標籤/搜索