【在主画面加入捷径】
       
【选择语系】
繁中 简中

[Corona SDK] 程序设计教学:存取偏好设定

MOBILE
【赞助商连结】

    在先前的范例中,每次项目重开时,程序又会回到初始状态,这时因为我们没有永久性地 (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 外,我们也将程序状态存在偏好中,下次启动程序时就会从同样的状态回复。

    【赞助商连结】