大屏幕菜谱

Android 提供了打造五星级大屏幕应用的所有要素。本菜谱精选并组合了优质食材,以解决特定的开发问题。每个菜谱都包含最佳实践、优质代码示例以及分步说明,帮助您成为大屏幕顶级厨师。

星级评分

菜谱的星级评分基于它们与大屏幕应用质量指南的符合程度。

Five-star rating 满足第 1 层大屏幕差异化标准
Four-star rating 满足第 2 层大屏幕优化标准
Three-star rating 满足第 3 层大屏幕就绪标准
Two-star rating 提供一些大屏幕功能,但未达到大屏幕应用质量指南的标准
One-star rating 满足特定用例的需求,但没有正确支持大屏幕

Chromebook 相机支持

Three-star rating

让 Chromebook 用户在 Google Play 上注意到您的应用。

如果您的相机应用只能使用基本相机功能,请不要让应用商店阻止 Chromebook 用户安装该应用,仅仅因为您无意中指定了高端手机上才有的高级相机功能。

Chromebook 具有内置的前置(面向用户)摄像头,非常适合视频会议、快照和其他应用。但并非所有 Chromebook 都配有后置(面向世界)摄像头,而且大多数 Chromebook 上的前置摄像头不支持自动对焦或闪光灯。

最佳实践

通用的相机应用支持所有设备,无论其摄像头配置如何——配备前置摄像头、后置摄像头或通过 USB 连接的外部摄像头的设备。

为确保应用商店让您的应用对尽可能多的设备可用,请始终声明您的应用使用的所有相机功能,并明确指示这些功能是否为必需。

食材

  • CAMERA 权限:允许您的应用访问设备的摄像头

  • <uses-feature> 清单文件元素:告知应用商店你的应用使用了哪些功能
  • required 属性:指示应用商店你的应用是否可以在没有指定功能的情况下运行

步骤

概要

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

1. 声明 CAMERA 权限

将以下权限添加到应用清单文件

<uses-permission android:name="android.permission.CAMERA" />
2. 声明基本相机功能

将以下功能添加到应用清单文件

<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" />
3. 指定每个功能是否为必需功能

android:required="false" 设置为 android.hardware.camera.any 功能,以允许拥有任何内置或外接摄像头(或根本没有摄像头)的设备访问你的应用。

对于其他功能,请将 android:required="false" 设置为确保没有后置摄像头、自动对焦或闪光灯的设备(例如 Chromebook)可以从应用商店访问你的应用。

结果

Chromebook 用户可以从 Google Play 和其他应用商店下载并安装你的应用。并且具有完整功能相机支持的设备(如手机)的相机功能不会受到限制。

通过明确设置你的应用支持的相机功能并指定你的应用所需的功能,你已使你的应用尽可能多地适用于各种设备。

其他资源

有关更多信息,请参阅 相机硬件功能 中的 <uses-feature> 文档。

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

Two-star rating

你的应用在手机上以纵向模式运行效果很好,因此你已将其限制为仅纵向模式。但是,你发现有机会在横向的大屏幕上做更多的事情。

你如何才能做到两全其美——在小屏幕上将应用限制为纵向模式,但在大型屏幕上启用横向模式?

最佳实践

最佳应用会尊重用户偏好,例如设备方向。

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

此方案是一种临时措施——大型屏幕支持的一点点帮助。在你可以改进你的应用以完全支持所有设备配置之前,可以使用此方案。

食材

步骤

概要

使应用能够在应用清单文件中默认处理方向更改。在运行时,确定应用窗口的大小。如果应用窗口较小,则通过覆盖清单文件的方向设置来限制应用的方向。

1. 在应用清单文件中指定方向设置

你可以选择不声明应用清单文件的 screenOrientation 元素(在这种情况下,方向默认为 unspecified),或者将屏幕方向设置为 fullUser。如果用户没有锁定传感器驱动的旋转,你的应用将支持所有设备方向。

<activity
    android:name=".MyActivity"
    android:screenOrientation="fullUser">

使用 unspecifiedfullUser 之间的差异很细微,但很重要。如果你不声明 screenOrientation 值,则系统会选择方向,并且系统用于定义方向的策略可能因设备而异。另一方面,指定 fullUser 更接近用户为设备定义的行为:如果用户已锁定传感器驱动的旋转,则应用会遵循用户偏好;否则,系统允许任何四种可能的屏幕方向(纵向、横向、反向纵向或反向横向)。请参阅 android:screenOrientation

2. 确定屏幕大小

将清单文件设置为支持所有用户允许的方向后,你可以根据屏幕大小以编程方式指定应用方向。

Jetpack WindowManager 库添加到模块的 build.gradlebuild.gradle.kts 文件中

Kotlin

implementation("androidx.window:window:version")
implementation("androidx.window:window-core:version")

Groovy

implementation 'androidx.window:window:version'
implementation 'androidx.window:window-core:version'

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

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

使用 WindowWidthSizeClass#COMPACTWindowHeightSizeClass#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;
}
    注意
  • 以上示例实现为活动的方法;因此,活动在 computeMaximumWindowMetrics() 的参数中被解除引用为 this
  • 使用 computeMaximumWindowMetrics() 方法而不是 computeCurrentWindowMetrics() 方法,因为应用可以在多窗口模式下启动,这会忽略屏幕方向设置。除非应用窗口是整个设备屏幕,否则确定应用窗口大小并覆盖方向设置毫无意义。

有关声明依赖项以使 computeMaximumWindowMetrics() 方法在你的应用中可用的说明,请参阅 WindowManager

3. 覆盖应用清单文件设置

当你确定设备具有紧凑的屏幕大小时,可以调用 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() 方法中,你可以在活动调整大小或在显示屏之间移动时(例如,设备旋转后或折叠设备折叠或展开时)获取最大窗口指标并覆盖方向设置。有关配置更改何时发生以及何时导致活动重新创建的更多信息,请参阅 处理配置更改

结果

现在,无论设备如何旋转,你的应用都应在小屏幕上保持纵向模式。在大屏幕上,应用应支持横向和纵向模式。

其他资源

有关帮助你升级应用以始终支持所有设备配置的信息,请参阅以下内容

使用外部键盘空格键暂停和恢复媒体播放

Four-star rating

大型屏幕优化包括能够处理外部键盘输入,例如对按下空格键以暂停或恢复视频和其他媒体的播放做出反应。这对于平板电脑(通常连接到外部键盘)和 Chromebook(通常配备外部键盘,但也可以用作平板电脑)尤其有用。

当媒体是窗口的唯一元素(如全屏视频播放)时,在活动级别或在 Jetpack Compose 中的屏幕级别响应按键事件。

最佳实践

无论何时你的应用播放媒体文件,用户都应该能够通过按下物理键盘上的空格键来暂停和恢复播放。

食材

Compose

  • onPreviewKeyEventModifier,使组件能够在它(或其子组件之一)获得焦点时拦截硬件按键事件。
  • onKeyEvent:与 onPreviewKeyEvent 类似,此 Modifier 使组件能够在它(或其子组件之一)获得焦点时拦截硬件按键事件。

视图

  • onKeyUp():当某个键被释放且活动中的视图未处理它时调用。

步骤

概要

基于视图的应用和基于 Jetpack Compose 的应用以类似的方式响应键盘按键:应用必须侦听按键事件、过滤事件并响应选定的按键,例如空格键。

1. 侦听键盘事件

视图

在应用中的活动中,覆盖 onKeyUp() 方法

Kotlin

override fun onKeyUp(keyCode: Int, event: KeyEvent?): Boolean {
    ...
}

Java

@Override
public boolean onKeyUp(int keyCode, KeyEvent event) {
    ...
}

每次按下键并释放时都会调用此方法,因此它会针对每个击键准确触发一次。

Compose

使用 Jetpack Compose,你可以在管理击键的屏幕中利用 onPreviewKeyEventonKeyEvent 修饰符

Column(modifier = Modifier.onPreviewKeyEvent { event ->
    if (event.type == KeyEventType.KeyUp) {
        ...
    }
    ...
})

Column(modifier = Modifier.onKeyEvent { event ->
    if (event.type == KeyEventType.KeyUp) {
        ...
    }
    ...
})

2. 筛选空格键按下事件

onKeyUp() 方法或 Compose 的 onPreviewKeyEventonKeyEvent 修饰符方法内部,筛选 KeyEvent.KEYCODE_SPACE 以将正确的事件发送到你的媒体组件

视图

Kotlin

if (keyCode == KeyEvent.KEYCODE_SPACE) {
    togglePlayback()
    return true
}
return false

Java

if (keyCode == KeyEvent.KEYCODE_SPACE) {
    togglePlayback();
    return true;
}
return false;

Compose

Column(modifier = Modifier.onPreviewKeyEvent { event ->
    if (event.type == KeyEventType.KeyUp && event.key == Key.Spacebar) {
        ...
    }
    ...
})

Column(modifier = Modifier.onKeyEvent { event ->
    if (event.type == KeyEventType.KeyUp && event.key == Key.Spacebar) {
        ...
    }
    ...
})

结果

您的应用现在可以响应空格键按下以暂停和恢复视频或其他媒体。

其他资源

要了解有关键盘事件以及如何管理它们的更多信息,请参阅处理键盘输入

触控笔手掌拒绝

Five-star rating

触控笔可以在大屏幕上成为非常高效和创意的工具。但是,当用户使用触控笔在应用上绘图、书写或交互时,他们有时会用手掌触摸屏幕。触摸事件可能会在系统识别并将其作为意外的手掌触摸而驳回之前报告给您的应用。

最佳实践

您的应用必须识别无关的触摸事件并忽略它们。Android 通过分派MotionEvent对象来取消手掌触摸。检查对象是否存在ACTION_CANCELACTION_POINTER_UP以及FLAG_CANCELED,以确定是否拒绝由手掌触摸引起的触控手势。

食材

  • MotionEvent:表示触摸和移动事件。包含确定是否应忽略事件所需的信息。
  • OnTouchListener#onTouch():接收MotionEvent对象。
  • MotionEvent#getActionMasked():返回与运动事件关联的操作。
  • ACTION_CANCELMotionEvent常量,表示应撤消手势。
  • ACTION_POINTER_UPMotionEvent常量,表示除第一个指针以外的指针已抬起(即已释放与设备屏幕的接触)。
  • FLAG_CANCELEDMotionEvent常量,表示指针抬起导致了意外的触摸事件。在 Android 13(API 级别 33)及更高版本中添加到ACTION_POINTER_UPACTION_CANCEL事件。

步骤

概要

检查分派到您的应用的MotionEvent对象。使用MotionEvent API 来确定事件特征

  • 单指针事件—检查ACTION_CANCEL。在 Android 13 及更高版本中,还要检查FLAG_CANCELED
  • 多指针事件—在 Android 13 及更高版本中,检查ACTION_POINTER_UPFLAG_CANCELED

响应ACTION_CANCELACTION_POINTER_UP/FLAG_CANCELED事件。

1. 获取运动事件对象

向您的应用添加OnTouchListener

Kotlin

val myView = findViewById<View>(R.id.myView).apply {
    setOnTouchListener { view, event ->
        // Process motion event.
    }
}

Java

View myView = findViewById(R.id.myView);
myView.setOnTouchListener( (view, event) -> {
    // Process motion event.
});
2. 确定事件操作和标志

检查ACTION_CANCEL,它指示所有 API 级别上的单指针事件。在 Android 13 及更高版本中,检查ACTION_POINTER_UP是否存在FLAG_CANCELED.

Kotlin

val myView = findViewById<View>(R.id.myView).apply {
    setOnTouchListener { view, event ->
        when (event.actionMasked) {
            MotionEvent.ACTION_CANCEL -> {
                //Process canceled single-pointer motion event for all SDK versions.
            }
            MotionEvent.ACTION_POINTER_UP -> {
                if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.TIRAMISU &&
                   (event.flags and MotionEvent.FLAG_CANCELED) == MotionEvent.FLAG_CANCELED) {
                    //Process canceled multi-pointer motion event for Android 13 and higher.
                }
            }
        }
        true
    }
}

Java

View myView = findViewById(R.id.myView);
myView.setOnTouchListener( (view, event) -> {
    switch (event.getActionMasked()) {
        case MotionEvent.ACTION_CANCEL:
            // Process canceled single-pointer motion event for all SDK versions.
        case MotionEvent.ACTION_UP:
            if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.TIRAMISU &&
               (event.getFlags() & MotionEvent.FLAG_CANCELED) == MotionEvent.FLAG_CANCELED) {
                //Process canceled multi-pointer motion event for Android 13 and higher.
            }
    }
    return true;
});
3. 撤消手势

一旦您识别出手掌触摸,您可以撤消手势在屏幕上的效果。

您的应用必须保留用户操作的历史记录,以便可以撤消意外的输入(例如手掌触摸)。请参阅实现基本的绘图应用(位于增强 Android 应用中的触控笔支持codelab中)以了解示例。

结果

您的应用现在可以识别和拒绝 Android 13 及更高 API 级别上的多指针事件以及所有 API 级别上的单指针事件的手掌触摸。

其他资源

有关更多信息,请参阅以下内容

WebView 状态管理

Three-star rating

WebView是一个常用的组件,它提供了一个高级的状态管理系统。WebView必须在配置更改中维护其状态和滚动位置。当用户旋转设备或展开折叠式手机时,WebView可能会丢失滚动位置,这会迫使用户从WebView顶部再次滚动到之前的滚动位置。

最佳实践

最大程度减少WebView重新创建的次数。WebView擅长管理其状态,您可以通过尽可能多地管理配置更改来利用此优势。您的应用必须处理配置更改,因为Activity重新创建(系统处理配置更改的方式)也会重新创建WebView,这会导致WebView丢失其状态。

食材

步骤

概要

要保存WebView状态,请尽可能避免Activity重新创建,然后让WebView失效,以便它可以在保留其状态的同时调整大小。

1. 将配置更改添加到应用的AndroidManifest.xml文件中

通过指定应用(而不是系统)处理的配置更改来避免活动重新创建

<activity
  android:name=".MyActivity"
  android:configChanges="screenLayout|orientation|screenSize
      |keyboard|keyboardHidden|smallestScreenSize" />

2. 每当您的应用收到配置更改时使WebView失效

Kotlin

override fun onConfigurationChanged(newConfig: Configuration) {
    super.onConfigurationChanged(newConfig)
    webView.invalidate()
}

Java

@Override
public void onConfigurationChanged(@NonNull Configuration newConfig) {
    super.onConfigurationChanged(newConfig);
    webview.invalidate();
}

此步骤仅适用于视图系统,因为 Jetpack Compose 无需使任何内容失效即可正确调整Composable元素的大小。但是,如果 Compose 未正确管理,则会经常重新创建WebView。使用Accompanist WebView包装器在您的 Compose 应用中保存和恢复WebView状态。

结果

您的应用的WebView组件现在可以在多次配置更改(从调整大小到方向更改再到折叠和展开)中保留其状态和滚动位置。

其他资源

要了解有关配置更改以及如何管理它们的更多信息,请参阅处理配置更改

RecyclerView 状态管理

Three-star rating

RecyclerView可以使用最少的图形资源显示大量数据。当RecyclerView滚动浏览其项目列表时,RecyclerView会重用已滚动出屏幕的项目的View实例,以在它们滚动到屏幕上时创建新项目。但是,配置更改(例如设备旋转)可能会重置RecyclerView的状态,迫使用户再次滚动到列表中RecyclerView项目的先前位置。

最佳实践

RecyclerView应维护其状态(特别是滚动位置)以及在所有配置更改期间其列表元素的状态。

食材

步骤

概要

设置RecyclerView.Adapter的状态恢复策略以保存RecyclerView滚动位置。保存RecyclerView列表项的状态。将列表项的状态添加到RecyclerView适配器,并在将其绑定到ViewHolder时恢复列表项的状态。

1. 启用Adapter状态恢复策略

启用RecyclerView适配器的状态恢复策略,以便在配置更改中维护RecyclerView的滚动位置。将策略规范添加到适配器构造函数中

Kotlin

class MyAdapter() : RecyclerView.Adapter() {
    init {
        stateRestorationPolicy = StateRestorationPolicy.PREVENT_WHEN_EMPTY
    }
    ...
}

Java

class MyAdapter extends RecyclerView.Adapter {

    public Adapter() {
        setStateRestorationPolicy(StateRestorationPolicy.PREVENT_WHEN_EMPTY);
    }
    ...
}

2. 保存有状态列表项的状态

保存复杂RecyclerView列表项的状态,例如包含EditText元素的项目。例如,要保存EditText的状态,请添加类似于onClick处理程序的回调以捕获文本更改。在回调中,定义要保存的数据

Kotlin

input.addTextChangedListener(
    afterTextChanged = { text ->
        text?.let {
            // Save state here.
        }
    }
)

Java

input.addTextChangedListener(new TextWatcher() {

    ...

    @Override
    public void afterTextChanged(Editable s) {
        // Save state here.
    }
});

在您的ActivityFragment中声明回调。使用ViewModel存储状态。

3. 将列表项状态添加到Adapter

将列表项的状态添加到您的RecyclerView.Adapter。在主机ActivityFragment创建时,将项目状态传递给适配器构造函数

Kotlin

val adapter = MyAdapter(items, viewModel.retrieveState())

Java

MyAdapter adapter = new MyAdapter(items, viewModel.retrieveState());

4. 在适配器的ViewHolder中恢复列表项状态

RecyclerView.Adapter中,当您将ViewHolder绑定到项目时,恢复项目的狀態

Kotlin

override fun onBindViewHolder(holder: RecyclerView.ViewHolder, position: Int) {
    ...
    val item = items[position]
    val state = states.firstOrNull { it.item == item }

    if (state != null) {
        holder.restore(state)
    }
}

Java

@Override
public void onBindViewHolder(@NonNull RecyclerView.ViewHolder holder, int position) {
    ...
    Item item = items[position];
    Arrays.stream(states).filter(state -> state.item == item)
        .findFirst()
        .ifPresent(state -> holder.restore(state));
}

结果

您的RecyclerView现在能够恢复其滚动位置以及RecyclerView列表中每个项目的状态。

其他资源

可分离键盘管理

Three-star rating

支持可拆卸键盘有助于在大型屏幕设备上最大程度地提高用户生产力。每次将键盘连接到设备或从设备上分离时,Android 都会触发配置更改,这可能会导致 UI 状态丢失。您的应用可以保存并恢复其状态,让系统处理 Activity 重建,或者限制 Activity 重建以进行键盘配置更改。在所有情况下,与键盘相关的所有数据都存储在Configuration 对象中。keyboardkeyboardHidden 配置对象字段包含有关键盘类型及其可用性的信息。

最佳实践

针对大屏幕优化的应用支持各种类型的输入设备,从软件和硬件键盘到触控笔、鼠标、触控板和其他外围设备。

支持外接键盘涉及配置更改,您可以通过以下两种方式之一进行管理

  1. 让系统重建当前正在运行的 Activity,并负责管理应用的状态。
  2. 自行管理配置更改(Activity 不会重建)
    • 声明所有与键盘相关的配置值
    • 创建配置更改处理程序

生产力应用通常需要对 UI 进行精细控制以进行文本输入和其他输入,可以通过自行处理配置更改的方式从中受益。

在特殊情况下,您可能希望在连接或断开硬件键盘时更改应用布局,例如,为工具或编辑窗口腾出更多空间。

由于监听配置更改的唯一可靠方法是覆盖视图的onConfigurationChanged() 方法,因此您可以向应用 Activity 添加一个新的 View 实例,并在视图的 onConfigurationChanged() 处理程序中响应由键盘连接或断开引起的配置更改。

食材

  • android:configChanges:应用清单的 <activity> 元素的属性。通知系统应用管理的配置更改。
  • View#onConfigurationChanged():对新应用配置传播做出反应的方法。

步骤

概要

声明 configChanges 属性并添加与键盘相关的值。向 Activity 的视图层次结构添加一个 View 并监听配置更改。

1. 声明 configChanges 属性

通过将 keyboard|keyboardHidden 值添加到已管理的配置更改列表中,更新应用清单中的 <activity> 元素。

<activity
      …
      android:configChanges="...|keyboard|keyboardHidden">

2. 向视图层次结构添加一个空视图

声明一个新的视图,并在视图的 onConfigurationChanged() 方法中添加您的处理程序代码。

Kotlin

val v = object : View(this) {
  override fun onConfigurationChanged(newConfig: Configuration?) {
    super.onConfigurationChanged(newConfig)
    // Handler code here.
  }
}

Java

View v = new View(this) {
    @Override
    protected void onConfigurationChanged(Configuration newConfig) {
        super.onConfigurationChanged(newConfig);
        // Handler code here.
    }
};

结果

您的应用现在将响应外接键盘的连接或断开,而无需重建当前正在运行的 Activity。

其他资源

要了解如何在键盘连接或断开等配置更改期间保存应用的 UI 状态,请参阅保存 UI 状态