[Corona SDK] 程式設計教學:在應用程式中存取偏好設定

PUBLISHED ON FEB 25, 2019 — MOBILE
Facebook Twitter LinkedIn LINE Skype EverNote GMail Yahoo Email

    在先前的範例中,每次專案重開時,程式又會回到初始狀態,這時因為我們沒有永久性地 (persistently) 儲存程式的狀態 (states)。在實務上,我們會儲存程式的狀態,像是使用者的偏好 (preferences)、遊戲的最高分數等;下次程式使用者重開程式時,又會回到先前的狀態。

    在電腦程式中,這類議題和持久性 (persistence) 相關。在實務上,我們會透過硬碟 (hard disc) 這類非揮發性 (non-volatile) 儲存裝置將程式的狀態儲存起來。近年來流行的雲端儲存 (cloud storage) 其實仍是存在某個遠端的 (remote) 伺服器上的硬碟,故這個概念仍可通用。

    在本文中,我們探討 Corona 在本地端的 (local) 持久性方案。一般來說,有兩種方式:

    • 檔案 (flat files),像是 JSON 檔
    • 資料庫 (databases),常見的是 SQLite 這類內嵌性資料庫

    這兩者的差別在於需不需要資料庫索引的特性,JSON 像是一個簡單的鍵-值資料檔,而 SQLite 則是一個具體而微的資料庫。一般來說,使用者偏好設定會存在 JSON 檔案裡,而 TODO 清單這類資料就會存在 SQLite 資料庫中。

    由於存取系統偏好設定是一個常見的功能,Corona 團隊將這個功能進行進一步地封裝,程式人只要用 Corona 提供的 API 來存取偏好,不用煩惱內部使用什麼方式來儲存 (可看這裡)。本範例即是用這項特性來儲存程式狀態;我們將完整的程式碼放在這裡,本文僅節錄部分內容。

    本範例程式的示意圖如下:

    Corona SDK 的 segmented control 和 slider

    基本上,這個範例和先前的範例在功能上大抵雷同,只是加上持久性狀態的功能。

    一開始,我們設置一些程式會用到的基本數值:

    -- Set valid colors.
    local color = {}
    color.red = {255, 0, 0}
    color.orange = {255, 165, 0}
    color.yellow = {255, 255, 0}
    color.green = {0, 255, 0}
    color.blue = {0, 0, 255}
    color.purple = {128, 0, 128}
    
    -- Set valid sizes for different shapes.
    local sizeCircle = {}
    sizeCircle.small = 30
    sizeCircle.medium = 40
    sizeCircle.large = 50
    
    local sizeRect = {}
    sizeRect.small = {60, 36}
    sizeRect.medium = {80, 48}
    sizeRect.large = {100, 60}
    
    local sizeTriangle = {}
    sizeTriangle.small = {-20, -25, -20, 25, 30, 0}
    sizeTriangle.medium = {-30, -35, -30, 35, 40, 0}
    sizeTriangle.large = {-40, -45, -40, 45, 50, 0}
    
    local size = {}
    size.circle = sizeCircle
    size.rectangle = sizeRect
    size.triangle = sizeTriangle
    
    -- Set location for our item.
    local itemLocX = 0.5
    local itemLocY = 0.2
    
    -- Set the list of valid options.
    local sizes = { "small", "medium", "large" }
    local colors = { "red", "orange", "yellow", "green", "blue", "purple" }
    local shapes = { "circle", "rectangle", "triangle" }

    在這個範例中,我們將生成多邊形的函式獨立出來,因為我們在程式啟動及事件處理器中都會用到這個函式。程式碼參考如下:

    local function getShape(options)
      local item
    
      if options.shape == "circle" then
        item = display.newCircle(options.x, options.y, size[options.shape][options.size])
      elseif options.shape == "rectangle" then
        item = display.newRoundedRect(options.x, options.y,
          size[options.shape][options.size][1], size[options.shape][options.size][2], 10)
      elseif options.shape == "triangle" then
        item = display.newPolygon(options.x, options.y,
          {size[options.shape][options.size][1], size[options.shape][options.size][2],
            size[options.shape][options.size][3], size[options.shape][options.size][4],
            size[options.shape][options.size][5], size[options.shape][options.size][6]})
      end
    
      item:setFillColor(color[options.color][1] / 255, color[options.color][2] / 255, color[options.color][3] / 255)
    
      return item
    end

    這個函式是我們要從 PickerWheel 中取得索引值用的:

    local function getIndex(list, item)
      for i = 1, #list do
        if item == list[i] then
          return i
        end
      end
    end

    在程式一開始時,我們讀取偏好值:

    -- Get system prefs.
    local mySize = system.getPreference("app", "mySize")
    if mySize == nil then
      mySize = "medium"
    end
    
    local myColor = system.getPreference("app", "myColor")
    if myColor == nil then
      myColor = "orange"
    end
    
    local myShape = system.getPreference("app", "myShape")
    print("myShape: " .. myShape)
    if myShape == nil then
      myShape = "circle"
    end

    由於程式第一次啟動時,偏好值為空值,故我們在程式中要檢查這個情形,並塞入起始值。

    在程式啟動時建立起始的 item 多邊形物件:

    -- Init `item`.
    local item = getShape({
      x = display.contentWidth * itemLocX,
      y = display.contentHeight * itemLocY,
      size = mySize,
      color = myColor,
      shape = myShape,
    })

    建立 PickerWheel 物件:

    -- Set initial columnData by system prefs.
    local columnData = {
      {
        align = "left",
        width = 100,
        startIndex = getIndex(sizes, mySize),
        labels = sizes,
      },
      {
        align = "left",
        width = 110,
        startIndex = getIndex(colors, myColor),
        labels = colors,
      },
      {
        align = "left",
        startIndex = getIndex(shapes, myShape),
        labels = shapes,
      }
    }
    
    -- Create a PickerWheel object.

    這裡和先前的範例不同,我們不把起始值寫死,而是藉由讀取系統偏好來動態地調整 PickerWheel 的起始位置。

    同樣地,我們用 timer 物件來監看 PickerWheel 的值,並動態地改變 item 物件:

    -- The event listener for `pickerWheel`.
    local onValueSelected = function ()
      local values = pickerWheel:getValues()
    
      local _size = values[1].value
      local _color = values[2].value
      local _shape = values[3].value
    
      item:removeSelf()
    
      item = getShape({
        x = display.contentWidth * itemLocX,
        y = display.contentHeight * itemLocY,
        size = _size,
        color = _color,
        shape = _shape,
      })
    
      system.setPreferences("app", {
        mySize = _size,
        myColor = _color,
        myShape = _shape,
      })
    end
    
    -- Update `item` by current selection.
    timer.performWithDelay(100, onValueSelected, -1)

    在這段程式碼中,除了修改 item 外,我們也將程式狀態存在偏好中,下次啟動程式時就會從同樣的狀態回復。

    你或許對以下產品有興趣:
    © 2014-2019. Michael Chen
    All code in the website is licensed under Apache 2.0 unless otherwise mentioned.