Android 的覆蓋范圍在遞增,體驗(yàn)也在變得越來(lái)越好,現(xiàn)已有超過(guò) 2.5 億臺(tái)大屏設(shè)備搭載了 Android 系統(tǒng),包括平板電腦、可折疊設(shè)備以及 Chrome OS 設(shè)備。如何適配不同的屏幕尺寸并保障良好的體驗(yàn),一直以來(lái)都是開(kāi)發(fā)者的一大難題。尤其隨著可折疊設(shè)備等新興產(chǎn)品的涌現(xiàn),適配工作也愈發(fā)迫切。
谷歌官方發(fā)文,將重點(diǎn)介紹 Material Design 指南中更新的相關(guān)內(nèi)容,并提供一些建議來(lái)幫助開(kāi)發(fā)者按照自適應(yīng)界面的原則來(lái)構(gòu)建應(yīng)用,從而解決在平板電腦和可折疊設(shè)備上的適配問(wèn)題。
如果您更喜歡通過(guò)視頻了解本文內(nèi)容,請(qǐng)點(diǎn)擊下方:
△ 折疊屏上應(yīng)用設(shè)計(jì)規(guī)范
Compose
https://developer.android.google.cn/jetpack/compose/nav-adaptive
設(shè)計(jì)指南
2021 年年初,我們?cè)?Material Design 網(wǎng)站上發(fā)布了針對(duì)大屏設(shè)備的指南文檔。Android 開(kāi)發(fā)者峰會(huì)期間我們更新了一些內(nèi)容,以幫助開(kāi)發(fā)者為可折疊設(shè)備等更多其他類型的設(shè)備做好準(zhǔn)備。
https://m3.material.io/foundations/adaptive-design/overview
深入理解布局
深入理解布局指南介紹了布局容器的相關(guān)概念,它提供了一個(gè)整體框架,可幫助開(kāi)發(fā)者思考如何在屏幕上排列導(dǎo)航欄、工具欄和內(nèi)容等界面元素。
https://material.io/design/layout/understanding-layout.html#principles
△ 布局的三個(gè)主要區(qū)域
指南中的組合部分帶您了解如何充分利用屏幕空間以保障可讀性,并且以尊重用戶心智模型的方式在不同的場(chǎng)景下合理排布重要內(nèi)容和操作選項(xiàng)。包括適當(dāng)縮放以展示更多內(nèi)容,如示例中的副標(biāo)題和日期,以及較小的組合技術(shù),例如在緊湊型的布局中對(duì)內(nèi)容進(jìn)行視覺(jué)分組并保持其相關(guān)性等。
https://material.io/design/layout/understanding-layout.html#composition
△ 組合指南中涉及的部分布局方式
以 Fortnightly 示例應(yīng)用為例,它在平板電腦上的界面布局十分均衡,這得益于它遵從了指南里對(duì)容器的建議。而且可以看到,F(xiàn)ortnightly 使用了視覺(jué)分隔線 (Visual Divider) 用于分隔最新新聞,在屏幕的另一邊,則利用留白和排版對(duì)不同類別的新聞報(bào)道進(jìn)行分組。
△ Fortnightly 遵循指南對(duì)內(nèi)容進(jìn)行分隔和分組
網(wǎng)格系統(tǒng)
現(xiàn)在,許多應(yīng)用將屏幕視作一個(gè)大畫布或單欄,以水平和垂直的方式按相互關(guān)系繪制元素,有些應(yīng)用也會(huì)在一側(cè)整體留出邊距。這一做法在小屏上或許行得通,當(dāng)屏幕尺寸較大時(shí)就會(huì)出現(xiàn)明顯的問(wèn)題。網(wǎng)格系統(tǒng)則將您的布局劃分為一系列欄,從而幫助您在規(guī)范網(wǎng)格中設(shè)計(jì)更具表現(xiàn)力的布局。在布局中使用欄式網(wǎng)格 (如下圖),能夠讓大屏設(shè)備的體驗(yàn)呈現(xiàn)更貼心,更組織有序的印象,使得設(shè)備和內(nèi)容更自然地融為一體。
△ 欄式網(wǎng)格
您可以通過(guò)這些欄將屏幕劃分為不同區(qū)域,用于容納相關(guān)的信息和操作,進(jìn)而改善信息層次結(jié)構(gòu)。如下圖所示,這里分了三個(gè)區(qū)域,這些區(qū)域?qū)凑赵O(shè)計(jì)者期望用戶閱讀的順序,把用戶的注意力吸引到這些區(qū)域?qū)?yīng)在屏幕的主要信息片段或信息組上。最重要的一點(diǎn)是,欄式網(wǎng)格提供了一種合理的方式來(lái)思考當(dāng)屏幕尺寸變大或變小時(shí)如何將內(nèi)容進(jìn)行重排,從而幫助您對(duì)不同的屏幕尺寸作出一致響應(yīng)。
△ 使用欄式網(wǎng)格將屏幕劃分為三個(gè)主要區(qū)域
在本例中,三個(gè)主要區(qū)域通過(guò)重排來(lái)保持相同的信息層次結(jié)構(gòu),但以更加人性化的方式在小屏幕上顯示。
△ 使用欄式網(wǎng)格在不同屏幕尺寸中對(duì)內(nèi)容進(jìn)行重排
記住網(wǎng)格系統(tǒng)有助于您選擇組件行為,在不同的布局中,以對(duì)設(shè)備尺寸和場(chǎng)景最有意義的方式?jīng)Q定替換還是更改組件。例如,在大屏設(shè)備上,您可使用 Navigation rail (左側(cè)邊欄導(dǎo)航條) 代替底部導(dǎo)航 (Bottom navigation),兩者功能相同,視覺(jué)表現(xiàn)方式也類似,但 Navigation rail 能夠更加人性化地排布頁(yè)面。手機(jī)上的全屏對(duì)話框 (Full-screen dialog) 在大屏幕上可以采用簡(jiǎn)單對(duì)話框 (Simple dialog) 替代,以保持用戶當(dāng)前操作的上下文。
△ 在大屏上使用簡(jiǎn)單對(duì)話框 (右) 代替全屏對(duì)話框 (左)
Navigation rail
https://m3.material.io/components/navigation-rail/overview
底部導(dǎo)航 (Bottom navigation)
https://m3.material.io/components/bottom-navigation
尺寸類別
請(qǐng)記住,替換組件時(shí),首先要滿足用戶的功能性和人性化需求。找到調(diào)整界面的正確閾值,這是實(shí)現(xiàn)響應(yīng)式界面的重要步驟。因此我們定義了新斷點(diǎn)值,這有助于將設(shè)備劃分到預(yù)設(shè)的尺寸類別中,這些尺寸代表了市場(chǎng)上實(shí)際設(shè)備的尺寸。它們有助于將應(yīng)用版面的原始尺寸轉(zhuǎn)換為離散的標(biāo)準(zhǔn)化組,您可以據(jù)此做出更高層次的界面決策。例如,幾乎所有標(biāo)準(zhǔn)手機(jī)在豎屏模式下都采用了較小 (Compact) 寬度和中等 (Medium) 高度的組合,由于普遍使用垂直滾動(dòng),對(duì)大多數(shù)應(yīng)用而言,根據(jù)寬度的尺寸類別進(jìn)行適配就已足夠。
△ 基于寬度的尺寸類別
△ 基于高度的尺寸類
這些尺寸類將作為新的 API 出現(xiàn)在 1.1 版 Jetpack Window Manager 庫(kù)中。從 Android Studio Bumblebee 開(kāi)始,我們還以參考設(shè)備 (Reference devices) 的形式,將尺寸類別整合到工具中,在此基礎(chǔ)上實(shí)現(xiàn)界面有利于保持一致性,操作也更加簡(jiǎn)單。而且開(kāi)發(fā)者不需要去檢查實(shí)際物理尺寸或屏幕方向,或其他容易出錯(cuò)的標(biāo)識(shí)。您在設(shè)計(jì)和構(gòu)建不同的尺寸類別時(shí),請(qǐng)想想人們會(huì)如何手持和觸摸這些類別所代表的設(shè)備。關(guān)注設(shè)備的形狀和尺寸,有助于您打造出更加人性化的體驗(yàn)。例如,在平板電腦或大屏手機(jī)上,如果不完全調(diào)整握持姿勢(shì),人們可能很難觸及屏幕的頂部區(qū)域,因此請(qǐng)將重要操作和內(nèi)容放在容易觸及的區(qū)域中。
尺寸類
https://developer.android.google.cn/guide/topics/large-screens/support-different-screen-sizes#window_size_classes
Window Manager
https://developer.android.google.cn/jetpack/androidx/releases/window
Android Studio Bumblebee
https://developer.android.google.cn/studio
規(guī)范布局
規(guī)范布局提供了一系列通用布局方案,對(duì)設(shè)計(jì)大屏幕應(yīng)用非常有幫助。第一種是列表 / 詳情,或列表網(wǎng)格視圖的簡(jiǎn)單組合,同時(shí)在開(kāi)始展示內(nèi)容的屏幕起始側(cè),設(shè)置 / 不設(shè)置導(dǎo)航容器。
△ 列表 / 詳情布局
支持面板可用于人們需要集中精力的體驗(yàn)中,例如文檔。在屏幕尾側(cè)或底部添加一塊面板,以便于使用工具或上下文控件。
△ 支持面板
信息流是新聞或社交類應(yīng)用中的常見(jiàn)模式,模板采用圖塊 (Tile) 的形式來(lái)吸引用戶發(fā)現(xiàn)更多內(nèi)容。這種交互與移動(dòng)手機(jī)一樣 —— 打開(kāi)一項(xiàng)即表示打開(kāi)一個(gè)新頁(yè)面,但這種體驗(yàn)更具沉浸感,而且專為大屏幕尺寸而設(shè)計(jì)。
△ 信息流
主頁(yè)橫幅優(yōu)先將內(nèi)容排列在屏幕頂部,并在內(nèi)容周圍和下方設(shè)計(jì)了支持元素,這對(duì)以媒體為中心的應(yīng)用來(lái)說(shuō),是非常棒的體驗(yàn)。
△ 主頁(yè)橫幅
規(guī)范布局實(shí)踐
采用響應(yīng)式界面不僅僅是為不同屏幕尺寸提供并行結(jié)構(gòu),應(yīng)用還要足夠靈活,這樣才能根據(jù)各種需要調(diào)整尺寸,例如旋轉(zhuǎn)設(shè)備、多窗口模式以及折疊和非折疊姿態(tài)。因此在運(yùn)行期間,應(yīng)用可從一個(gè)尺寸類別過(guò)渡到另一個(gè)尺寸類別,并再次過(guò)渡回去。重要的是,不要將尺寸類別視作完全獨(dú)立的桶,應(yīng)用也需保證連續(xù)性 (即不中斷用戶體驗(yàn)),所以應(yīng)用狀態(tài)或數(shù)據(jù)不能丟失。
△ 響應(yīng)式界面可根據(jù)屏幕尺寸變化而調(diào)整內(nèi)容布局
設(shè)想一下,當(dāng)您調(diào)整瀏覽器窗口大小時(shí),如果瀏覽器回退了一個(gè)頁(yè)面,或者重定向到另一個(gè)頁(yè)面,又或者修改了歷史記錄,這種體驗(yàn)非常奇怪。因此,每個(gè)頁(yè)面都應(yīng)足夠靈活,而且應(yīng)當(dāng)能夠在尺寸過(guò)渡期間保持狀態(tài)不變,這個(gè)時(shí)候規(guī)范布局就能發(fā)揮重要作用。針對(duì)每個(gè)頁(yè)面,您可以思考一下,當(dāng)屏幕尺寸變大時(shí),可以添加什么內(nèi)容。當(dāng)屏幕尺寸變小時(shí),可以刪除哪些內(nèi)容。然后再選擇合適的策略。這可能意味著您需要重新審視導(dǎo)航圖,尤其是當(dāng)您目前的設(shè)計(jì)以手機(jī)為主時(shí)更應(yīng)如此。
如需構(gòu)建響應(yīng)式界面,我們應(yīng)該優(yōu)先考慮界面中長(zhǎng)駐元素的位置,例如導(dǎo)航元素。遵循 Material 指南,我們可以根據(jù)寬度的尺寸類別提供替代布局,將導(dǎo)航調(diào)整到最方便使用的位置。例如,小屏幕采用底部導(dǎo)航視圖,中等屏幕采用 Navigation rail,大屏幕采用完整導(dǎo)航視圖。請(qǐng)大家注意,這些布局采用的是寬度限定符 "-w",而非最小寬度限定符 "-sw"。剩余空間用于排列內(nèi)容,我們可以在這些空間應(yīng)用規(guī)范布局。
列表 / 詳情
對(duì)列表 / 詳情而言,AndroidX 中有個(gè)名為 SlidingPaneLayout 的專用控件,使用前需為它的兩個(gè)子元素指定 layout_width,在運(yùn)行期間,SlidingPaneLayout 會(huì)判斷是否有足夠空間同時(shí)展示兩個(gè)窗格:
<SlidingPaneLayout …> <FragmentCOntainerView android : id=”@+id/list_pane” android : layout_width=”300dp” android : layout_weight=”1” … /> <FragmentCOntainerView android : id=”@+id/detail_pane” android : layout_width=”360dp” android : layout_weight=”2” <SlidingPaneLayout …>
△ SlidingPaneLayout 布局示例
當(dāng)屏幕空間足夠,則兩個(gè)窗格至少都要達(dá)到指定的寬度,剩余空間可通過(guò) layout_weight 分配,如左圖所示;如果空間不足,如右圖所示,則每個(gè)窗格都使用父視圖的全寬,詳情窗格將被滑到一邊,或直接覆蓋第一個(gè)窗格。
△ SlidingPaneLayout 中空間分配結(jié)果
viewModel.selectedItemFlow.collect { item -> // 更新詳情窗格的內(nèi)容 detailPane.showItem(item) // 將詳細(xì)信息窗格滑動(dòng)到視圖中 // 如果并排放置兩個(gè)窗格 // 并不會(huì)產(chǎn)生實(shí)際效果 slidingPaneLayout.openPane() }
如上代碼所示,您可以通過(guò)代碼控制滑動(dòng)窗格,當(dāng)用戶從列表中選擇一個(gè)項(xiàng)目,我們從 ViewModel 的 Kotlin 流中接收到該項(xiàng)目,然后更新詳情窗格的內(nèi)容,并通過(guò)調(diào)用 openPane 將其滑入視圖。在 Trackr 應(yīng)用中效果如下圖所示:
關(guān)于如何使用 SlidingPaneLayout 實(shí)現(xiàn)雙窗格布局的相關(guān)內(nèi)容,請(qǐng)參閱 Android 開(kāi)發(fā)者網(wǎng)站: 創(chuàng)建雙窗格布局,該頁(yè)面還介紹了其他內(nèi)容,例如集成系統(tǒng)返回按鈕以實(shí)現(xiàn)側(cè)滑回退窗格等。
Trackr 應(yīng)用
https://github.com/android/trackr
創(chuàng)建雙窗格布局
https://developer.android.google.cn/guide/topics/ui/layout/twopane
信息流
我們可以通過(guò)信息流沉浸式地展示一個(gè)數(shù)據(jù)集,因此 RecyclerView 是非常適合的選擇,我們可以通過(guò)改變 RecyclerView 使用的 LayoutManager 來(lái)改變其展現(xiàn)形式。LinearLayoutManager 適合用于較小型寬度,但在中等寬度和展開(kāi)型寬度場(chǎng)景下,頁(yè)面內(nèi)容則會(huì)出現(xiàn)過(guò)度拉伸和變形的情況,這時(shí)改用 GridLayoutManager,或 StaggeredGridLayoutManager 甚至 FlexBoxLayoutManager,可能會(huì)更合適。
△ 通過(guò)更換 RecyclerView 的 LayoutManager 來(lái)改變其展現(xiàn)形式
主頁(yè)橫幅
我們還可以改變單項(xiàng)布局,使某些項(xiàng)比其他項(xiàng)更高或更寬,以此凸顯其重要性,打造更有趣的視覺(jué)效果。在主頁(yè)橫幅布局中,我們強(qiáng)調(diào)某個(gè)特定元素,重新排布它周圍的其他支持元素。當(dāng)然我們有很多方法可以實(shí)現(xiàn)這一點(diǎn),但 ConstraintLayout 的靈活性最大,因?yàn)樗峁┝撕芏喾N方式來(lái)約束子元素的尺寸,以及相對(duì)于其他子元素的位置。在如下媒體類示例應(yīng)用,它的首圖限制在 16:9 的寬高比內(nèi),描述窗格占 60% 寬度,剩余空間留給其他元素。約束條件可以改變甚至還可以用 MotionLayout 設(shè)置動(dòng)畫,它是一個(gè)特殊的 ConstraintLayout。
△ 主頁(yè)橫幅示例
對(duì)于支持面板而言,從 LinearLayout 到 ConstraintLayout 的任何布局控件,都可以當(dāng)作容器來(lái)定位面板。如下圖所示,我們考慮一件事,當(dāng)過(guò)渡到小屏幕尺寸時(shí),面板上的內(nèi)容應(yīng)該放在哪里。我們有許多可選方案,比如使用屏幕尾側(cè)的側(cè)邊抽屜式導(dǎo)航欄,或者使用上滑式底部動(dòng)作條,或者使用選項(xiàng)菜單,甚至可以將內(nèi)容完全隱藏起來(lái)。
適配可折疊設(shè)備
可折疊設(shè)備不僅配備了更大的屏幕,它們還可以根據(jù)設(shè)備的折疊方式和用戶的使用方式調(diào)整設(shè)備的方向 / 姿勢(shì)。
目前有三種常見(jiàn)的設(shè)備形態(tài): 折疊、未折疊和桌面模式 (懸停)。另外,我們稍后也將看到其他理論上存在的狀態(tài),例如書本模式。
△ 折疊設(shè)備的三種常見(jiàn)姿態(tài)
與其他大屏幕設(shè)備一樣,我們需要多想想用戶會(huì)怎樣握持未折疊設(shè)備?如平板電腦,部分屏幕區(qū)域難以用大拇指觸及,用戶也很難騰出整只手來(lái)自由操控屏幕。用戶輕易就能觸及屏幕的底部角落,但可能無(wú)法觸及屏幕最頂端,尤其是在豎屏模式下。這意味著如果您使用 Navigation rail 這類組件,將導(dǎo)航按鈕居中或固定在屏幕底部,這會(huì)更便于用戶的操作。
△ 大屏設(shè)備中的用戶操作熱區(qū)
同時(shí),我們還需要考慮鉸鏈位置對(duì)交互的影響。鉸鏈會(huì)帶來(lái)明顯的觸覺(jué)差異,甚至兩個(gè)屏幕會(huì)存在物理分離。因此,請(qǐng)您避免將按鈕和其他重要操作項(xiàng)直接放在鉸鏈區(qū)域。大多數(shù)設(shè)備上的鉸鏈區(qū)域?qū)挾燃s為 48 dp,在桌面模式下也請(qǐng)避免將界面元素放在鉸鏈區(qū)域,因?yàn)樵谶@種設(shè)備模式下,用戶幾乎無(wú)法使用該區(qū)域的任何功能。
△ 鉸鏈區(qū)域
當(dāng)設(shè)備從折疊模式轉(zhuǎn)換到非折疊模式時(shí),有兩種主要的技術(shù)方案可用于設(shè)計(jì)布局。第一種是擴(kuò)大屏幕,該方案采用了一種簡(jiǎn)單的響應(yīng)式布局,在該布局下應(yīng)用會(huì)擴(kuò)展內(nèi)容并填充到屏幕上。通常情況下,我們會(huì)根據(jù)前面提到的 Material 指南來(lái)擴(kuò)展欄式網(wǎng)格:
https://m3.material.io/foundations/adaptive-design/foldables/compositions
第二種是增加另一個(gè)頁(yè)面,根據(jù)您構(gòu)建的應(yīng)用不同,可以采用與列表 / 詳情或者以另一個(gè)面板補(bǔ)充主面板功能相同的方案。
△ 情境 1: 擴(kuò)大屏幕 (圖左) 情境 2: 增加頁(yè)面 (圖右)
在這兩種情況下,根據(jù) material.io 的指南,您需要?jiǎng)?chuàng)建一個(gè)平均分布在鉸鏈區(qū)域兩側(cè)的八欄網(wǎng)格,當(dāng)添加 Navigation rail 等導(dǎo)航容器時(shí),屏幕起始側(cè)會(huì)被壓縮以容納導(dǎo)航容器。
△ 平均分布在鉸鏈兩側(cè)的八欄網(wǎng)格 (藍(lán)背景)
適配示例
現(xiàn)在我們來(lái)看如何在運(yùn)行期間利用好折疊狀態(tài)。Jetpack Window Manager 庫(kù)提供了相應(yīng)的 API,可以檢測(cè)應(yīng)用窗口是否存在折疊。任何 Activity 都可以獲得一個(gè) WindowInfoRepository 實(shí)例。然后,在 Started 和 Stopped 這兩種生命周期狀態(tài)之間,我們可以安全地從窗口布局信息流中收集信息。每當(dāng)流發(fā)射一個(gè)值時(shí),我們都可以檢查 displayFeature,然后有針對(duì)性地尋找 FoldingFeature。
override fun onCreate(savedInstanceState: Bundle?) { super.onCreate(savedInstanceState) val windowInfoRepo = windowInfoRepository() // 在 STARTED 和 STOPPED 這兩種生命周期狀態(tài)之間安全地從 windowInfoRepo 中收集數(shù)據(jù) lifecycleScope.launch(Dispatchers.Main) { lifecycle.repeatOnLifecycle(Lifecycle.State.STARTED) { windowInfoRepo.windowLayoutInfo.collect { info -> for (feature in info.displayFeatures) { val fold = feature as? FoldingFeature ?: continue // 使用 FoldingFeature } } } } }
△ 識(shí)別折疊姿態(tài)
掌握了折疊姿態(tài)的相關(guān)信息后,我們可以通過(guò)一些方法來(lái)查看設(shè)備是否處于前面提及的某種姿態(tài)。在書本模式下,設(shè)備的狀態(tài)為 HALF_OPENED,且其方向?yàn)?VERTICAL;在桌面模式下的狀態(tài)為 HALF_OPENED,且其方向?yàn)?HORIZONTAL。
// 書本模式是半打開(kāi)的垂直折疊模式 fun FoldingFeature.isBookMode() = state == FoldingFeature.State.HALF_OPENED && orientation == FoldingFeature.Orientation.VERTICAL // 桌面模式是半打開(kāi)的水平折疊模式 fun FoldingFeature.isTableTopMode() = state == FoldingFeature.State.HALF_OPENED && orientation == FoldingFeature.Orientation.HORIZONTAL
△ 書本模式于桌面模式的判定條件
FoldingFeature 中還包含窗口中的折疊位置,當(dāng)折疊導(dǎo)致內(nèi)容視圖被割裂時(shí),我們應(yīng)該及時(shí)更新布局參數(shù)。您可以做些調(diào)整,比如將支持面板置于一側(cè),或者在折疊的上半部分展示主頁(yè)橫幅。首先,我們需要知道內(nèi)容視圖在窗口中的位置,通過(guò) getLocationInWindow 可以獲取位置信息。我們將使用這些坐標(biāo)以及寬度和高度創(chuàng)建一個(gè) Rect 對(duì)象,這樣我們便得到了窗口坐標(biāo)空間中的視圖邊界。
FoldingFeature 給出了在窗口的坐標(biāo)空間中的折疊邊界,因此我們可以直接檢查這兩個(gè)區(qū)域是否相交,如果相交,我們可以將 featureRect 的邊界轉(zhuǎn)換為視圖的坐標(biāo)空間并將其返回。順便說(shuō)一下,如果您使用 SlidingPaneLayout 來(lái)實(shí)現(xiàn)列表 / 詳情布局,您會(huì)自動(dòng)獲得對(duì)書本模式的支持。只要兩個(gè)窗格都能容納進(jìn)去,SlidingPaneLayout 會(huì)將窗格置于折疊姿態(tài)的另一側(cè)。
fun getFoldBoundsInView( foldingFeature: FoldingFeature, view: View ): Rect? { // 獲取視圖在窗口坐標(biāo)空間中的邊界 val viewLocation = IntArray(2) view.getLocationInWindow(viewLocation) val (viewX, viewY) = viewLocation val viewRect = Rect( left = viewX, top = viewY right = viewX + view.width, bottom = view + view.height ) … //顯示功能的邊界已經(jīng)在窗口的坐標(biāo)空間中 // 檢查 view 的邊界和顯示功能的邊界是否相交 val featureRect = Rect(foldingFeature.bounds) val intersects = featureRect. intersect (viewRect) if (featureRect.isEmpty || ! intersects) return null } // 將功能的邊界坐標(biāo)轉(zhuǎn)換為 view 的坐標(biāo)空間 featureRect.offset(-viewX, -viewY) return featureRect }
△ 獲取折疊的位置信息
測(cè)試
如果您的應(yīng)用存在與折疊狀態(tài)相關(guān)的特殊行為,您需要為此編寫單元測(cè)試。Jetpack Window Manager 里面有一條測(cè)試規(guī)則,支持在插樁測(cè)試期間模擬 FoldingFeature。由于測(cè)試需用到視圖,我們添加了 WindowLayoutInfoPublisherRule,以及 ActivityScenarioRule,兩者一起組成了一個(gè)測(cè)試規(guī)則鏈。在該測(cè)試方法中,我們通過(guò) activityRule 獲取 Activity,然后創(chuàng)建窗口特性來(lái)模擬桌面模式,構(gòu)建 WindowLayoutInfo 對(duì)象并使用 publisherRule 發(fā)布該對(duì)象。之后,我們可以使用 Espresso 和 JUnit 斷言來(lái)檢查 Activity 在桌面模式下能否正常運(yùn)行。
private val publisherRule = WindowLayoutInfoPublisherRule() private val activityRule = ActivityScenarioRule (MyActivity: :class.java) @get :Rule val testRule = RuleChain.outerRule (publisherRule) .around(activityRule) @Test fun testDeviceOpen_TableTop(): Unit = testScope.runBlockingTest { activityRule.scenario.onActivity { activity -> val feature = FoldingFeature (activity, HALF_OPENED, HORIZONTAL) val testWindowInfo = WindowLayoutInfo.Builder( ) .setDisplayFeatures (listOf (feature)) .build() publisherRule.overrideWindowLayoutInfo(testWindowInfo) } // 編寫基于桌面模式的斷言 }
△ 測(cè)試折疊狀態(tài)
界面測(cè)試存在一定難度,因?yàn)橛行y(cè)試須在特定設(shè)備上進(jìn)行。為此,Android Studio 正在增加對(duì) Gradle 托管的虛擬設(shè)備的支持。您可以使用 7.1 及以上版本的 Android Gradle 插件來(lái)體驗(yàn)該功能。
在應(yīng)用級(jí)的 build.gradle 文件中的 testOptions 模塊下,指定虛擬設(shè)備配置文件,就像您平時(shí)在 Android Studio 管理和運(yùn)行虛擬設(shè)備那樣。例如,這里使用的是 Pixel C 平板電腦鏡像,接下來(lái) Gradle 會(huì)創(chuàng)建能夠在指定設(shè)備上執(zhí)行測(cè)試的目標(biāo),甚至還能根據(jù)需要下載設(shè)備鏡像。
android { testoptions { devices { pixelCapi30 (ManagedVirtualDevice) { device = "Pixel C" // 平板電腦設(shè)備 apilevel = 30 systemImageSource = "aosp" // 如需 GooglePlay 服務(wù),使用“google” abi = "x86” } } } } #Gradle target = {device name} + {build variant} + "AndroidTest" ./gradlew pixelCapi30debugAndroidTest
△ 虛擬設(shè)備配置
為便于區(qū)分哪些測(cè)試是針對(duì)哪些設(shè)備的,我們將創(chuàng)建自定義注解 LargeScreenTest,并用該注解來(lái)標(biāo)記測(cè)試函數(shù)。運(yùn)行前面的 Gradle 命令時(shí),我們會(huì)為 AndroidTestRunner 添加一項(xiàng)參數(shù),確保只運(yùn)行具有此注釋的測(cè)試。若您不使用注釋,也可以使用 TestRunner 的其他過(guò)濾選項(xiàng),比如運(yùn)行特定類中的測(cè)試。將這些特性加以組合,我們可以為測(cè)試設(shè)置一致運(yùn)行配置。
annotation class LargeScreenTest @RunWith(AndroidJUnit4: :class) class MyActivityTest { @Test @LargeScreenTest fun largeScreenDeviceTest() { // 在平板電腦設(shè)備上測(cè)試界面 } } # 只運(yùn)行帶有指定注解的測(cè)試 . /gradlew pixelCapi30debugAndroidTest \ -Pandroid.testInstrumentationRunnerArguments.annotation=com.mypkg.LargeScreenTest
△ 使用自定義注解為指定設(shè)備編寫測(cè)試
更多信息
除了讓屏幕上的內(nèi)容看起來(lái)更大之外,大屏幕還帶來(lái)了一些其他機(jī)會(huì),幫助您的應(yīng)用大放異彩。在多窗口模式下,您的應(yīng)用可以與其他應(yīng)用并排使用,除了響應(yīng)式調(diào)整之外,還可以考慮如何讓應(yīng)用在這種模式下發(fā)揮更大作用,比如支持拖拽等。這種小功能可以提高用戶的工作效率,用戶便更樂(lè)意使用您的應(yīng)用。
△ 多窗口模式效果
多窗口模式
https://developer.android.google.cn/guide/topics/ui/multi-window
除了通過(guò)觸摸進(jìn)行交互外,大屏幕設(shè)備還支持其他交互形式。設(shè)備的屏幕尺寸越大,用戶就越有可能使用鍵盤、手寫筆、鼠標(biāo)、游戲手柄或其他外接設(shè)備。如果您想提高應(yīng)用在這些情況下的易用性,可以計(jì)劃支持其中一些輸入方式,如需了解更多詳情,請(qǐng)參閱文章《是時(shí)候?yàn)楦魇皆O(shè)備適配完善的輸入支持了》。
在如此多樣化的硬件生態(tài)系統(tǒng)中,您可能很難擁有各種形狀和尺寸的設(shè)備,如今 Android SDK 為可折疊設(shè)備提供了模擬器圖像,這些模擬器允許您隨時(shí)將折疊狀態(tài)更改為鉸鏈的角度。即將推出的 Android Studio Chipmunk 也會(huì)配備可調(diào)整尺寸的模擬器,允許您自由改變應(yīng)用窗口的尺寸,每個(gè)開(kāi)發(fā)者都可以在幾乎任何類型的設(shè)備中試用他們的應(yīng)用。
△ Android Studio Chipmunk 中的可調(diào)整尺寸的模擬器
Android Studio Chipmunk
https://developer.android.google.cn/studio/preview
我們也一直在 Android Studio 中開(kāi)發(fā)新工具,希望為大家開(kāi)發(fā)大屏幕應(yīng)用提供支持。新的 Layout Validation 工具可以在覆蓋了各種尺寸類別的參考設(shè)備上預(yù)覽布局,并提示問(wèn)題區(qū)域 (例如文本使用了長(zhǎng)行),以及為不同斷點(diǎn)推薦不同界面組件。
△ Android Studio 中的 Layout Validation
最后,我們?cè)?Android 開(kāi)發(fā)者網(wǎng)站上列出了針對(duì)大屏幕的應(yīng)用質(zhì)量指南,指南中的前面部分介紹的是基本兼容性預(yù)期,比如應(yīng)用是否同時(shí)支持橫屏和豎屏模式,后面幾部分重點(diǎn)介紹支持各種屏幕類型和狀態(tài),并使用特定屏幕類型或狀態(tài)打造不同的體驗(yàn)。
大屏幕的應(yīng)用質(zhì)量指南
https://developer.android.google.cn/docs/quality-guidelines/large-screens-app-quality
我們希望大家都能夠利用今天分享的內(nèi)容,并參考新的質(zhì)量指南,構(gòu)建出在各種屏幕尺寸下都能讓用戶心動(dòng)的應(yīng)用。
廣告聲明:文內(nèi)含有的對(duì)外跳轉(zhuǎn)鏈接(包括不限于超鏈接、二維碼、口令等形式),用于傳遞更多信息,節(jié)省甄選時(shí)間,結(jié)果僅供參考,IT之家所有文章均包含本聲明。