Android NDK开发详解大屏设备之大屏设备的实战宝典一


Android 提供打造 5 星级大屏应用所需的全部要素。在此实战宝典中的各个方案里,我们会选择并结合利用各种高品质要素,解决开发方面的特定问题。每个方案都包含最佳实践、优质的代码示例和分步说明,可帮助您成为大屏应用开发的高手。

星级评分

我们根据方案对大屏应用质量指南的遵循程度对其进行星级评分。

在这里插入图片描述

Chromebook 相机支持

在这里插入图片描述

在 Google Play 上获得 Chromebook 用户的关注。

如果您的相机应用只需基本的相机功能即可正常运行,请注意避免因为无意中指定高端手机上才有的高级相机功能而致使应用商店阻止 Chromebook 用户安装该应用。

Chromebook 内置有前置(朝向用户)的摄像头,非常适合视频会议、快照及其他应用场景。不过并非所有 Chromebook 都有后置(朝向外面)的摄像头,而且 Chromebook 上朝向用户的摄像头大多不支持自动对焦或闪光灯。

最佳实践

兼容面广的相机应用支持所有设备(无论摄像头配置如何),包括配有前置摄像头、后置摄像头和通过 USB 连接外接摄像头的设备。

为确保应用商店能让尽可能多的设备使用您的应用,请务必声明应用使用的所有相机功能,并明确指出各项功能是否为必需功能。

所需要素

CAMERA 权限:向应用授予对设备相机的使用权限
清单元素:向应用商店声明应用所使用的功能
required 属性:向应用商店指明应用在不用某项特定功能的情况下能否运行

步骤

摘要
声明 CAMERA 权限。声明可提供基本相机支持的相机功能。指明每项功能是否为必需功能。

  1. 声明 CAMERA 权限
    在应用清单中添加以下权限:

<uses-permission android:name="android.permission.CAMERA" />
  1. 声明基本的相机功能
    在应用清单中添加以下功能:

<uses-feature android:name="android.hardware.camera.any" android:required="false" />
<uses-feature android:name="android.hardware.camera" android:required="false" />
<uses-feature android:name="android.hardware.camera.autofocus" android:required="false" />
<uses-feature android:name="android.hardware.camera.flash" android:required="false" />

注意:android.hardware.camera 功能特指后置(朝向外面)的摄像头。
3. 指明每项功能是否为必需功能
为 android.hardware.camera.any 功能设置 android:required=“false” 以允许具有任何种类的内置或外置摄像头(或根本没有摄像头)的设备使用您的应用。

注意:如果您的应用只有在设备配有摄像头的情况下才能正常运行,请为 android.hardware.camera.any 的 required 属性指定 “true”。这样一来,没有摄像头的设备就无法使用您的应用。
对于其他功能,请设置 android:required=“false”,以确保没有后置摄像头、自动对焦或闪光灯功能的设备(例如 Chromebook)也能使用您发布到应用商店中的应用。

成果

Chromebook 用户可以从 Google Play 和其他应用商店下载并安装您的应用。在具有全面的相机支持的设备(例如手机)上,相机功能不会受到限制。

通过明确设置应用支持的相机功能并指定应用必需的功能,您已让自己的应用可在尽可能多的设备上使用。

其他资源

如需了解更多信息,请参阅 文档中的相机硬件功能。

限制应用在手机上的屏幕方向,但不限制其在大屏设备上的屏幕方向

在这里插入图片描述

您的应用在手机上的纵向模式下效果好,因此您将应用限制为仅限纵向显示。但是您发现,对于大屏设备而言,该应用在横向模式下或许可以实现更多优势。

如何同时兼顾两种场景?也就是说,如何让应用在小屏设备上仅可纵向显示,但在大屏设备上也可横向显示?

最佳实践

出色的应用会遵循用户偏好设置,例如设备屏幕方向设置。

大屏设备应用质量指南建议应用支持所有设备配置,包括纵向和横向屏幕方向、多窗口模式以及可折叠设备的折叠和展开状态。应用应针对不同的配置优化布局和界面,并且在配置发生更改时应保存及恢复状态。

此方案是一种临时性措施 - 提供些许的大屏幕支持。在您能够改进应用,使其针对所有设备配置提供全面支持之前,可以权且使用此方案。

所需要素

screenOrientation:应用清单设置,让您可以指定应用如何响应设备屏幕方向的变化
Jetpack WindowManager:让您可以确定应用窗口的尺寸和宽高比的库;向后兼容 API 级别 14
Activity#setRequestedOrientation():用于在运行时更改应用屏幕方向的方法

步骤

摘要
在应用清单中让应用默认能够响应屏幕方向变化。在运行时,确定应用窗口大小。如果应用窗口很小,则通过替换清单中的屏幕方向设置来限制应用的屏幕方向。

  1. 在应用清单中指定屏幕方向设置
    您可以避免声明应用清单的 screenOrientation 元素(在这种情况下,屏幕方向默认为 unspecified),或将屏幕方向设为 fullUser。如果用户没有锁定基于传感器的旋转,您的应用将支持所有设备屏幕方向。
<activity
    android:name=".MyActivity"
    android:screenOrientation="fullUser">

注意:当应用在多窗口模式下运行时,screenOrientation 会被忽略(或等同于 unspecified)。因此,如果您将应用限制为仅采用一种屏幕方向,那么在多窗口模式下,应用可能无法正常运行。
使用 unspecified 和 fullUser 之间的区别虽然微不足道,但很重要。如果您未声明 screenOrientation 值,系统会选择屏幕方向,并且系统用于定义屏幕方向的政策可能会因设备而异。另一方面,指定 fullUser 与用户为设备定义的行为更相符:如果用户锁定了基于传感器的旋转,应用会遵循用户的偏好设置;否则,系统会允许设备处于四种可能的屏幕方向(纵向、横向、反向纵向或反向横向)中的任何一种。请参阅 android:screenOrientation。

  1. 确定屏幕尺寸
    在清单中设置为支持用户允许的所有屏幕方向后,您就能以编程方式根据屏幕尺寸指定应用的屏幕方向。

使用 Jetpack WindowManager 库的 WindowMetricsCalculator#computeMaximumWindowMetrics() 方法可获取设备屏幕尺寸(作为 WindowMetrics 对象)。窗口指标可与窗口大小类进行比较,以确定何时限制屏幕方向。

窗口大小类提供小屏幕和大屏幕之间的断点。

使用 WindowWidthSizeClass#COMPACT 和 WindowHeightSizeClass#COMPACT 断点来确定屏幕尺寸:

Kotlin

/** Determines whether the device has a compact screen. **/
fun compactScreen() : Boolean {
    val metrics = WindowMetricsCalculator.getOrCreate().computeMaximumWindowMetrics(this)
    val width = metrics.bounds.width()
    val height = metrics.bounds.height()
    val density = resources.displayMetrics.density
    val windowSizeClass = WindowSizeClass.compute(width/density, height/density)

    return windowSizeClass.windowWidthSizeClass == WindowWidthSizeClass.COMPACT ||
        windowSizeClass.windowHeightSizeClass == WindowHeightSizeClass.COMPACT
}

Java

/** Determines whether the device has a compact screen. **/
private boolean compactScreen() {
    WindowMetrics metrics = WindowMetricsCalculator.getOrCreate().computeMaximumWindowMetrics(this);
    int width = metrics.getBounds().width();
    int height = metrics.getBounds().height();
    float density = getResources().getDisplayMetrics().density;
    WindowSizeClass windowSizeClass = WindowSizeClass.compute(width/density, height/density);
    return windowSizeClass.getWindowWidthSizeClass() == WindowWidthSizeClass.COMPACT ||
                windowSizeClass.getWindowHeightSizeClass() == WindowHeightSizeClass.COMPACT;
}

注意:
以上示例作为 activity 的方法实现;因此,系统会在 computeMaximumWindowMetrics() 的参数中将 activity 作为 this 解除引用。
示例中使用 computeMaximumWindowMetrics() 方法代替 computeCurrentWindowMetrics(),因为应用可以在多窗口模式下启动,该模式会忽略屏幕方向设置。除非应用窗口占满整个设备屏幕,否则确定应用窗口大小并替换屏幕方向设置没有意义。
如需了解如何声明依赖项以使 computeMaximumWindowMetrics() 方法在您的应用中可用,请参阅 WindowManager。

  1. 替换应用清单设置
    确定设备的屏幕尺寸较小后,可以调用 Activity#setRequestedOrientation() 来替换清单的 screenOrientation 设置:

Kotlin

override fun onCreate(savedInstanceState: Bundle?) {
    super.onCreate(savedInstanceState)
    requestedOrientation = if (compactScreen())
        ActivityInfo.SCREEN_ORIENTATION_PORTRAIT else
        ActivityInfo.SCREEN_ORIENTATION_FULL_USER
    ...
    // Replace with a known container that you can safely add a
    // view to where the view won't affect the layout and the view
    // won't be replaced.
    val container: ViewGroup = binding.container

    // Add a utility view to the container to hook into
    // View.onConfigurationChanged. This is required for all
    // activities, even those that don't handle configuration
    // changes. You can't use Activity.onConfigurationChanged,
    // since there are situations where that won't be called when
    // the configuration changes. View.onConfigurationChanged is
    // called in those scenarios.
    container.addView(object : View(this) {
        override fun onConfigurationChanged(newConfig: Configuration?) {
            super.onConfigurationChanged(newConfig)
            requestedOrientation = if (compactScreen())
                ActivityInfo.SCREEN_ORIENTATION_PORTRAIT else
                ActivityInfo.SCREEN_ORIENTATION_FULL_USER
        }
    })
}

Java

@Override
protected void onCreate(Bundle savedInstance) {
    super.onCreate(savedInstanceState);
    if (compactScreen()) {
        setRequestedOrientation(ActivityInfo.SCREEN_ORIENTATION_PORTRAIT);
    } else {
        setRequestedOrientation(ActivityInfo.SCREEN_ORIENTATION_FULL_USER);
    }
    ...
    // Replace with a known container that you can safely add a
    // view to where the view won't affect the layout and the view
    // won't be replaced.
    ViewGroup container = binding.container;

    // Add a utility view to the container to hook into
    // View.onConfigurationChanged. This is required for all
    // activities, even those that don't handle configuration
    // changes. You can't use Activity.onConfigurationChanged,
    // since there are situations where that won't be called when
    // the configuration changes. View.onConfigurationChanged is
    // called in those scenarios.
    container.addView(new View(this) {
        @Override
        protected void onConfigurationChanged(Configuration newConfig) {
            super.onConfigurationChanged(newConfig);
            if (compactScreen()) {
                setRequestedOrientation(ActivityInfo.SCREEN_ORIENTATION_PORTRAIT);
            } else {
                setRequestedOrientation(ActivityInfo.SCREEN_ORIENTATION_FULL_USER);
            }
        }
    });
}

注意:该清单设置不会发生更改,只是被替换。
通过将逻辑添加到 onCreate() 和 View.onConfigurationChanged() 方法,您可以获取最大窗口指标,并且每当 activity 调整大小或在不同显示屏之间移动(例如在设备旋转后或者可折叠设备折叠或展开时)时,均可替换屏幕方向设置。如需详细了解何时配置会更改以及何时会导致 activity 重新创建,请参阅处理配置更改

注意:如果您没有在清单中指定 android:screenOrientation,则应将出现的 ActivityInfo.SCREEN_ORIENTATION_FULL_USER 替换为 ActivityInfo.SCREEN_ORIENTATION_UNSPECIFIED 以匹配默认值。

成果

现在,在小屏设备上,无论设备如何旋转,应用应该会始终保持纵向模式。在大屏设备上,该应用应支持横向模式和纵向模式。

其他资源

如果您希望升级应用以使其始终支持所有设备配置,并需要这方面的帮助,请参阅以下内容:

支持不同的屏幕尺寸
处理配置变更
保存界面状态

  • 16
    点赞
  • 24
    收藏
    觉得还不错? 一键收藏
  • 打赏
    打赏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

五一编程

程序之路有我与你同行

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值