bones腳本篇 - 實現一個簡單的列表

一 簡述
bones到目前爲止沒有提供基本的list控件,原因很簡單list的風格多變,基本上在PC上使用DUI的軟件 它們的列表都是自定義的。
原來打算只是寫一個簡單的列表來做個示範,恰巧前幾天看到QQ電腦管家的一個列表比較有意思所以花了點時間模擬了一下,下文將簡單講下如何寫這個列表。
二 列表結構
先看下電腦管家的列表長什麼樣 如下:
這裏寫圖片描述
這個列表左邊是個tab用於切頁, 中間是一個可滾動的列表 右邊則是滾動條
tab的切換會導致中間列表和滾動條滾動到正確的位置,點擊滾動條滾動時列表也會跟着滾動 並且在滾動結束時(通常是鼠標左鍵彈起) tab也要切換到正確的頁面
拖動滾動條
拖動滾動條
滾動結束
滾動結束
三 模擬列表
模擬列表中的用到的位置 尺寸和顏色 都是我用截圖工具測量的,所以會跟電腦管家的列表有點區別,
諸如 常用條目下的 軟件管理 電腦診所等按鈕用到的素材 我直接將前面演示按鈕的位圖縮放了一下來顯示,
滾動條還是跟以前一樣使用色塊(沒素材)。
由於缺乏各種素材 所以外觀仍然不是很好看,但作爲一個demo足夠了。
3.1 scrollbar
這裏使用 前面教程編寫的scrollbar,但是要針對這個列表的邏輯做點修改
scrollbar會在mousemove事件中發出drag通知

--self是滑塊
function mod.onSliderMouseMove(self, e)   
    if self.click_ then
        local x, y = e:getRootLoc()
        local ydelta = y - self.last_y_
        local xdelta = x - self.last_x_
        local parent = self:getParent()        
        local w, h = self:getSize()
        local cur = 0
        local horiz = parent.horiz_
        if horiz then
            cur = xdelta / w * parent.view_ + self.last_cur_
        else
            cur = ydelta / h * parent.view_ + self.last_cur_ 
        end
        if type(parent.onDelegate_) == "function" then
            parent:onDelegate_("drag", cur)
        end      
    end
end

在mouse up事件中發送release通知

function mod.onSliderMouseUp(self, e)
    if e:isLeftMouse() then
        self.click_ = false
        self.last_x_ = -1
        self.last_y_ = -1

        local parent = self:getParent()
        if type(parent.onDelegate_) == "function" then
            parent:onDelegate_("release", parent.cur_)
        end
    end
end

要響應這2個高級事件 可以調用setDelegate方法

local function setDelegate(self, delegate)
    self.onDelegate_ = delegate
end

3.2 tab
tab前文沒有講過如何寫,但這裏用到了所以還需要寫一個tab
tab由於電腦管家沒有使用圖片 所以我直接使用截圖工具 看了下像素,雖然不是完全一樣 但也差不了多少
tab使用擴展標籤tabctrl來表示,同時tab是有多個子項 這裏使用擴展標籤tabitem來表示子項
3.2.1 tabitem
tabitem的結構

  <!--底色-->
  <shape>
    <!--上-->
    <shape></shape>
    <!--下-->
    <shape></shape>
    <!--右-->
    <shape></shape>
    <!--圓點-->
    <shape></shape>
    <!--文字-->
    <text></text>
    <notify name ='onCreate' module='tabitem' func='onCreate'></notify>
    <notify name ='onSizeChanged' module='tabitem' func='onSizeChanged'></notify>
    <event name ="onMouseMove" phase ="target" module ="tabitem" func ="onMouseMove"></event>
    <event name ="onMouseLeave" phase ="target" module ="tabitem" func ="onMouseLeave"></event>
    <event name ="onMouseDown" phase ="target" module ="tabitem" func ="onMouseDown"></event>
    <event name ="onMouseUp" phase ="target" module ="tabitem" func ="onMouseUp"></event>
  </shape>

tabitem在鼠標事件中改變自己的狀態 來控制內置標籤的顯示

        if state == selected then       
            self:setColor(0xffebeffa)
            self.top_line_:setVisible(true)
            self.right_line_:setVisible(false)
            self.bottom_line_:setVisible(true)
            self.circle_:setVisible(true)
        elseif state == highlight then
            self:setColor(0xffd7dced)
            self.top_line_:setVisible(false)
            self.right_line_:setVisible(true)
            self.bottom_line_:setVisible(false)
            self.circle_:setVisible(false)

        elseif state == common then
            self:setColor(0)

            --上顏色
            self.top_line_:setVisible(false)
            --右
            self.right_line_:setVisible(true)
            --下隱藏
            self.bottom_line_:setVisible(false)
            --圓隱藏
            self.circle_:setVisible(false)
            --text不變
        end

完整代碼見tabitem.xml tabitem.lua
3.2.2 tabctrl
tabctrl由於包含5頁 結構比較簡單

  <shape>
    <!--垂直的一條線-->
    <shape></shape>
    <tabitem></tabitem>
    <tabitem></tabitem>
    <tabitem></tabitem>
    <tabitem></tabitem>
    <tabitem></tabitem>
    <notify name ='onCreate' module='tabctrl' func='onCreate'></notify>
    <notify name ='onSizeChanged' module='tabctrl' func='onSizeChanged'></notify>
  </shape>

tabctrl主要是有一個item的互斥 即一頁被選中其他頁都變爲未選中狀態

--這裏的self是指 被點擊的item
local function itemSelected(self)
    if self:isSelected() then
        return
    end

    local ctrl = self:getParent()
    local im = 0
    for i = 1, 5 do
        if self == ctrl.items_[i] then
            ctrl.items_[i]:setSelected(true)
            im = i
        else
            ctrl.items_[i]:setSelected(false)
        end
    end
    if type(ctrl.onDelegate_) == "function" then
        ctrl:onDelegate_(im)
    end
end

同樣tabctrl會發送一個頁面切換的事件,要處理這個事件同樣調用tabctrl的setDelegate

--設置回調
local function setDelegate(self, delegate)
    self.onDelegate_ = delegate
end

注意這裏tabctrl我直接包含了5個listitem節點,比較標準的寫法則是有一個addItem的方法,在這個方法裏動態調用bones.createObject來創建listitem節點,但我比較懶 所以就這麼寫了(listitem裏動態創建button節點)
完整代碼見 tabctrl.xml tabctrl.lua
3.2.3 效果圖
這裏寫圖片描述
3.3 list
中間的列表是一個可滾動顯示的控件,這裏使用擴展標籤listctrl來表示,同樣它是包含多個子項的所以也需要listitem來代表子項
3.3.1 listitem
listitem的結構:

  <!--線-->
  <shape>
    <!--標題-->
    <text></text>
    <notify name ='onCreate' module='listitem' func ='onCreate'></notify>
    <notify name ='onSizeChanged' module='listitem' func='onSizeChanged'></notify>
  </shape>

看起來比較簡單 但內部使用了動態創建button來表示響應區域的

    local item = bones.createObject(self, "button")
    item:setBitmap(common, highlight, press, disable)

完整代碼見listitem.xml listitem.lua

3.3.2 listctrl
listctrl 是可以滾動顯示的,看過前面教程的人應該可以猜到listctrl肯定是包含一個scroller的
listctrl的結構:

  <scroller>
    <notify name ='onCreate' module='listctrl' func ='onCreate'></notify>
    <!--註冊通知 關聯滾動條-->
    <notify name ="onScrollRange" module ="listctrl" func ="onScrollRange"></notify>
    <notify name ="onScrollPos" module ="listctrl" func ="onScrollPos"></notify>

    <listitem></listitem>
    <listitem></listitem>
    <listitem></listitem>
    <listitem></listitem>
    <listitem></listitem>

  </scroller>

listctrl的scroller用法同樣要做點修改,這裏修改成事件傳遞而不是想前面教程簡單粗暴的調用scrollbar

function mod.onScrollRange(self, min, max, view, horiz)
    if not horiz and type(self.onDelegate_) == "function" then
        self:onDelegate_("range", min, max, view)
    end
end

function mod.onScrollPos(self, cur, horiz)
    if not horiz and type(self.onDelegate_) == "function" then
        self:onDelegate_("pos", cur)
    end
end

它發送2個高級事件 要處理這2個事件同樣需要調用setDelegate

local function setDelegate(self, delegate)
    self.onDelegate_ = delegate
end

完整代碼見listctrl.xml listctrl.lua

3.3.3 效果圖
這裏寫圖片描述

3.4 組合
3.4.1 完整的控件
完整的列表控件結構

      <!--滾動list-->
      <shape class="list">
        <tabctrl></tabctrl>
        <!--list ctrl 就是scroller 註冊scroller通知-->
        <listctrl></listctrl>
        <!--垂直滾動條-->
        <scrollbar></scrollbar>

        <notify name ="onCreate" module ="shape" func ="onListCreate"></notify>
        <notify name ="onSizeChanged" module ="shape" func ="onListSizeChanged"></notify>

      </shape>

在onCreate中需要註冊回調來響應事件

    --先註冊回調
    self.list_:setDelegate(onListDelegate)
    self.bar_:setDelegate(onBarDelegate)   
    self.tab_:setDelegate(onTabDelegate)

對各個子控件事件的處理

--點擊tab頁產生的事件
local function onTabDelegate(self, im)
    local scroll_list = self:getParent()
    scroll_list.list_:scrollItem(im)
end
--list 滾動變化產生的事件
local function onListDelegate(self, action, min, max, view)
    local scroll_list = self:getParent()
    if "range" == action then
        scroll_list.bar_:setScrollRange(min, max, view)
    elseif "pos" == action then
        local cur = min
        scroll_list.bar_:setScrollPos(cur)
    end 
end
--scrollbar 鼠標產生的高級事件
local function onBarDelegate(self, action, cur)
    local scroll_list = self:getParent()
    --拖動滾動滑塊
    if "drag" == action then
        scroll_list.list_:setScrollPos(cur, false)
    --鼠標在滑塊上彈起 要將tab切換到list首個顯示的item
    elseif "release" == action then
        scroll_list.tab_:switchItem(scroll_list.list_:getItem(cur))
    end
end

完整代碼見test.xml test.lua

3.4.2 效果
這裏寫圖片描述

這裏寫圖片描述

本篇完整代碼下載:
http://blog.csdn.net/dalixux/article/details/48830721

發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章