适配 Android N 多窗口特性的 5 个要诀
樱桃大丸子
9年前
<p>英文原文:<a href="/misc/goto?guid=4959669933407597630">5 tips for preparing for Multi-Window in Android N</a><br> 作者:Ian Lake, Google Android 技术推广工程师<br> 翻译:Guokai Han</p> <p>如果你看了 <a href="/misc/goto?guid=4959669933507006477">What’s New in Android N</a> 这个视频,你会无意中发现了<a href="http://developer.android.com/preview/features/multi-window.html?utm_campaign=adp_series_prepareformultiwindow_032316&utm_source=medium&utm_medium=blog">多窗口支持</a>。</p> <p><a href="https://simg.open-open.com/show/e9500870acdc8fbc8547780b216b7d91.png"><img alt="适配 Android N 多窗口特性的 5 个要诀" src="https://simg.open-open.com/show/e9500870acdc8fbc8547780b216b7d91.png" width="650" height="320"></a>使用多窗口分屏功能,能够并排地同时看到两个应用。你可能非常兴奋,想知道这是如何做到的,于是立刻去查阅文档,看看是什么新 API 实现了这一独特功能。</p> <p>结果却发现并没有出现很多新的 API 。只有少量用于定制是否完全支持多窗口的 XML 属性以及少量用于检查当前是否处于多窗口模式的 Activity 方法。那这个魔法到底打是哪来的?<strong>其实魔法一直都在那。</strong></p> <p>所谓的魔法就是 Android 的<a href="http://developer.android.com/guide/topics/resources/overview.html?utm_campaign=adp_series_prepareformultiwindow_032316&utm_source=medium&utm_medium=blog">资源系统</a>。资源系统中最强大的部分之一是<a href="http://developer.android.com/guide/topics/resources/providing-resources.html?utm_campaign=adp_series_prepareformultiwindow_032316&utm_source=medium&utm_medium=blog#AlternativeResources">提供替代资源</a>(alternate resources )的能力——基于不同条件改变大小、布局、绘制、菜单等等。</p> <p><strong>多窗口就是利用了这个资源系统 – 基于窗口大小来调整配置。</strong>除了屏幕大小,最小宽度(即宽度或高度的最小值)和朝向(orientation)也会在调整窗口大小时更新。</p> <p>这引出了我们第一个要诀。</p> <h2>要诀1:使用正确的上下文</h2> <p>使用<a href="https://possiblemobile.com/2013/06/context?utm_campaign=adp_series_prepareformultiwindow_032316&utm_source=medium&utm_medium=blog">适合的上下文</a>(Context)加载合适的资源。如果你使用 <em><a href="http://developer.android.com/reference/android/app/Activity.html?utm_campaign=adp_series_prepareformultiwindow_032316&utm_source=medium&utm_medium=blog">Activity</a></em> 上下文处理填充布局、获取资源等工作,这就对了。</p> <p>然而,如果使用的是 <em><a href="http://developer.android.com/reference/android/app/Application.html?utm_campaign=adp_series_prepareformultiwindow_032316&utm_source=medium&utm_medium=blog" rel="nofollow">Application</a> </em>上下文处理 UI 相关的事情,你会发现加载的资源无法感知到多窗口。除了不会使用 Activity 主题的问题,你可能会加载了完全错误的资源!最好让你的 UI 资源与 Activity 上下文在一起。</p> <h2>要诀2:正确地处理配置变化</h2> <p>使用了正确的上下文,你要确保依据窗口大小获取正确的资源(无论之前是全屏还是两个应用的分屏)。重新加载这些资源的过程是基于你如何<a href="http://developer.android.com/guide/topics/resources/runtime-changes.html?utm_campaign=adp_series_prepareformultiwindow_032316&utm_source=medium&utm_medium=blog">处理运行时变化</a>的。</p> <p>默认情况是你的整个 activity 被销毁然后再重建。这个过程会恢复你在 <em><a href="http://developer.android.com/reference/android/app/Activity.html?utm_campaign=adp_series_prepareformultiwindow_032316&utm_source=medium&utm_medium=blog#onSaveInstanceState%28android.os.Bundle%29" rel="nofollow">onSaveInstanceState()</a> </em>方法中保存的所有状态,以及重新加载所有资源/布局。这个特性很好,你清楚所有事情都与新的配置一致并且配置的<strong>每种</strong>类型都被处理了。</p> <p><strong>不用说你也知道,每次配置变化的处理应该快速且流畅</strong>。请确保你没有在 <em><a href="http://developer.android.com/reference/android/app/Activity.html?utm_campaign=adp_series_prepareformultiwindow_032316&utm_source=medium&utm_medium=blog#onResume%28%29" rel="nofollow">onResume()</a></em> 方法中做太多工作,并考虑使用 <a href="https://medium.com/google-developers/making-loading-data-on-android-lifecycle-aware-897e12760832?utm_campaign=adp_series_prepareformultiwindow_032316&utm_source=medium&utm_medium=blog">loaders</a> 来保证配置变化时数据的连续。</p> <p><a href="http://developer.android.com/guide/topics/resources/runtime-changes.html?utm_campaign=adp_series_prepareformultiwindow_032316&utm_source=medium&utm_medium=blog#HandlingTheChange">你依然可以自己处理配置变化</a>,在这种情况下你的 Activity (和 Fragment) 将收到 <em><a href="http://developer.android.com/reference/android/app/Activity.html?utm_campaign=adp_series_prepareformultiwindow_032316&utm_source=medium&utm_medium=blog#onConfigurationChanged%28android.content.res.Configuration%29" rel="nofollow">onConfigurationChanged()</a> </em>方法回调而不是先销毁再重建,并且你需要手工管理视图更新,重新加载资源等工作。</p> <p>要捕获多窗口相关的配置变化,你需要在 manifest 文件中添加一个 <em><a href="http://developer.android.com/guide/topics/manifest/activity-element.html?utm_campaign=adp_series_prepareformultiwindow_032316&utm_source=medium&utm_medium=blog#config" rel="nofollow">android:configChanges</a></em> 属性,并至少设置这些值:</p> <pre> <code class="language-xml"><activity android:name=".MyActivity" android:configChanges="screenSize|smallestScreenSize |screenLayout|orientation" /></code></pre> <p><br> 请确保你处理了所有可能需要变化的资源(当你自己负责处理配置变化时,这是你的责任了)。</p> <p>这包括重新加载那些之前被认为是不变的资源。考虑一下你在 <em>values </em>和<em> <em>values-sw600dp </em></em>中设置的大小。在<em>非</em>多窗口环境下,你无法在运行时切换它们,因为最小宽度是不变的(它总是设备的最小宽度)。然而在多窗口环境下,你可以且必需随着应用大小的调整而切换这些资源。</p> <h2>要诀3:处理所有朝向</h2> <p>回想下本文开头的介绍,我们谈到当窗口大小调整时朝向也会随之改变。没错,<strong>即使设备处于横屏的时候,应用也可能处于“竖屏”的朝向。</strong></p> <p>其实“竖屏”真正的意义仅仅是高度大于宽度,“横屏”的意义是宽度大于高度。所以从这个定义来考虑,在应用调整大小时,可能会从一个朝向转到另一个朝向就说得通了。</p> <p>这也就是意味着朝向之间的转换应该尽可能平滑。引用 <a href="https://www.google.com/design/spec/layout/split-screen.html?utm_campaign=adp_series_prepareformultiwindow_032316&utm_source=medium&utm_medium=blog#split-screen-layout">material design 规范中分屏部分的描述</a>:</p> <blockquote> <p>改变设备的朝向不应该导致 UI 发生非预期的改变。例如,在某个分屏中(竖屏模式下)正在显示视频的应用,在旋转到横屏模式时,不应该自动进入全屏播放状态。</p> </blockquote> <p><strong>注意:</strong>如果你想要你的应用在全屏状态时(非多窗口)仍有这种类型的功能,你可以使用 <em>inMultiWindowMode()</em> 方法检查你当前所处的准确状态。</p> <p>使用 <em><a href="http://developer.android.com/guide/topics/manifest/activity-element.html?utm_campaign=adp_series_prepareformultiwindow_032316&utm_source=medium&utm_medium=blog#screen" rel="nofollow">android:screenOrientation</a> </em>锁定屏幕朝向也会受到多窗口的影响。<strong>对于不是针对(target为) Android N 的应用,添加 android:screenOrientation 意味着你的应用根本不支持多窗口</strong> – 这总是会强制用户离开多窗口模式。但在针对 Android N 的应用则稍有不同 – 在这种情况下不是不支持多窗口,而是处于多窗口模式时你在 <em>android:screenOrientation </em>中设置的任何朝向都会被忽略。</p> <p>请记住,无论应用是否针对 Android N,在多窗口模式下在运行时锁定朝向的 <em><a href="http://developer.android.com/reference/android/app/Activity.html?utm_campaign=adp_series_prepareformultiwindow_032316&utm_source=medium&utm_medium=blog#setRequestedOrientation%28int%29" rel="nofollow">setRequestedOrientation()</a> </em>方法都是无效的。</p> <h2>要诀4:针对所有屏幕尺寸构建响应式 UI</h2> <p>朝向并非是<a href="https://www.google.com/design/spec/layout/split-screen.html?utm_campaign=adp_series_prepareformultiwindow_032316&utm_source=medium&utm_medium=blog#split-screen-layout">面向分屏设计</a>的唯一问题。多窗口是平板 UI (tablet UI) 第一次遇到被缩小到微型尺寸的情况(你有平板 UI 吧?毕竟,14亿设备中的 12.5% 也是很多的设备)。</p> <p>如果你已经在构建响应式 UI ,能够适应可用的空间,手机和平板有相对类似的布局,你会发现你已经为多窗口环境做了很好的准备。作为建议,<strong>把 UI 缩小到 220dp 的宽/高,然后让应用从这个大小扩展到全屏大小是你现在可做的事情。</strong></p> <p><a href="https://simg.open-open.com/show/40055a339c2cc1d8482ac31bb094d198.png"><img alt="适配 Android N 多窗口特性的 5 个要诀" src="https://simg.open-open.com/show/40055a339c2cc1d8482ac31bb094d198.png" width="800" height="728"></a></p> <p>构建单一的响应式布局使得应用大小调整时能够平滑过渡</p> <p>然而,如果你的手机和平板 UI 差异巨大,请不要迫使用户在这两者之间切换 – 而是以平板 UI 为主并想办法缩小它。有许多<a href="https://www.google.com/design/spec/layout/responsive-ui.html?utm_campaign=adp_series_prepareformultiwindow_032316&utm_source=medium&utm_medium=blog#responsive-ui-patterns">响应式 UI 模式</a>可供你参考,它们可以为用户提供流畅的调整大小体验 – 再次说明,这些并不需要使用 Android N 的 API。</p> <h2>要诀5:由其它应用启动的 Activity 必需总是支持多窗口</h2> <p>在多窗口环境下,你的整个<a href="http://developer.android.com/guide/components/tasks-and-back-stack.html?utm_campaign=adp_series_prepareformultiwindow_032316&utm_source=medium&utm_medium=blog">任务</a>(task)都代表的是一个单窗口。这就是为什么如果要<a href="http://developer.android.com/preview/features/multi-window.html?utm_campaign=adp_series_prepareformultiwindow_032316&utm_source=medium&utm_medium=blog#launch">启动一个显示在(多窗口)旁边的 activity</a> 需要启动一个新的任务 – 新任务才能是新窗口。</p> <p>反过来也是如此,来自同一个页面中引用的内容:</p> <blockquote> <p>如果你在一个任务堆栈中启动一个新的 activity,这个 activity 会替换屏幕上的当前 activity ,并且会继承所有它的多窗口属性。</p> </blockquote> <p><strong>这意味着,如果你有一个能够被其它应用启动的 activity ,这个 activity 会继承调用方 activity 同样的多窗口属性。</strong>这些属性包括最小尺寸。如果使用的是 <em><a href="http://developer.android.com/reference/android/app/Activity.html?utm_campaign=adp_series_prepareformultiwindow_032316&utm_source=medium&utm_medium=blog#startActivityForResult%28android.content.Intent,%20int%29" rel="nofollow">startActivityForResult()</a> </em>,这种情况下你的 activity 必需是同一个任务堆栈的一部分,甚至在 <a href="http://developer.android.com/guide/components/intents-filters.html?utm_campaign=adp_series_prepareformultiwindow_032316&utm_source=medium&utm_medium=blog#ExampleSend" rel="nofollow">implicit intent</a> 的情况下也是如此。你无法确定别人是否使用了 <em><a href="http://developer.android.com/reference/android/content/Intent.html?utm_campaign=adp_series_prepareformultiwindow_032316&utm_source=medium&utm_medium=blog#FLAG_ACTIVITY_NEW_TASK" rel="nofollow">FLAG_ACTIVITY_NEW_TASK</a> </em>。</p> <p>因此,所有这些直到最小尺寸的 activity (以及由 activity 启动的 activity)都<strong>必需</strong>支持多窗口。所以请彻底测试!</p> <p>全面测试!</p> <p>针对多窗口环境的最佳准备工作就是<strong>测试你的应用</strong>。即使没有修改任何代码,也没有配置过 Android N SDK ,只要把现有的应用安装到 Android N 设备或模拟器上就是很好的第一步,也是最容易做的事情。</p> <p>来源:http://chinagdg.org/2016/04/5-tips-for-preparing-for-multi-window-in-android-n/</p>