返回博客首页
← 所有文章

为您的 NativeScript 应用程序构建简单对话框

2018 年 10 月 4 日 — 作者:TJ VanToll

在 NativeScript 应用程序中实现对话框的方法有很多。 NativeScript 对话框模块 允许您使用内置 API 显示各种对话框,非常适合简单的用例。

dialog-module
NativeScript 对话框模块示例。 在 NativeScript Playground 中尝试此示例

另一方面,您还可以创建完全模态页面的对话框,并带有原生过渡效果。当您希望完全控制对话框的外观和工作方式时,这些对话框非常有用。

modal-page
NativeScript 中模态页面示例。 在 NativeScript Playground 中尝试此示例

但是,根据我的经验,有时您需要一个可以自定义样式的简单对话框,而无需创建整个页面。基本上,您想要类似这样的东西。

dialog

在本文中,我将引导您完成创建上面所示简单对话框的过程。我将使用 Angular 进行说明,但无论您使用的是 Angular、Vue.js 还是 NativeScript Core,这些技术都大致相同。

构建标记

以下是在高级别上构建此对话框的标记。

<GridLayout class="page">
  <GridLayout class="content">
    <StackLayout>
      <!-- Page content goes here -->
    </StackLayout>
  </GridLayout>

  <AbsoluteLayout class="dialog-wrapper">
    <StackLayout class="dialog">
      <!-- Dialog content goes here -->
    </StackLayout>
  </AbsoluteLayout>
</GridLayout>

首先,您有一个外部 <GridLayout>,它包含整个页面。如果您使用的是 Angular,则这需要是页面组件的根元素,如果您没有使用 Angular,则这需要是根 <Page> 元素的唯一子元素。

您的页面有两个子元素:一个 <GridLayout>,它将包含应用程序的内容,以及一个 <AbsoluteLayout>,它将充当对话框的容器。这里的想法是,您将像往常一样编写内容,并在应用程序中根据需要显示和隐藏对话框。为了做到这一点,让我们编写一些 TypeScript 和 CSS。

显示和隐藏对话框

在您的代码中,您需要创建一个属性来跟踪对话框是否打开,以及一些用于操作对话框状态的方法。如果您使用的是 Angular,则代码如下所示。

import { Component } from "@angular/core";

@Component({
  selector: "Home",
  moduleId: module.id,
  templateUrl: "./home.component.html",
  styleUrls: ['./home.component.css']
})
export class HomeComponent {
  dialogOpen = false;

  showDialog() {
    this.dialogOpen = true;
  }

  closeDialog() {
    this.dialogOpen = false;
  }
}

dialogOpen 属性顾名思义,跟踪对话框当前是否打开。您将其设置为默认值 false,因为您通常不希望对话框在页面加载时显示。

现在您已经有了属性,下一步是将其应用于您的标记。一种方法是使用以下标记。

<GridLayout class="page" [class.dialogOpen]="dialogOpen">
  <GridLayout class="content">
    <StackLayout>
      <Button class="btn btn-primary" text="Show Dialog" (tap)="showDialog()"></Button>
    </StackLayout>
  </GridLayout>

  <AbsoluteLayout class="dialog-wrapper">
    <StackLayout class="dialog">
      <Label textWrap="true" text="Are you sure you want to continue?"></Label>
      <Button class="btn btn-primary" text="Yes"></Button>
      <Button class="btn" text="No" (tap)="closeDialog()"></Button>
    </StackLayout>
  </AbsoluteLayout>
</GridLayout>

此标记中有一些更改以使用您的新属性。首先,您添加了两个 tap 事件处理程序,一个是您主要内容中的一个按钮,用于显示对话框,另一个是在对话框的取消按钮上,用于关闭对话框。

其次,您使用 Angular 的 [] 语法有条件地将 dialogOpen CSS 类名称应用于您的主要页面元素。此类名称将作为您需要使对话框表现得像对话框的钩子。要了解其工作原理,让我们看看您需要使这一切都结合在一起的 CSS。

添加样式

信不信由你,您已经拥有使此示例工作所需的所有标记和代码,因为此示例的真正魔力发生在 CSS 中。以下是最少所需的 CSS 代码。

.dialogOpen .content {
  opacity: 0.2;
}
.dialogOpen .dialog-wrapper {
  visibility: visible;
}
.dialog-wrapper {
  visibility: collapse;
}
.dialog {
  border-width: 1 0 1 0;
  border-color: black;
  width: 100%;
  margin-top: 100;
}

默认情况下,对话框包装器元素的 visibility 设置为 collapse,这会将对话框隐藏在用户面前。

但是,当 dialogOpen 类被应用时,.dialogOpen .dialog-wrapper 选择器现在被应用,对话框包装器的 visibility 设置为 visible,这会将对话框显示给用户。此外,.dialogOpen .content 选择器现在被应用,并将主要内容的 opacity 更改为 0.2,这使其略微变暗,有助于将焦点放在对话框内容上。

将所有这些结合在一起,您应该有一个看起来有点像这样的对话框。

dialog-in-progress

进一步改进

您可以通过多种方式改进这个简单的设计。首先,您可以添加一些动画,为对话框提供一个不错的淡入效果。该代码如下所示。

@keyframes show {
  from { opacity: 0; }
  to { opacity: 1; }
}

.dialogOpen .content {
  opacity: 0.2;
}
.dialogOpen .dialog-wrapper {
  visibility: visible;
  animation-name: show;
  animation-duration: 0.3s;
  animation-fill-mode: forwards;
}
.dialog-wrapper {
  visibility: collapse;
  opacity: 0;
}
.dialog {
  border-width: 1 0 1 0;
  border-color: black;
  width: 100%;
  margin-top: 100;
}

现在,当您设置 dialogOpen 类名称时,show CSS 动画将在 0.3 秒内将对话框包装器的透明度从 0 更改为 1。结果是一个不错的淡入效果,看起来像这样。

dialog-in-progress-2

此时,您可能需要编写一些 CSS 来清理此 UI 中的间距,具体取决于您想要在对话框中显示的内容。您可以参考本文末尾列出的完整代码示例,查找如何执行此操作的一个示例。

dialog

在我离开之前,还有一个提示需要提一下。为了讨论,让我们回到此示例的基本标记。

<GridLayout class="page">
  <GridLayout class="content">
    <StackLayout>
      <!-- Page content goes here -->
    </StackLayout>
  </GridLayout>

  <AbsoluteLayout class="dialog-wrapper">
    <StackLayout class="dialog">
      <!-- Dialog content goes here -->
    </StackLayout>
  </AbsoluteLayout>
</GridLayout>

重点关注对话框包装器。因为对话框包装器是父页面的子元素,所以它继承了页面的尺寸,即包装器占据与页面相同的尺寸。这是设计使然,因为这意味着当对话框打开时,用户无法点击对话框后面的元素。本质上,包装器将此对话框变成了一个模态对话框。

但是,有一个例外。虽然包装器阻止了主页面被点击,但它的尺寸不会扩展到页面的 <ActionBar> 之上。因此,如果您的页面使用了 <ActionBar>,用户将能够在对话框打开时与您放入其中的任何控件进行交互。

例如,假设您的应用程序中有一个 <ActionItem> 执行共享操作。类似于这样。

<ActionItem text="Share" (tap)="share()"></ActionItem>
import { Component } from "@angular/core";

@Component({
  ...
})
export class HomeComponent {
  dialogOpen = false;

  share() {
    // Sharing code here
  }

  showDialog() { ... }
  closeDialog() { ... }
}

在此示例中,用户可以在对话框打开时执行共享操作。这可能适合您的应用程序和您的场景。但是,如果不是,最简单的阻止操作方法是使用简单的 if 检查。例如,请注意以下代码中 if (this.dialogOpen) 的使用。

import { Component } from "@angular/core";

@Component({
  ...
})
export class HomeComponent {
  dialogOpen = false;

  share() {
    if (this.dialogOpen) {
      return;
    }
    // Sharing code here
  }

  showDialog() { ... }
  closeDialog() { ... }
}

总结

最终,您选择如何配置此示例取决于您应用程序的需求。请随时逐字复制此方法,或根据需要自定义此结构。

此示例的完善版本可用于 Angular、Vue.js 和 NativeScript Core,使用以下链接。请随意使用它们,如果您想出任何有趣的自定义功能,请在下面的评论中列出它们。

提示 此简单对话框只是 NativeScript 市场 中免费提供的大量示例之一。查看 示例的完整列表,甚至可以 贡献您自己的示例 😉