8.7 发布—WinterCG 符合性第一部分
了解更多

本教程介绍了 NativeScript 的基础知识,通过引导您构建具有某些基本功能的示例应用程序来进行讲解。

本教程将教会您以下内容

  • 使用 NativeScript 组件构建布局
  • 使用手势为您的应用程序添加交互性
  • 使用路由在不同视图之间导航

先决条件

NativeScript Core 使用 JavaScript 或 TypeScript 和 XML 来创建应用程序。为了充分利用本教程,您应该已经具备对 JavaScript 的基本了解。本教程演示了 JavaScript 的用法,TypeScript 教程在 这里

示例应用程序概述

您将构建一个主从应用程序,该应用程序显示音乐剧列表,并允许您导航到详细信息页面以查看有关每部音乐剧的更多信息。

Example app preview

您可以在 GitHub 上找到该应用程序的完整源代码。

设置您的环境

要设置您的开发环境,请按照文档中 环境设置 部分的说明进行操作。

创建一个新的 NativeScript 应用程序

我们将在此教程中使用 JavaScript。要创建新的 NativeScript JavaScript 应用程序,请运行 CLI 命令 ns create,后跟应用程序的名称,再加 --js

cli
ns create example-app --js

NativeScript CLI 创建一个新目录,根文件夹名为 example-app,其中包含初始的骨架应用程序项目,并安装必要的包和依赖项。这可能需要几分钟时间,安装完成后应该可以运行。

运行应用程序

转到项目的目录,并运行以下命令在相应的平台上运行它。

cli
cd example-app

// run on iOS
ns run ios

// run on Android
ns run android

ns run 命令构建应用程序并在已连接的 Android 设备或 Android 模拟器上启动应用程序(针对 Android),并在已连接的 iOS 设备或 iOS 模拟器上启动应用程序(针对 iOS)。默认情况下,它会侦听代码中的更改,同步这些更改并刷新所有选定的设备。

文件夹结构

基于 JavaScript 初始应用程序,我们将为我们的应用程序创建以下文件/文件夹结构。

app
  |- assets
      |- anastasia.png
      |- beetlejuicemusical.png
      |- bookofmormon.png
  |- home
      |- home-page.js
      |- home-page.xml
      |- home-view-model.js
  |- details
      |- details-page.js
      |- details-page.xml
      |- details-view-model.js
  |- services
      |- flick.service.js
  |- app-root.xml
  |- app.css
  |- app.js

创建主页

让我们从创建包含以下内容的主页文件开始

javascript
// app/home/home-page.js

import { HomeViewModel } from './home-view-model'

export function navigatingTo(args) {
  if (args.isBackNavigation) {
    return
  }

  const page = args.object
  page.bindingContext = new HomeViewModel()
}
javascript
// app/home/home-view-model.js

import { Observable } from '@nativescript/core'

export class HomeViewModel extends Observable {}
xml
<!-- app/home/home-page.xml -->
<Page xmlns="http://schemas.nativescript.org/tns.xsd" navigatingTo="navigatingTo" />

路由设置

我们将把主页设置为应用程序启动时的默认路由。我们可以通过将主页文件中的路径传递给根 Frame 组件的 defaultPage 属性来设置默认路由。打开 app-root.xml 并添加以下代码

xml
<!-- app/app-root.xml -->
<Frame defaultPage="home/home-page" />

主页 UI

在创建主页的 UI 之前,我们将创建一个 FlickService 来返回我们的电影数据。在 app 中创建 services 目录,并创建一个名为 flick.service.js 的新文件。打开新的 flick.service.js 并添加以下内容

javascript
// app/services/flick.service.js
export class FlickService {
  #flicks = [
    {
      id: 1,
      genre: 'Musical',
      title: 'Book of Mormon',
      image: '~/assets/bookofmormon.png',
      url: 'https://nativescript.cn/images/ngconf/book-of-mormon.mov',
      description: `A satirical examination of the beliefs and practices of The Church of Jesus Christ of Latter-day Saints.`,
      details: [
        {
          title: 'Music, Lyrics and Book by',
          body: 'Trey Parker, Robert Lopez, and Matt Stone',
        },
        {
          title: 'First showing on Broadway',
          body: 'March 2011 after nearly seven years of development.',
        },
        {
          title: 'Revenue',
          body: 'Grossed over $500 million, making it one of the most successful musicals of all time.',
        },
        {
          title: 'History',
          body: 'The Book of Mormon was conceived by Trey Parker, Matt Stone and Robert Lopez. Parker and Stone grew up in Colorado, and were familiar with The Church of Jesus Christ of Latter-day Saints and its members. They became friends at the University of Colorado Boulder and collaborated on a musical film, Cannibal! The Musical (1993), their first experience with movie musicals. In 1997, they created the TV series South Park for Comedy Central and in 1999, the musical film South Park: Bigger, Longer & Uncut. The two had first thought of a fictionalized Joseph Smith, religious leader and founder of the Latter Day Saint movement, while working on an aborted Fox series about historical characters. Their 1997 film, Orgazmo, and a 2003 episode of South Park, "All About Mormons", both gave comic treatment to Mormonism. Smith was also included as one of South Park\'s "Super Best Friends", a Justice League parody team of religious figures like Jesus and Buddha.',
        },
        {
          title: 'Development',
          body: `During the summer of 2003, Parker and Stone flew to New York City to discuss the script of their new film, Team America: World Police, with friend and producer Scott Rudin (who also produced South Park: Bigger, Longer & Uncut). Rudin advised the duo to see the musical Avenue Q on Broadway, finding the cast of marionettes in Team America similar to the puppets of Avenue Q. Parker and Stone went to see the production during that summer and the writer-composers of Avenue Q, Lopez and Jeff Marx, noticed them in the audience and introduced themselves. Lopez revealed that South Park: Bigger, Longer & Uncut was highly influential in the creation of Avenue Q. The quartet went for drinks afterwards, and soon found that each camp wanted to write something involving Joseph Smith. The four began working out details nearly immediately, with the idea to create a modern story formulated early on. For research purposes, the quartet took a road trip to Salt Lake City where they "interviewed a bunch of missionaries—or ex-missionaries." They had to work around Parker and Stone\'s South Park schedule. In 2006, Parker and Stone flew to London where they spent three weeks with Lopez, who was working on the West End production of Avenue Q. There, the three wrote "four or five songs" and came up with the basic idea of the story. After an argument between Parker and Marx, who felt he was not getting enough creative control, Marx was separated from the project.[10] For the next few years, the remaining trio met frequently to develop what they initially called The Book of Mormon: The Musical of the Church of Jesus Christ of Latter-day Saints. "There was a lot of hopping back and forth between L.A. and New York," Parker recalled.`,
        },
      ],
    },
    {
      id: 2,
      genre: 'Musical',
      title: 'Beetlejuice',
      image: '~/assets/beetlejuicemusical.png',
      url: 'https://nativescript.cn/images/ngconf/beetlejuice.mov',
      description: `A deceased couple looks for help from a devious bio-exorcist to handle their haunted house.`,
      details: [
        {
          title: 'Music and Lyrics',
          body: 'Eddie Perfect',
        },
        {
          title: 'Book by',
          body: 'Scott Brown and Anthony King',
        },
        {
          title: 'Based on',
          body: 'A 1988 film of the same name.',
        },
        {
          title: 'First showing on Broadway',
          body: 'April 25, 2019',
        },
        {
          title: 'Background',
          body: `In 2016, a musical adaptation of the 1988 film Beetlejuice (directed by Tim Burton and starring Geena Davis as Barbara Maitland, Alec Baldwin as Adam Maitland, Winona Ryder as Lydia Deetz and Michael Keaton as Betelgeuse) was reported to be in the works, directed by Alex Timbers and produced by Warner Bros., following a reading with Christopher Fitzgerald in the title role. In March 2017, it was reported that Australian musical comedian Eddie Perfect would be writing the music and lyrics and Scott Brown and Anthony King would be writing the book of the musical, and that another reading would take place in May, featuring Kris Kukul as musical director. The musical has had three readings and two laboratory workshops with Alex Brightman in the title role, Sophia Anne Caruso as Lydia Deetz, Kerry Butler and Rob McClure as Barbara and Adam Maitland.`,
        },
      ],
    },
    {
      id: 3,
      genre: 'Musical',
      title: 'Anastasia',
      image: '~/assets/anastasia.png',
      url: 'https://nativescript.cn/images/ngconf/anastasia.mov',
      description: `The legend of Grand Duchess Anastasia Nikolaevna of Russia.`,
      details: [
        { title: 'Music and Lyrics', body: 'Lynn Ahrens and Stephen Flaherty' },
        {
          title: 'Book by',
          body: 'Terrence McNally',
        },
        {
          title: 'Based on',
          body: 'A 1997 film of the same name.',
        },
        {
          title: 'Background',
          body: `A reading was held in 2012, featuring Kelli Barret as Anya (Anastasia), Aaron Tveit as Dmitry, Patrick Page as Vladimir, and Angela Lansbury as the Empress Maria. A workshop was held on June 12, 2015, in New York City, and included Elena Shaddow as Anya, Ramin Karimloo as Gleb Vaganov, a new role, and Douglas Sills as Vlad.
        The original stage production of Anastasia premiered at the Hartford Stage in Hartford, Connecticut on May 13, 2016 (previews). The show was directed by Darko Tresnjak and choreography by Peggy Hickey, with Christy Altomare and Derek Klena starring as Anya and Dmitry, respectively.
        Director Tresnjak explained: "We've kept, I think, six songs from the movie, but there are 16 new numbers. We've kept the best parts of the animated movie, but it really is a new musical." The musical also adds characters not in the film. Additionally, Act 1 is set in Russia and Act 2 in Paris, "which was everything modern Soviet Russia was not: free, expressive, creative, no barriers," according to McNally.
        The musical also omits the supernatural elements from the original film, including the character of Rasputin and his musical number "In the Dark of the Night", (although that song’s melody is repurposed in the new number "Stay, I Pray You"), and introduces instead a new villain called Gleb, a general for the Bolsheviks who receives orders to kill Anya.`,
        },
      ],
    },
  ]

  static getInstance() {
    return FlickService.#instance
  }

  static #instance = new FlickService()

  getFlicks() {
    return this.#flicks
  }

  getFlickById(id) {
    return this.#flicks.find((flick) => flick.id === id) || undefined
  }
}

向项目添加 /app/assets/ 目录,并将 3 张静态图片从示例项目 这里 复制过来。

接下来,让我们分解主页的布局和 UI 元素。

Home page layout breakdown

主页可以分为两个主要部分,带有标题的 ActionBar 和带有卡片的可滚动主要内容区域(我们将在下一节讨论卡片)。让我们从创建带有标题的 ActionBar 开始。打开 home-page.xml 并添加以下代码

xml
<!-- app/home/home-page.xml -->
<Page xmlns="http://schemas.nativescript.org/tns.xsd" navigatingTo="navigatingTo">
  <ActionBar title="NativeFlix" />
</Page>

由于我们有一个要显示的电影数组,因此我们可以使用 NativeScript 的 ListView 组件。ListView 是 NativeScript UI 组件,可以有效地在垂直或水平滚动列表中渲染项目。让我们首先在主页组件中创建一个名为 flick 的变量,我们将用作 ListView 的数据源。打开 home-view-model.js 并添加以下内容

javascript
// app/home/home-view-model.js

import { Observable, ObservableArray } from '@nativescript/core'
import { FlickService } from '../services/flick.service'

// Add the contents of HomeViewModel class 👇
export class HomeViewModel extends Observable {
  #flicks;

  constructor() {
    super()
    this.populateFlicks()
  }

  // this will be used as the data source of our ListView
  get flicks() {
    return new ObservableArray(this.#flicks)
  }

  populateFlicks() {
    this.#flicks = FlickService.getInstance().getFlicks()
  }
}

接下来,添加 ListView 组件

xml
<!-- app/home-page/home-page.xml -->
<Page xmlns="http://schemas.nativescript.org/tns.xsd" navigatingTo="navigatingTo">
  <ActionBar title="NativeFlix" />

  <!-- Add this 👇 -->
  <StackLayout height="100%">
    <ListView height="100%" separatorColor="transparent" items="{{ flicks }}">
      <ListView.itemTemplate>
        <Label text="{{ title }}" />
      </ListView.itemTemplate>
    </ListView>
  </StackLayout>
</Page>

NativeScript 中的 ListView 使用 items 属性作为数据源。在上面的代码片段中,我们将 items 属性设置为 flicks。这将循环遍历 flicks 可观察数组,并为每个条目渲染 ListView.itemTemplate 中的内容。如果您现在运行应用程序,您应该会看到一个电影标题列表。

创建电影卡片

在我们深入探讨下面的卡片创建之前,让我们为应用程序中将要使用的背景色和文本颜色创建一些类。由于这将在整个应用程序中共享,因此让我们将其添加到 app.css 中。打开 app.css 并添加以下内容

css
/* app/app.css */

/* applied when device is in light mode */
.ns-light .bg-primary {
  background-color: #fdfdfd;
}
.ns-light .bg-secondary {
  background-color: #ffffff;
}
.ns-light.text-primary {
  color: #444;
}
.ns-light.text-secondary {
  color: #777;
}

/* applied when device is in dark mode */
.ns-dark .bg-primary {
  background-color: #212121;
}
.ns-dark .bg-secondary {
  background-color: #383838;
}
.ns-dark .text-primary {
  color: #eee;
}
.ns-dark .text-secondary {
  color: #ccc;
}

Home page cards breakdown

如上面的图像所示,每张卡片由 3 个组件组成,即预览图片、标题和描述。我们将使用 GridLayout 作为我们的容器,并使用 ImageLabel 组件来显示预览图片和文本。打开 home-page.xml 并添加以下内容

xml
<!-- app/home/home-page.xml -->
<Page xmlns="http://schemas.nativescript.org/tns.xsd" navigatingTo="navigatingTo">
  <ActionBar title="NativeFlix" />
  <StackLayout height="100%">
    <ListView
      height="100%"
      separatorColor="transparent"
      items="{{ flicks }}"
      margin="10"
    >
      <ListView.itemTemplate>
        <!-- Add this 👇 -->
        <GridLayout
          height="280"
          borderRadius="10"
          class="bg-secondary"
          rows="*, auto, auto"
          columns="*"
          margin="5 10"
          padding="0"
        >
          <Image row="0" margin="0" stretch="aspectFill" src="{{ image }}" />
          <Label
            row="1"
            margin="10 10 0 10"
            fontWeight="700"
            class="text-primary"
            fontSize="18"
            text="{{ title }}"
          />
          <Label
            row="2"
            margin="0 10 10 10"
            class="text-secondary"
            fontSize="14"
            textWrap="true"
            text="{{ description }}"
          />
        </GridLayout>
      </ListView.itemTemplate>
    </ListView>
  </StackLayout>
</Page>

检查点

如果您已经完成到这里,那么在任一平台上运行应用程序应该会产生一个与该屏幕截图类似的应用程序,列表可以垂直滚动。

Home page

创建详细信息页面

让我们从创建包含以下内容的详细信息文件开始

javascript
// app/details/details-page.js

import { DetailsViewModel } from './details-view-model'

export function navigatingTo(args) {
  const page = args.object
  page.bindingContext = new DetailsViewModel()
}
javascript
// app/details/details-view-model.js

import { Observable } from '@nativescript/core'

export class DetailsViewModel extends Observable {}
xml
<!-- app/details/details-page.xml -->
<Page xmlns="http://schemas.nativescript.org/tns.xsd" navigatingTo="navigatingTo" />

设置从主页到详细信息组件的导航

我们将使用 Frame 类中的 navigate 函数从主页组件导航到详细信息组件。除了路由名称之外,我们还将把电影的 id 作为 navigate 函数的 context 对象的一部分传递。我们将在详细信息组件中使用此 id 来访问有关电影的更多信息。打开 home-view-model.js 并添加以下内容

javascript
// app/home/home-view-model.js

// Update this 👇
import { Frame, Observable, ObservableArray } from '@nativescript/core'
import { FlickService } from '../services/flick.service'

// Add the contents of HomeViewModel class 👇
export class HomeViewModel extends Observable {
  #flicks;

  constructor() {
    super()
    this.populateFlicks()
  }

  // this will be used as the data source of our ListView
  get flicks() {
    return new ObservableArray(this.#flicks)
  }

  populateFlicks() {
    this.#flicks = FlickService.getInstance().getFlicks();
  }

  // Add this 👇
  onFlickTap(args) {
    Frame.topmost().navigate({
      moduleName: 'details/details-page',
      context: { flickId: this.#flicks[args.index].id }
    })
  }
}

接下来,让我们将点击事件添加到 ListView 项目。打开 home-page.xml 并添加以下内容

xml
<!-- app/home/home-page.xml -->
<Page xmlns="http://schemas.nativescript.org/tns.xsd" navigatingTo="navigatingTo">
  <ActionBar title="NativeFlix" />
  <StackLayout height="100%">
    <!-- Update this 👇 -->
    <ListView
      height="100%"
      separatorColor="transparent"
      items="{{ flicks }}"
      itemTap="{{ onFlickTap }}"
      margin="10"
    >
      <ListView.itemTemplate>
        <GridLayout
          height="280"
          borderRadius="10"
          class="bg-secondary"
          rows="*, auto, auto"
          columns="*"
          margin="5 10"
          padding="0"
        >
          <Image row="0" margin="0" stretch="aspectFill" src="{{ image }}" />
          <Label
            row="1"
            margin="10 10 0 10"
            fontWeight="700"
            class="text-primary"
            fontSize="18"
            text="{{ title }}"
          />
          <Label
            row="2"
            margin="0 10 10 10"
            class="text-secondary"
            fontSize="14"
            textWrap="true"
            text="{{ description }}"
          />
        </GridLayout>
      </ListView.itemTemplate>
    </ListView>
  </StackLayout>
</Page>

访问导航属性

在上一节中,我们传递了用户点击的电影卡片的 id,当我们导航到详细信息页面时,我们也会传递它。我们可以通过页面的 navigationContext 访问传递的 id。我们首先将在详细信息页面获取 navigationContext 并将其传递给 DetailsViewModel。然后我们可以使用 id 获取选定的电影信息,以便在详细信息组件的模板中显示。打开 details-page.js 并添加以下内容

javascript
// app/details/details-page.js

import { DetailsViewModel } from './details-view-model'

export function navigatingTo(args) {
  const page = args.object
  page.bindingContext = new DetailsViewModel(page.navigationContext)
}

接下来,让我们访问此属性并在 DetailsViewModel 中获取电影信息。打开 details-view-model.js 并添加以下内容

javascript
// app/details/details-view-model.js

import { Observable } from '@nativescript/core'
import { FlickService } from '../services/flick.service'

// Add the contents of HomeViewModel class 👇
export class DetailsViewModel extends Observable {
  #flick

  // the passed-in context object during the navigation will be here
  constructor(_context) {
    super()

    this.#flick = FlickService.getInstance().getFlickById(_context.flickId)
  }

  get flick() {
    return this.#flick
  }
}

详细信息 UI

让我们分解详细信息页面的布局和 UI 元素。

Details page layout breakdown

详细信息页面可以分为三个主要部分,带有电影标题的 ActionBar、英雄图片和带有电影详细信息的主要内容。我们将使用 flicks 对象中的 details 数组来填充电影详细信息部分。details 数组包含具有 titlebody 的对象,它们以统一的方式渲染,每个对象都有自己的样式。我们可以使用 NativeScript 的 Repeater 组件循环遍历数组,为数组中的每个条目创建一个 UI 元素或一组元素。打开 details-page.xml 并添加以下代码

xml
<!-- app/details/details-page.xml -->
<Page xmlns="http://schemas.nativescript.org/tns.xsd" navigatingTo="navigatingTo">
  <!-- Add this 👇 -->
  <ActionBar title="{{ flick.title }}" />

  <!-- Add this 👇 -->
  <ScrollView>
    <StackLayout>
      <Image margin="10" stretch="aspectFill" src="{{ flick.image }}" />
      <StackLayout padding="10 20">
        <Repeater items="{{ flick.details }}">
          <Repeater.itemTemplate>
            <StackLayout>
              <Label
                marginTop="15"
                fontSize="16"
                fontWeight="700"
                class="text-primary"
                textWrap="true"
                text="{{ $value.title }}"
              />
              <Label
                fontSize="14"
                class="text-secondary"
                textWrap="true"
                text="{{ $value.body }}"
              />
            </StackLayout>
          </Repeater.itemTemplate>
        </Repeater>
      </StackLayout>
    </StackLayout>
  </ScrollView>
</Page>

检查点

在任一平台上运行应用程序现在应该会产生一个与该屏幕截图类似的应用程序,并具有在主页和详细信息页面之间导航的功能。

Details page

下一步

恭喜!您构建了第一个在 iOS 和 Android 上运行的 NativeScript 应用程序。您可以继续添加更多 NativeScript UI 组件(或构建您自己的自定义 UI 组件),或者您可以添加一些 原生功能。可能性是无限的!