返回博客首页
← 所有帖子

Calcu{N}ator - NativeScript 计算器

2015 年 5 月 21 日 — 作者 Alexander Ziskind

我制作了一个小型计算器来模仿 iOS 自带的计算器应用程序,并想分享我使用的一些技术。虽然实现一个计算器本身是一项非常简单的任务,但这样做是学习新技术的一些概念的好方法。NativeScript 是一项相当新的技术,提供了许多可扩展性点。通过在 NativeScript 中构建一个计算器,您将学习这些概念以及这些功能如何协同工作。

NativeScript_Calc_Poster

注意:这个计算器是一个简单的版本,只有四个基本功能:除法、乘法、加法和减法,我会在另一篇文章中介绍科学计算器的实现。


NativeScript 计算器应用程序演示的概念

  1. 设置背景图像

  2. 使用多个嵌套布局

  3. 覆盖默认 UI 元素

  4. 为您的应用程序添加声音

  5. 更改 UI 元素使用的字体

  6. 处理 UI 元素加载事件


运行计算器应用程序

这篇文章展示了如何构建一个简单的 iOS 计算器克隆。该 示例应用程序代码 在 GitHub 上免费提供。按照以下步骤自己运行该应用程序。确保已安装 NativeScript。如果您需要这方面的帮助,请查看 入门指南.

将 git 存储库克隆到您的本地 Mac 上

git clone https://github.com/alexziskind1/nativescript-calculator-demoapp.git
这将从 GitHub 将代码下载到名为

nativescript-calculator-demoapp的目录中。在您的终端中导航到该目录,以便您可以执行 NativeScript 命令

cd nativescript-calculator-demoapp
将 iOS 平台添加到项目中,为在模拟器或设备上运行做好准备

tns platform add ios
现在您已准备好运行该应用程序。如果您想在 iOS 模拟器中运行该应用程序,请执行 emulate 命令

tns emulate ios
您也可以直接在 iPhone 上运行它,只需将手机通过 USB 连接到 Mac 并执行 run 命令

tns run ios
计算器应该在您的模拟器或设备中打开,并将如下所示

NativeScript_Calc_01

拆解它

现在您已经运行了该应用程序,甚至可能查看了代码,您可能想知道一些内容是如何组合在一起的。以下部分将解释应用程序中使用的一些技术。

设置背景图像

NativeScript 支持在 UI 元素上将背景颜色设置为 CSS 属性 。这是一项非常常见的任务,即使在 Web 世界中我们也习惯于这样做。您可以在计算器应用程序中看到此示例,方法是打开 app.css 文件

.label-back {
    background-color: #202020;
    padding-top: 50;
}
iOS 计算器应用程序在右侧的操作条中有一个轻微的渐变,从 West Side 到 Ecstacy。或者正如我所说的,从浅橙色到深橙色。

我们还不能使用背景颜色来模拟这种渐变。获得这种效果的最简单方法是使用背景图像,该图像包含在应用程序的 res 文件夹中。

NativeScript_Calc_02


渐变图像是一个略微夸张的 iOS 计算器渐变版本,用于效果。以下是背景图像以及使用它作为背景的按钮操作条。

NativeScript_Calc_03


操作条是通过使用 GridLayout 构建的,在 GridLayout 加载时,背景图像在 JavaScript 代码中设置。

function actionsGridLoaded(args) {
    var theView = args.object._view;
    if (args.object.ios) {
        theView.layer.contents = UIImage.imageNamed("app/res/calc-back-orange.png").CGImage;
    }
}
exports.actionsGridLoaded = actionsGridLoaded;
这将从捆绑包中加载图像,并将网格的背景设置为使用该图像。有几种技术可以设置布局的背景,这些技术在我的 关于该主题的文章中进行了详细说明。

使用多个嵌套布局

自然,布局应该能够嵌套,NativeScript 提供了大量选项来找到合适的组合。计算器应用程序演示了三种布局类型的使用:DockLayoutStackLayoutGridLayout,以及多级嵌套。

以下代码显示了计算器的布局结构,其中所有其他元素都被移除。

<DockLayout stretchLastChild="true">
    <StackLayout dock="top">
            
    </StackLayout>
    <GridLayout rows="*, *, *, *, *" columns="*, *, *, *" dock="bottom">
            
        <GridLayout rowSpan="5" col="3" rows="*, *, *, *, *" columns="*">
                
        </GridLayout>
    </GridLayout>
</DockLayout>


覆盖默认 UI 元素

虽然 NativeScript 支持开箱即用的许多常见本机功能,但有时您可能想要更进一步,对这些控件进行额外的操作。它的美妙之处在于您绝对可以做到这一点,因为 NativeScript 允许您在需要时深入到本机级别。

iOS 计算器数字显示的工作方式就是一个这样的需求。字体的大小保持不变,直到输入一定数量的数字。一旦数字的数量超过显示的宽度,字体就会自动开始减小尺寸。这是一个很好的效果,并且在 iOS 中通过在标签上设置属性来支持。

我们可以通过扩展 NativeScript 自带的标签来利用这种能力。以下代码展示了如何通过设置名为 adjustsFontSizeToFitWidth 的本机底层 iOS 标签属性来扩展 Label 元素。


注意:在下面的代码和文章的后面,我引用了一个名为 __extends 的函数。您可以在文章末尾看到它的定义。

var ScalingLabel = (function (_super) {
    __extends(ScalingLabel, _super);
    function ScalingLabel () {
        _super.call(this);
        this.mylabel = UILabel.alloc().init();
        this._ios.adjustsFontSizeToFitWidth = true;
        this.ios.font = UIFont.fontWithNameSize("HelveticaNeue-UltraLight", 80);
        this.ios.addSubview( this.mylabel );
    }
    return ScalingLabel;
})(label.Label);
exports.ScalingLabel = ScalingLabel;
这段代码创建了一个名为 ScalingLabel 的新标签类型,它将根据计算器显示中显示的数字数量自动调整其字体大小。计算器应用程序在 components 文件夹中的 calccomponents.ios.js 文件中具有 ScalingLabel 等额外组件。

NativeScript_Calc_04


为了在我们的 XML 中使用这个新标签,顶层 Page 元素提供了另一个属性,该属性指向定义 ScalingLabel 的代码文件的位置。

<Page xmlns:cc="components/calccomponents" xmlns="http://schemas.nativescript.org/tns.xsd"
loaded="pageLoaded">
...
</Page>

最后,我们可以将实际的 ScalingLabel 放置到我们的页面中

<cc:ScalingLabel text="{{ mainLabelText }}" cssClass="display" />

 

为您的应用程序添加声音

在 iOS 计算器上点击时,每次点击都会听到轻微的按键点击声。为了模拟这种声音,我们将跳到本机 iOS 音频播放库的一层。NativeScript 足够好,可以公开 AVAudioPlayer 类,该类是 AVFoundation 框架的一部分,这正是我们将在这里使用的。

捆绑包中包含一个 mp3 文件,它是点击声。

NativeScript_Calc_05


为了使按钮发出点击声,我们在 NativeScript 项目中创建了一个新模块。这里只显示 iOS 版本,但 Android 版本也可以轻松创建。

名为 clicker.ios.js 的文件包含该模块,该模块将维护对音频播放器的活动引用。该模块将创建 AVAudioPlayer 的一个新实例,并公开一个名为 click 的函数,该函数将在每次点击按钮时触发。

var Clicker = function(resource) {
    var soundPath = NSBundle.mainBundle().pathForResourceOfType("app/"+resource, "mp3");
    var soundUrl = NSURL.fileURLWithPath(soundPath);
    var player = AVAudioPlayer.alloc().initWithContentsOfURLError(soundUrl, null);
    player.prepareToPlay();
 
    this.click = function() {
        player.currentTime = 0.0;
        player.play();
    };
};
module.exports.Clicker = Clicker;
在视图模型中,我们维护对 clicker 类的引用,并在需要时调用它。

var Calc = (function (_super) {
    __extends(Calc, _super);
    function Calc() {
        _super.call(this);
 
        var self = this;
         
        this.clicker = new clicker.Clicker("res/click_short");
    }
}
当 UI 中的按钮被点击时,click 函数被调用,播放器播放点击声。

    Calc.prototype.btnTapEq = function () {
        this.playSound();
        this.equalsPressed();
    };
     
    Calc.prototype.playSound = function() {
        this.clicker.click();
    };


更改 UI 元素使用的字体

通过使用 iOS 设备上可用的其他字体,甚至您自己的字体,可以为应用程序添加额外的光彩。虽然计算器在不更改字体的情况下也能正常工作,但我们可以通过更改数字显示和所有按钮的字体,使其看起来更像 iOS 计算器。

NativeScript_Calc_06

您可能在想,它们看起来都很好,为什么我要关心这些细微的字体差异?但在现代应用程序设计中,细节决定成败,字体是其中非常重要的一个因素。

我们的计算器应用程序使用两种类型的 UI 元素,需要更新它们的字体

  1. 顶部的数字显示是一个标签
  2. 显示下方所有的按钮都是按钮元素的实例

在撰写本文时,NativeScript 不允许在 CSS 中设置字体,因此我们必须以编程方式执行此操作。更改这些元素字体的其中一项技术是从 NativeScript 捆绑的元素继承。查看上面的覆盖默认 UI 元素部分以了解该技术。这实际上将本机 UI 元素公开给我们,因此我们可以像突出显示的行所示的那样更改字体

var button = require( "ui/button/button" );
var FontButton = (function (_super) {
    __extends(FontButton, _super);
    function FontButton() {
        _super.call(this);
        this.mybutton = UIButton.alloc().init();
        this.ios.font = UIFont.fontWithNameSize("HelveticaNeue-Thin", 80);
        this.ios.addSubview( this.mybutton );
    }
    return FontButton;
})(button.Button);
 
exports.FontButton = FontButton;
查看我的 关于字体的文章 ,以了解有关 NativeScript 字体技术的更多详细信息,以及使用自定义字体。

处理 UI 元素加载事件

如果您已经使用过 NativeScript 一段时间,您一定已经注意到 XML 中 Page 元素上的 loaded 属性。实际上,Page 元素并不是唯一触发 loaded 事件的 UI 元素。您可以为布局、按钮、标签等添加加载处理程序。您只需在 XML 中的元素上添加 loaded 属性,并指定 JavaScript 代码中公开的处理程序即可。

计算器应用程序使用这种技术将背景图像附加到右侧的操作条。iOS 计算器在操作条的背景中有一个轻微的渐变,因此使用渐变图像来再现这种效果是一个简单的解决方案。

NativeScript_Calc_07


操作条是通过使用附加了 loaded 处理程序的 GridLayout 来构建的

<GridLayout loaded="actionsGridLoaded" rowSpan="5" col="3" rows="*, *, *, *, *" columns="*">
    
</GridLayout>

我们在页面 JavaScript 代码文件中定义并导出了处理程序函数本身

function actionsGridLoaded(args) {
    var theView = args.object._view;
    if (args.object.ios) {
        theView.layer.contents = UIImage.imageNamed("app/res/calc-back-orange.png").CGImage;
    }
}
exports.actionsGridLoaded = actionsGridLoaded;
loaded 事件处理程序接收对底层视图对象的引用,我们可以操作该对象并将背景图像设置为视图的 layer 属性。


总结

如果您还没有,请从 GitHub 获取计算器代码,运行它,看看所有部分是如何相互作用的。使用本文帮助您了解文档中可能尚未涵盖的某些概念。然后开始构建您自己的应用程序!


特别感谢 Valentin Stoychev 和 TJ VanToll 对本文的帮助。


定义


__extends 函数 - 此函数通过将所有父属性复制到子类来模拟继承。

var __extends = this.__extends || function (d, b) {
    for (var p in b) if (b.hasOwnProperty(p)) d[p] = b[p];
    function __() { this.constructor = d; }
    __.prototype = b.prototype;
    d.prototype = new __();
};