# 使用 NativeScript 构建响应式应用程序
NativeScript 出色地完成了工作,允许开发人员从同一代码库为 Android 和 iOS 创建应用程序。但是手机和平板电脑呢?我们还可以针对不同的屏幕尺寸和比例使用相同的代码库吗?如果我们不这样做,那将是愚蠢的。在本文中,我们将探讨一些策略来调整布局以适应所有类型的设备。
如果您来自网络世界,则可能熟悉术语“响应式布局”,即适应浏览器大小的布局。但是,本机应用程序并不完全相同。用户可以根据自己的选择调整浏览器的大小,但是应用程序(几乎)将始终占据整个屏幕的大小。屏幕大小有数百种,但是我们可以将其分为两种基本类型:手机和平板电脑。区别不在于屏幕大小本身(有 6.5 英寸的手机和 7 英寸的平板电脑)本身,而是用户与设备交互的方式。例如,人们通常用一只手以纵向模式握住电话,而平板电脑通常以横向模式站立在桌子上。
并非每个应用都需要响应。您的应用可能仅针对手机。如果您需要使其具有响应性,那么本文适合您!
TIP
本文中的示例使用 NativeScript-Vue,但是这些技术适用于 NativeScript 的任何“风味”。
在 NativeScript 中没有“标准”的方式来制作响应式应用程序。值得庆幸的是,NativeScript 非常灵活且功能强大,我们可以采用多种方式实现目标。让我们深入研究其中一些技巧。
# 使用 CSS
实际上,NativeScript 的 CSS 不提供与媒体查询等效的功能。但是,我们有一些非常接近的东西:plugin nativescript-platform-css
。让我们看看它将如何帮助我们。
您需要使用以下命令添加插件:
tns plugin add nativescript-platform-css
然后使用以下命令对其进行初始化app.js
:
require('nativescript-platform-css');
首先,我们必须了解该插件的功能:它将有关平台和屏幕尺寸的顶级 CSS 类添加到我们的应用程序中。其中一些类是:
.androidXXX
或.iosXXX
,其中XXX
可以是 1280、1024、800、600、540、480,400、360 或 320。.phone
或.tablet
,具体取决于设备类型。
TIP
此插件具有更多功能和自定义设置。在这里查看它们。
考虑这个假食谱应用程序的登录屏幕:
1 之前登录
这是屏幕的主要模板:
<FlexboxLayout alignItems="stretch" flexDirection="column">
<Image marginTop="46" width="250" height="183" alignSelf="center" src="res://loginlogo"/>
<Label flexGrow="1"/>
<Button class="social-login fb" text="Log in with Facebook"/>
<Button class="social-login google" text="Log in with Google"/>
</FlexboxLayout>
在手机上看起来不错,但在平板电脑上却不尽如人意,尤其是在横向模式下。这里的问题是按钮太宽。要解决此问题,我们可以添加 css 样式以限制按钮的宽度,并将其范围设置为.tablet
仅适用于平板电脑,如下所示:
.tablet button.social-login {
align-self: center;
width: 400;
}
我们将按钮对齐到(FlexLayout)[https://docs.nativescript.org/ui/layouts/layout-containers#flexboxlayout (opens new window)]的中心,并将其固定宽度设为 400dpi。现在好多了:
2 次登录后
这是一个简单的示例,但是它显示了很多可能性。如果您习惯 CSS 媒体查询,那么此技术适合您。感谢nativescript-platform-css
插件!
# 动态更改网格布局
万能的人<GridLayout>
也可以帮助我们提高应用程序的响应速度。这是因为当我们在运行时更改其布局时,此布局响应良好。我们可以利用它来发挥优势,并根据屏幕的宽度重新排列布局。
查看<GridLayout>
在我们的伪食谱应用程序中实现的食谱屏幕:
3 网格布局之前
在横向图形输入板上,文本在水平方向上拉伸得太大。通过将配料与制备说明并排放置,我们可以更好地利用可用的筛子宽度。“横向网格”与“纵向网格”的区别在于,由于成分与说明位于同一行,因此我们少一行。让我们看看如何做到这一点。
首先,我们做了<GridLayout>
的行具有反应性的,还有row
,col
与colSpan
用于改变其位置的网格单元。模板最终如下所示:
<GridLayout
columns="*, *, *, *"
:rows="gridLayout.rows"
ref="layout"
@layoutChanged="updateLayout"
>
(...)
<StackLayout
:row="gridLayout.ingredients.row"
:colSpan="gridLayout.ingredients.colSpan"
textWrap="true"
id="ingredientsText"
ref="ingredientsText"
>
<!-- Ingredients text added here -->
</StackLayout>
<StackLayout
:row="gridLayout.instructions.row"
:col="gridLayout.instructions.col"
colSpan="4"
textWrap="true"
id="instructionsText"
ref="instructionsText"
>
<!-- Recipe text added here -->
</StackLayout>
(...)
</GridLayout>
(...)
我们向gridLayout
组件的数据中添加了一个对象,以使这些布局更改井井有条:
data() {
return {
gridLayout: {
rows: "40, auto, auto, 40, auto",
ingredients: {
row: 1,
colSpan: 4
},
instructions: {
row: 2,
col: 0
}
},
(...)
gridLayout
默认情况下, 此数据在人像模式下初始化。然后,我们创建了一个方法updateLayout()
,如果有足够的空间,可以将该对象的值更改为横向模式。看一看:
updateLayout() {
const width = utils.layout.toDeviceIndependentPixels(
this.$refs.layout.nativeView.getMeasuredWidth()
);
if (width < 1000) {
this.gridLayout = {
rows: "40, auto, auto, 40, auto",
ingredients: {
row: 1,
colSpan: 4
},
instructions: {
row: 2,
col: 0
}
};
} else {
this.gridLayout = {
rows: "40, auto, 40, auto",
ingredients: {
row: 1,
colSpan: 1
},
instructions: {
row: 1,
col: 1
}
};
}
}
在此函数中,我们首先抓取<GridLayout>
with 的宽度getMeasuredWidth()
。该值可能以像素为单位,因此我们必须使用函数将其转换为与设备无关的像素(DIP)toDeviceIndependentPixels()
。
WARNING
警告!该函数utils.layout.toDeviceIndependentPixels()
来自 NativeScript utils 包,因此您需要使用以下命令导入它: import * as utils from "utils/utils";
然后,如果屏幕的宽度大于 1000 DPI,我们将通过更改gridLayout
数据对象来重新排列网格布局。
另一个重要的细节是我们将方法附加updateLayout()
到@layoutChanged 事件上。每当<GridLayout>
重新计算布局时都会触发此事件,这是在首次渲染布局时以及屏幕方向更改时发生的。没错,布局会根据屏幕旋转进行调整!
我们选择了 1000 个 DPI 作为断点,因为这是关于景观图块开始的位置。设备的差异很大,但以下是一些近似值,可以帮助您确定断点:
- 纵向模式下的电话大约为 350 至 450 DPI。
- 横向模式下的电话大约 600 至 800 DPI。
- 纵向模式下的平板电脑宽约 700 至 900 DPI。
- 横向模式下的平板电脑宽约 1000 至 1200 DPI。
这项技术似乎很复杂,但是肯定要重新实现平板电脑的布局。而且,<GridLayout>
性能一尘不染:没有可察觉的“跳跃布局”,屏幕闪烁或滞后。你自己看:
hubby_chef
# Rad 列表视图网格布局
本<RadListView>
是一个功能丰富的组件,它提供了大量的在改进<ListView>
。这些功能之一是能够使用网格布局而不是堆叠布局。以及该功能如何帮助我们使应用程序具有响应能力?嗯,根据文档,我们可以使用参数轻松定义列数gridSpanCount
。
这是响应式网格列表视图在我们的应用程序中的外观:
5 角列表视图
请注意,在电话上,网格有两列,在纵向平板电脑上,网格有四列,在横向平板电脑上,网格有五列。这是组件的代码:
<template>
<RadListView
for="recipe in recipes"
layout="grid"
itemHeight="200"
:gridSpanCount="gridColumns"
ref="layout"
@layoutChanged="updateLayout">
<v-template>
<RecipeCard :recipe="recipe"/>
</v-template>
</RadListView>
</template>
<script>
import * as utils from "utils/utils";
import RecipeCard from "../components/RecipeCard";
export default {
props: ["recipes"],
components: { RecipeCard },
data() {
return {
gridColumns: 4
};
},
methods: {
updateLayout() {
const width = utils.layout.toDeviceIndependentPixels(
this.$refs.layout.nativeView.getMeasuredWidth()
);
this.gridColumns = parseInt(width / 180);
}
}
};
</script>
“响应魔术”发生在gridSpanCount
属性中。我们使列数对变量具有反应性gridColumns
。然后,我们在updateLayout
方法中更改了gridColumns
变量。我们使用了上一节中的方法来检索可用宽度。这也意味着布局会在屏幕旋转时自动调整。我们将总宽度除以 180,以使卡的宽度在 180 到 250 之间变化,因此它们在每个设备中看起来都是大致平方的。
# 结论
在本文中,我们以三种不同的方式实现了“响应性”。这些技术可让您使用最少的代码使应用程序在宽屏(例如平板电脑)上看起来不错。
第一种技术使用 CSS,因此吸引了经验丰富的 Web 开发人员。使用(和滥用)NativeScript 的反应性系统的其他方法,显示了此框架的可塑性。有了一些创造力,我们可以做任何事情!
可能有上千种方法使应用程序具有响应能力。您是否使用过其他方法?我希望在评论中听到他们的消息!