返回博客首页
← 所有文章

使用 NativeScript 的设备应用设置

2015年3月5日 — 作者:Valio Stoychev

将用户设置保存到设备的本地存储是移动应用的常见需求。因此,我们使使用NativeScript创建设置页面变得非常简单。在这篇博文中,您将学习如何使用 NativeScript 的最新功能(XML 声明、数据绑定和 CSS 样式)构建设置页面。为了将 UI 代码与应用的逻辑分离,我们将遵循MVVM 模式。为了演示这一点,我们将使用真实的 Fitness 应用,其源代码可在http://github.com/nativescript/sample-fitness找到。

创建视图模型

一旦您通过使用Telerik PlatformNativeScript CLI启动并运行您的项目,我们将首先定义设置页面的视图模型。它将保存我们即将推出的健身应用所需的所有设置:姓名、体重、身高和一些通知偏好设置。

我们将使用application-settings 模块来保存和检索设备本地存储中的设置。我们将为每个属性使用不同的字符串键。

settingsViewModel也将是Observable对象,以支持双向绑定

JS (view-model.js)

var observable = require("data/observable");
var appSettings = require("application-settings");
var dialogs = require("ui/dialogs");
 
var NAME = "settings-name";
var HEIGHT = "settings-height";
var WEIGHT = "settings-weight";
var VIBRATE = "settings-vibrate";
var SOUND = "settings-sound";
var SOUND_VOLUME = "settings-sound-value";
 
var settings = new observable.Observable();
Object.defineProperty(settings, "name", {
   get: function () { return appSettings.getString(NAME, "John Doe"); },
   set: function (value) { appSettings.setString(NAME, value); },
   enumerable: true,
   configurable: true
});
 
Object.defineProperty(settings, "height", {
   get: function () { return appSettings.getString(HEIGHT, "180"); },
   set: function (value) { appSettings.setString(HEIGHT, value); },
   enumerable: true,
   configurable: true
});
 
Object.defineProperty(settings, "weight", {
   get: function () { return appSettings.getString(WEIGHT, "80"); },
   set: function (value) { appSettings.setString(WEIGHT, value); },
   enumerable: true,
   configurable: true
});
 
Object.defineProperty(settings, "vibrateEnabled", {
   get: function () { return appSettings.getBoolean(VIBRATE, true); },
   set: function (value) { appSettings.setBoolean(VIBRATE, value); },
   enumerable: true,
   configurable: true
});
 
Object.defineProperty(settings, "soundEnabled", {
   get: function () { return appSettings.getBoolean(SOUND, true); },
   set: function (value) { appSettings.setBoolean(SOUND, value); },
   enumerable: true,
   configurable: true
});
 
Object.defineProperty(settings, "soundVolume", {
   get: function () { return appSettings.getNumber(SOUND_VOLUME, 100); },
   set: function (value) { appSettings.setNumber(SOUND_VOLUME, value); },
   enumerable: true,
   configurable: true
});
 
settings.promptName = function(args) {
    console.log("in promptName");
    dialogs.prompt("Enter your name:", settings.name).then(function (promptResult) {
        console.log("prompt result:" + promptResult.result);
        if (promptResult.result) {
            settings.set("name", promptResult.text);
        }
    });
}
exports.settingsViewModel = settings;

创建视图

现在我们有了视图模型,是时候创建设置的 UI 了。
我们将向项目中添加两个文件

  • main-page.xml - 包含 UI 的 XML 声明。
  • main-page.js - 包含页面的 JS 代码。

首先,我们将创建一个页面,其中包含一个空的 StackLayout,并处理 loaded 事件。在 pageLoaded 函数中,我们将页面的 bindingContext 设置为我们之前创建的视图模型:

XML(main-page.xml)

<Page xmlns="http://www.nativescript.org/tns.xsd" loaded="pageLoaded">
  <StackLayout>
    ...
  </StackLayout>
</Page>


JavaScript(main-page.js)

var vmModule = require("./view-model");
 
var viewModel = vmModule.settingsViewModel;
 
function pageLoaded(args) {
   var page = args.object;
   page.bindingContext = viewModel;
}


接下来:我们将设置分成两组:个人资料和通知。

个人资料设置

现在让我们定义包含姓名、体重和身高的个人资料设置部分。我们将使用绑定语法将字段的文本值绑定到视图模型:

<GridLayout cssClass="field-group" columns="auto, 50, *" rows="auto, auto, auto">
     <!-- Name -->
     <Label text="Name"/>
     <Button col="1" colSpan="2" text="{{ name }}" tap="{{ promptName }}"/>
 
     <!-- 身高 -->
     <Label text="身高" row="1"/>
     <TextField row="1" col="1" text="{{ height }}" keyboardType="number"/>
     <Label col="2" row="1" text="cm"/>
 
     <!-- 体重 -->
     <Label row="2" text="体重"/>
     <TextField row="2" col="1" text="{{ weight }}" keyboardType="number"/>
     <Label row="2" col="2" text="kg"/>
</GridLayout>

我们将字段的 text 属性直接绑定到视图模型。但是,对于名称,我们希望显示一个提示对话框,因此我们使用了名称按钮的 tap 事件的绑定。我们应该在导出设置之前在视图模型中定义 promptName 函数:

settings.promptName = function(args) {
    console.log("in promptName");
    dialogs.prompt("请输入您的姓名:", settings.name).then(function (promptResult) {
        console.log("提示结果:" + promptResult.result);
        if (promptResult.result) {
            settings.set("name", promptResult.text);
        }
    });
}

结果(稍后我们将修复样式)

profile.png

通知设置

下一部分包含通知振动和声音的设置。我们将使用 SwitchSlider 控件。

XML:

<GridLayout cssClass="field-group" columns="*, auto" rows="auto, auto, auto">
     <!-- 通知 -->
     <Label cssClass="field" text="振动"/>
     <Switch col="1" cssClass="field-value" checked="{{ vibrateEnabled }}"/>
 
     <!-- 通知 -->
     <Label row="1" cssClass="field" text="声音"/>
     <Switch row="1" col="1" cssClass="field-value"  checked="{{ soundEnabled }}"/>
     <Slider row="2" colSpan="2" maxValue="100" value="{{ soundVolume }}" isEnabled="{{ soundEnabled }}"/>
</GridLayout>

这里不需要额外的代码。请注意,滑块的 isEnabled 属性绑定到视图模型的 soundEnabled 属性。因为 XML 中定义的所有绑定都处于双向模式,所以当开关未选中时,滑块将被禁用。

结果

 

样式

所有功能都已就绪。剩下的就是为页面添加一些 CSS 样式

提示:如果有一个 CSS 文件与包含 UI 声明的 XML 文件同名,它将自动应用于页面。您还可以创建一个名为 app.css 的文件并在其中定义样式,这些样式可以在应用程序中的任何页面使用。

我们需要在 XML 声明中添加一些 cssClass 属性。

...
<!-- 身高 -->
<Label cssClass="field" text="身高" row="1"/>
<TextField row="1" col="1" cssClass="field-value" text="{{ height }}" keyboardType="number"/>
<Label col="2" row="1" cssClass="field-unit" text="cm"/>
  
<!-- 体重 -->
<Label row="2" cssClass="field" text="体重"/>
<TextField row="2" col="1" cssClass="field-value" text="{{ weight }}" keyboardType="number"/>
<Label row="2" col="2" cssClass="field-unit" text="kg"/>
...

以及 CSS(app.css)

.field, .field-value, .field-unit, .field-dialog-button {
    font-size: 16;
    vertical-align: center;
}
 
.field, .field-unit {
    color: #3c3c3c;
}
 
.field-value, .field-dialog-button {
    color: #808080;
    text-align: right;
}  

添加标题标签和更多样式后,我们将得到以下结果

final.png

您可以在此处查看最终应用程序 - http://github.com/nativescript/sample-fitness