从简单的 HTML 网站创建移动应用程序

本文是一个简单的教程,旨在教你一些创建跨平台 Web 应用程序的基本技能。你将构建一个示例学校计划应用程序,它将在许多不同的平台上提供动态的“应用程序式”体验,并能离线工作。它将使用 Apache Cordova 和 Mozilla 的 Brick Web 组件。

应用程序背后的故事,由 Piotr 撰写

我有两个孩子,我总是忘记他们的学校计划,他们也是。当然我可以 将 HTML 复制到 JSFiddle 并将计划加载为 Firefox 应用程序。不幸的是,这将无法离线加载,目前也不能在 iOS 上运行。相反,我想创建一个应用程序,让我们的家人中的每一个人都能使用,无论他们选择使用什么设备。

我们将构建

一个移动应用程序,它将

  1. 显示学校计划
  2. 离线工作
  3. 在多个平台上运行

先决条件知识

  • 在开始之前,你应该了解 HTML、CSS 和 JavaScript 的基础知识。
  • 请阅读 说明,了解如何在本教程中加载任何阶段。
  • 阅读 Cordova 文档 也是一件好事,尽管我们将在下面解释你需要知道的要点。
  • 你还可以了解 Mozilla Brick 组件,以了解它们的功能。

准备工作

在构建示例应用程序之前,你需要准备你的环境。

安装 Cordova

我们决定在这个项目中使用 Apache Cordova,因为它目前是将 HTML 应用程序交付到许多不同平台的最佳免费工具。你可以使用 Web 技术构建应用程序,然后让 Cordova 自动将应用程序移植到不同的原生平台。让我们先安装它。

  1. 首先 安装 NodeJS:Cordova 是一个 NodeJS 包。
  2. 接下来,使用 npm 包管理器全局安装 Cordova
    npm install -g cordova

注意:在 Linux 或 OS X 上,你可能需要具有 root 访问权限。

安装最新版本的 Firefox

如果你已经有一段时间没有更新 Firefox,你应该 安装最新版本 以确保你拥有所有必要的工具。

安装 Brick

Mozilla Brick 是为应用程序开发人员构建的工具。它是一组现成的 Web 组件,使你能够非常快速地构建和使用常见的 UI 组件。

  1. 要安装 Brick,我们需要使用 Bower 包管理器。再次使用 npm 安装它
    npm install -g bower
  2. 你可以使用以下命令为当前项目安装 Brick
    bower install mozbrick/brick

    但现在不要这样做 - 你需要将它放在你的项目中,而不仅仅是任何地方。

获取一些示例 HTML

现在你应该找到一些示例 HTML 在项目中使用 - 将你自己的孩子的在线学校计划复制到这个目的,或者 使用我们的示例,如果你没有,但仍然想跟着做。现在将你的标记保存到一个安全的地方。

阶段 1:设置基本 HTML 项目

在本教程的这一部分中,我们将设置基本项目,并在纯 HTML 中显示学校计划。如果你想查看代码在本节结束时应该是什么样子,请参阅 GitHub 上的阶段 1 代码

  1. 首先设置一个简单的 Cordova 项目。在你的命令行中,转到你要创建应用程序项目的目录,并输入以下命令
    cordova create school-plan com.example.schoolplan SchoolPlan

    这将创建一个包含一些文件的 school-plan 目录。

  2. school-plan 内部,在你的文本编辑器中打开 www/index.html,并从 <body> 元素内部删除所有内容。
  3. 将你之前保存的学校计划 HTML 复制到单独的元素中。这可以根据你想要的方式进行结构化,但我们建议使用 HTML <table> 来保存每个单独的计划
    </head>
    <body>
      <h1>Angelica</h1>
      <table>
        <thead>
          <tr>
            <th></th>
            <th>Monday</th>
            <th>Tuesday</th>
            <th>Wednesday</th>
            <th>Thursday</th>
            <th>Friday</th>
          </tr>
        </thead>
        <tbody>
          <tr>
            <td>1.</td>
            <td>Art</td>
            <td>English</td>
        ...
  4. 如果你愿意,更改 www/css/index.css 中包含的样式,以使表格看起来像你想要的样子。我们选择使用“斑马条纹”以便于阅读。
    table {
      width: 100%;
      border-collapse: collapse;
      font-size: 10px;
    }
    th {
      font-size: 12px;
      font-weight: normal;
      color: #039;
      padding: 10px 8px;
    }
    td {
      color: #669;
      padding: 8px;
    }
    tbody tr:nth-child(odd) {
      background: #e8edff;
    }
  5. 为了快速轻松地测试应用程序,请添加 firefoxos 平台作为 Cordova 目标,并准备应用程序,输入以下两个命令
    cordova platform add firefoxos
    cordova prepare

    每次你想检查更改时,都需要执行最后一步。

  6. 在 Firefox 浏览器中打开 应用程序管理器。按下 [添加打包应用程序] 按钮,然后导航到准备好的 firefoxos 应用程序目录,它应该位于 school-plan/platforms/firefoxos/www 中。

    注意:如果你运行的是 Firefox Aurora 或 Nightly,你可以使用我们新的 WebIDE 工具 完成这些任务,它具有与应用程序管理器类似但略有不同的工作流程。

  7. 按下 [启动模拟器] 按钮,然后按下 [更新],你将看到应用程序在 Firefox OS 模拟器中运行。你可以使用应用程序管理器检查、调试和分析它 - 阅读 使用应用程序管理器 以获取更多详细信息。应用程序管理器按钮
  8. 现在让我们将应用程序导出为原生 Android APK,以便我们可以在该平台上看到它运行。添加平台并让 Cordova 使用以下两个命令构建 apk 文件
    cordova platform add android
    cordova build android
  9. apk 构建在 school-plan/platforms/android/ant-build/SchoolPlan-debug.apk 中 - 阅读 Cordova Android 平台指南 以获取有关如何测试它的更多详细信息。

Stage1 Result Screenshot

阶段 2

在我们应用程序实现的阶段 2 中,我们将研究使用 Brick 来改善应用程序的用户体验。我们将实现一个 Brick 自定义元素,使我们能够在同一个地方显示不同的计划,而不是必须滚动浏览许多课程计划才能找到你想要的计划。

你可以在 GitHub 上查看 已完成的阶段 2 代码

我们将使用 brick-deck 组件。它提供了一个“卡片组”类型的界面,显示一张 brick-card 并隐藏其他卡片

  1. 首先,在 www 目录中运行以下命令,将所需的整个 Brick 代码库安装到 app/bower_components 中。
    bower install mozbrick/brick
  2. 为了使用它,将以下代码添加到你的 index.html 文件的 <head> 中,以导入它的 HTML 和 JavaScript
    <script src="app/bower_components/brick/dist/platform/platform.js"></script>
    <link rel="import" href="app/bower_components/brick-deck/dist/brick-deck.html">
  3. 接下来,所有计划都需要包裹在一个 <brick-deck> 自定义元素中,每个单独的计划都应该包裹在一个 <brick-card> 自定义元素中 - 结构最终应该类似于以下内容
    <brick-deck id="plan-group" selected-index="0">
      <brick-card selected>
        <table>
          <!-- school plan 1 -->
        </table>
      </brick-card>
      <brick-card>
        <table>
          <!-- school plan 2 -->
        </table>
      </brick-card>
    </brick-deck>
  4. brick-deck 组件要求你将 <html><body> 元素的高度设置为 100%。将以下内容添加到 css/index.css 文件中
    html, body {height: 100%}
  5. 当你运行应用程序时,第一张卡片应该是可见的,而其他卡片则保持隐藏。为了处理这个问题,我们现在将在 JavaScript 中添加一些内容。首先,向 index.html 添加一些 <link> 元素,以将必要的 JavaScript 文件链接到 HTML
    <script type="text/javascript" src="cordova.js"></script>
    <script type="text/javascript" src="js/index.js"></script>
  6. cordova.js 包含有用的通用 Cordova 特定帮助程序函数,而 index.js 将包含我们应用程序的特定 JavaScript。index.js 已经包含了 app 变量的定义。应用程序在调用 app.initialize() 后运行。在 window 加载后调用它是一个好主意,因此添加以下内容
    window.onload = function() {
        app.initialize();
    }
  7. Cordova 添加了一些事件;其中之一 - deviceready - 在所有 Cordova 代码加载并初始化后触发。让我们将主要的应用程序操作代码放在此事件的回调 - app.onDeviceReady 中。
    onDeviceReady: function() {
        // starts when device is ready
    },
  8. Brick 向所有其元素添加了一些函数和属性。在这种情况下,loopnextCard 被添加到 <brick-deck> 元素中。由于它包含一个 id="plan-group" 属性,从 DOM 获取该元素的适当方法是 document.getElementById。我们希望卡片在 touchstart 事件触发时切换;此时,nextCard 将从回调 app.nextPlan 中调用。
    onDeviceReady: function() {
        app.planGroup = document.getElementById('plan-group');
        app.planGroup.loop = true;
        app.planGroup.addEventListener('touchstart', app.nextPlan);
    },
    nextPlan: function() {
        app.planGroup.nextCard();
    }

Stage2 Result Animation

阶段 3

在本教程的这一部分中,我们将添加一个菜单栏,其中包含当前显示的计划的名称,以提供额外的可用性增强功能。我们将使用 Brick 的 brick-tabbar 组件。查看 GitHub 上的 已完成的阶段 3 代码

  1. 我们需要导入该组件。将以下行添加到你的 HTML 的 <head>
    <script src="app/bower_components/brick/dist/platform/platform.js"></script>
    <link rel="import" href="app/bower_components/brick-deck/dist/brick-deck.html">
    <link rel="import" href="app/bower_components/brick-tabbar/dist/brick-tabbar.html">
  2. 接下来,向所有卡片添加一个 id,并将它们作为 brick-tabbar-tab 元素上的 target 属性的值包含在内,如下所示
    <brick-tabbar id="plan-group-menu" selected-index="0">
        <brick-tabbar-tab target="angelica">Angelica</brick-tabbar-tab>
        <brick-tabbar-tab target="andrew">Andrew</brick-tabbar-tab>
    </brick-tabbar>
    <brick-deck id="plan-group" selected-index="0">
        <brick-card selected id="angelica">
        ...
  3. Deck 的 nextCard 方法由 Brick 在后台使用选项卡的 reveal 事件调用。当选项卡栏元素被触摸时,卡片将发生变化。应用程序变得更简单,因为我们现在使用的是内置的 Brick 功能,而不是我们自己的自定义代码和 Cordova 功能。如果你想在这里结束教程,你可以安全地从 index.html 文件中删除链接到 index.js 和 cordova.js 的 <script> 元素。

Stage3 Result Animation

阶段 4

为了进一步改善触摸设备上的用户体验,我们现在将添加功能,使你能够左右滑动以在卡片之间导航。查看 GitHub 上的 已完成的阶段 4 代码

  1. 目前,卡片切换是使用 tabbar 组件完成的。要使选定的选项卡与当前 card 保持同步,你需要将它们链接回来。这是通过监听每个 cardshow 事件来完成的。对于存储在 app.planGroupMenu.tabs 中的每个选项卡
    tab.targetElement.addEventListener('show', function() {
        // select the tab
    });
  2. 由于存在竞态条件(planGroupMenu.tabs 在应用初始化时可能不存在),轮询 用于等待合适的时机,然后再尝试分配事件。
    function assignTabs() {
        if (!app.planGroupMenu.tabs) {
            return window.setTimeout(assignTabs, 100);
        }
        // proceed

    将标签链接到其关联卡片的代码如下所示

    onDeviceReady: function() {
        app.planGroupMenu = document.getElementById('plan-group-menu');
        function assignTabs() {
            if (!app.planGroupMenu.tabs) {
                return window.setTimeout(assignTabs, 100);
            }
            for (var i=0; i < app.planGroupMenu.tabs.length; i++) {
                var tab = app.planGroupMenu.tabs[i];
                tab.targetElement.tabElement = tab;
                tab.targetElement.addEventListener('show', function() {
                    this.tabElement.select();
                });
            }
        };
        assignTabs();
    
        // continue below ...
  3. 在 Firefox OS 应用中检测单指滑动非常容易。需要两个回调函数来监听 touchstarttouchend 事件,并计算 pageX 参数的增量。不幸的是,如果手指移动,Android 和 iOS 不会触发 touchend 事件。显然,监听 touchmove 事件是最好的选择,但该事件只触发一次,因为它被 scroll 事件拦截。最佳的解决办法是在 touchmove 回调函数中调用 preventDefault() 来阻止事件冒泡。这样,scroll 就会被禁用,功能可以正常运作。
    // ... continuation
    app.planGroup = document.getElementById('plan-group');
    var startX = null;
    var slideThreshold = 100;
    
    function touchStart(sX) {
        startX = sX;
    }
    
    function touchEnd(endX) {
        var deltaX;
        if (startX) {
            deltaX = endX - startX;
            if (Math.abs(deltaX) > slideThreshold) {
                startX = null;
                if (deltaX > 0) {
                    app.previousPlan();
                } else {
                    app.nextPlan();
                }
            }
        }
    }
    
    app.planGroup.addEventListener('touchstart', function(evt) {
        var touches = evt.changedTouches;
        if (touches.length === 1) {
            touchStart(touches[0].pageX);
        }
    });
    
    app.planGroup.addEventListener('touchmove', function(evt) {
        evt.preventDefault();
        touchEnd(evt.changedTouches[0].pageX);
    });

您可以添加任意数量的计划 - 只需确保它们的标题在标签栏中适合屏幕。操作将自动分配。

Stage4 Result Screenshot

待续...

我们正在准备下一部分,其中该应用将发展成为一个具有可下载计划的市场应用。敬请期待!

[编辑] 请查看 下一部分,我们在其中将数据与 DOM 分离。

关于 Piotr Zalewa

Piotr Zalewa 是 Mozilla 开发者生态系统团队的高级 Web 开发人员。致力于 Web 应用开发。他是 JSFiddle 的创建者。

更多 Piotr Zalewa 的文章...

关于 Chris Mills

Chris Mills 是 Mozilla 的高级技术作家,他编写关于开放 Web 应用、HTML/CSS/JavaScript、A11y、WebAssembly 等方面的文档和演示。他喜欢使用 Web 技术,并偶尔在会议和大学做技术演讲。他曾为 Opera 和 W3C 工作,喜欢演奏重金属鼓和喝好啤酒。他住在英国曼彻斯特附近,与他的爱人和三个美丽的孩子住在一起。

更多 Chris Mills 的文章...


12 条评论

  1. Norgen

    到处都是质量低下,无论是解释还是代码。请至少修复 4.3,这真的很糟糕,特别是考虑到您打算进行教育。

    2014 年 10 月 17 日 上午 11:01

    1. Robert Nyman [编辑]

      我希望您能更具建设性地指出您认为质量低下的地方。

      2014 年 10 月 20 日 上午 12:08

      1. Chris Mills

        我再次仔细阅读了本教程,并修复了一些错误和类似问题,但其中并没有什么内容可以归类为糟糕。我很乐意在您提供具体内容的情况下进一步改进它。

        2014 年 10 月 20 日 上午 2:42

  2. Atique Ahmed Ziad

    您没有介绍步骤 5 中的清单文件。

    2014 年 10 月 20 日 上午 4:15

    1. Piotr Zalewa

      Cordova 在后台生成清单文件。由于这只是一个针对少数人的项目,我决定使用 Cordova 项目的默认图标。IMO,在这个阶段没有必要讨论清单。

      2014 年 10 月 20 日 上午 4:30

  3. Miles

    我在几个地方不得不参考 github 源代码(具体来说,我需要将 selected-index 和 selected 属性添加到 brick-deck 和 brick-card 标签),但总体来说,我喜欢这篇文章。我期待下一部分的发布!

    2014 年 10 月 20 日 上午 11:22

    1. Piotr Zalewa

      感谢您的反馈 - 文章已修复。顺便说一下,应该存在一个默认状态...

      2014 年 10 月 20 日 上午 11:43

      1. Miles

        另外一个快速的评论。brick-tabbar-tab 元素的目标属性似乎应该是“angelica”和“andrew”(就像它们在 github stage3 中一样),而不是“tosia”和“magda”。

        2014 年 10 月 20 日 下午 4:36

        1. Piotr Zalewa

          已修复,谢谢(Tosia 和 Magda 是我的女儿)

          2014 年 10 月 21 日 上午 1:15

  4. Mats

    Stage 1:您构建 android 时输入“cordova build android”,而不是“platform”。

    2014 年 10 月 22 日 上午 9:07

  5. Antonio Sánchez

    非常有用 :)

    2014 年 10 月 31 日 上午 4:32

  6. Emmanuel

    我需要通过我的电子邮件帐户获取教程。

    2014 年 11 月 1 日 上午 9:07

本文的评论已关闭。