前言
隨著近年來手機行業的飛速發展,手機從功能機進入到智慧機,手機螢幕佔比也隨著技術和系統的進步越來越大,特別是Android 10推出以後,摺疊屏逐漸成為Android手機發展的趨勢。
圖 1 Android手機螢幕發展趨勢
京東小程式近年來也支援了越來越多的業務和應用,做好小程式的摺疊屏的適配也是符合未來的發展趨勢,能為使用者和業務方提供更好的體驗和價值。
Android應用摺疊屏適配摘要
應用在摺疊屏執行時,可以從一個螢幕切換到另一個螢幕。應用應該做好配置變更的適配和介面狀態的儲存,以保證應用當前任務能無縫遷移到轉換後的螢幕,從而為使用者提供出色的連續性體驗。
1.resizeableActivity
預設情況下,Activity resizableActivity 屬性為 true,系統假定該應用完全支援多視窗並且可調整大小。
圖 2 Android手機摺疊屏
如果您不希望自己的應用在多視窗模式下調整大小,你可以設定Activity resizableActivity 屬性為false,系統會將應用置於相容模式。某些原始裝置製造商 (OEM) 可能會實施一項功能,即每當 Activity 的顯示區域發生更改時,都會在螢幕上新增一個小型重啟圖示。這為使用者提供了在新配置中重啟 Activity 的機會。下圖示例展示了一次內屏到外屏,外屏到內屏切換中系統相關處理。
圖 3 摺疊屏應用重啟示例
此外,使用者需要“設定”-“顯示”中開啟應用的“在外屏上繼續使用應用程式”開關,否則,切換到外屏時系統將回到鎖屏介面,應用會被壓至後臺。不支援resize的應用會無法開啟此開關。
圖 4 Android摺疊屏展示開關
2.螢幕寬高比
Android 10 (API 級別 29) 或更高版本 支援更多種寬高比。對於可摺疊裝置而言,裝置型別可以是超長、超薄的螢幕(例如螢幕寬高比為 21:9 的摺疊裝置),也可以是 1:1 的螢幕。
如要與儘可能多的裝置相容,您應該儘量多針對以下螢幕寬高比測試自己的應用:
圖 5 Android手機螢幕寬高比
如果無法支援上述某些高寬比,您可以使用 maxAspectRatio(同之前一樣)以及 minAspectRatio 來指明自己應用可以處理的最高寬高比和最低寬高比。如果螢幕寬高比超出這些限制,應用可能會進入相容模式。
3.處理配置變更
某些裝置配置可能會在執行時發生變化(例如螢幕方向、鍵盤可用性,以及當使用者啟用多視窗模式時)。發生這種變化時,Android 會重啟正在執行的 Activity(先後呼叫 onDestroy() 和 onCreate())。重啟行為旨在透過利用與新裝置配置相匹配的備用資源來自動重新載入您的應用,從而幫助它適應新配置。
如要妥善處理重啟行為,Activity 必須恢復其先前的狀態。您可以同時使用 onSaveInstanceState()、ViewModel 物件以及持久儲存,以在配置變更時儲存並恢復 Activity 的介面狀態。
然而,您可能會遇到這種情況:重啟應用並恢復大量資料不僅成本高昂,而且會造成糟糕的使用者體驗。在此情況下,我們通常可以自行處理配置變更,以避免系統資源變更引起Activity重啟,透過在標籤中新增android:configChanges宣告實現。android:configChanges 屬性文件中列出該屬性的可能值。最常用的值包括 "orientation"、"screenSize" 和 "keyboardHidden” 等。
總之,為了做好Android應用的摺疊屏適配,應用應能妥善地儲存介面狀態和支援配置變更,並進行詳細的測試,詳細適配指導方案可以參考官方文件。
小程式摺疊屏適配現狀
小程式不同於原生的Android應用,微信小程式框架目前是基於webview渲染,小程式邏輯層、檢視層等進行相關檢視、元件的計算渲染時依賴於獲取到的裝置尺寸資料,當螢幕尺寸發生變化時,不可避免的會造成佈局樣式的錯亂。小程式業內目前還沒有官方的摺疊屏適配方案。以健康寶微信小程式為例,發生摺疊後,不僅介面上存在問題,還存在無法從歷史任務棧中開啟的問題。
圖 6 微信應用Android手機摺疊屏效果
此外,從微信開發社群我們瞭解到,有不少開發者對於小程式摺疊屏適配還是有訴求的。
圖 7 微信小程式摺疊屏適配訴求
京東小程式摺疊屏適配
1.京東小程式摺疊屏問題
京東小程式也存在元素尺寸不合適、摺疊後無法從任務棧中再次開啟等問題,我們看一下京東快遞小程式的現象。
圖 8 京東小程式適配前
內屏開啟小程式狀態:
圖 9 京東小程式適配前-內屏
內屏轉外屏狀態:
圖 10 京東小程式適配前-內屏轉外屏
外屏開啟小程式狀態:
圖 11 京東小程式適配前-外屏
外屏轉內屏狀態:
圖 12 京東小程式適配前-外屏 轉內屏
總之,就是在無論是內屏還是外屏,初次開啟時獲取到的螢幕尺寸資料是對的,小程式能按照適合的尺寸渲染元素;一旦發生摺疊,在新的狀態要麼是元素過大不適合窄屏,要麼是元素過小不適合寬屏。
那麼問題來了,為什麼在初試開啟狀態頁面上的元素是大小合適的呢?
2.小程式多螢幕適配
rpx ( responsive pixel)響應單位
rpx是微信小程式獨有的、解決螢幕自適應的尺寸單位, 在小程式開發中,推薦使用rpx這種響應式的畫素單位進行開發
可以根據螢幕寬度進行自適應,不論大小螢幕,規定螢幕寬為750rpx,以 iPhone6 為基準,iPhone6 的螢幕寬度為 375px,則 750rpx = 375px
真實裝置獲取到的物理畫素是多種多樣的,在小程式內部透過真實物理畫素與375的比值得到縮放比例,真正渲染使用時再轉換為對應的畫素,透過 rpx 設定元素和字型的大小,小程式在不同尺寸的螢幕下,可以實現自動適配。
3.摺疊屏問題分析
元素尺寸問題:
在摺疊屏展開狀態開啟小程式,此時取到的裝置尺寸等均為展開時的資料,螢幕摺疊後,元素大小沒有發生變化,但是承載小程式的容器大小變化了,螢幕變窄了,於是按照原有的尺寸,所有的佈局空間發生壓縮,導致頁面擠壓在一起。
同樣的,在外屏開啟小程式時獲取到的尺寸資料是適合外屏的,再摺疊到內屏狀態時也無法及時更新到內屏的尺寸。
究其原因,在發生螢幕摺疊時,小程式沒有獲取到最新的螢幕資料,無法更新螢幕縮放比,同時沒有機制通知小程式進行重新渲染或載入。
無法重啟問題:
小程式在Android端執行在獨立的程式中,不同小程式執行在不同程式,小程式引擎具有自己獨有的管理機制。在之前螢幕摺疊後小程式被殺死程式,透過歷史任務棧無法再次拉起該程式。
4.解決思路
監聽螢幕摺疊:
1.記錄當前螢幕引數(寬、高、方向)
2.在onConfigurationChanged(Configuration newConfig)回撥中獲取最新螢幕配置
當螢幕發生摺疊後,系統會將newConfig下發給應用程式,取出newConfig.orientation 、newConfig.screenWidthDp 和 newConfig.screenHeightDp , 與 之前儲存的螢幕引數進行對比。如果寬、高發生變化,通常認為螢幕發生摺疊。
3.細節處理
a.由於視屏播放器全屏狀態下通常會是橫屏狀態,當從全屏狀態切回正常模式時往往會回到豎屏,這裡螢幕的 orientation 會與之前的不同,不能當做摺疊處理。
b.摺疊屏手機螢幕往往底部還有一個最近應用的快捷導航條,如果是開啟狀態,因為需要重匯的緣故,在發生摺疊後,系統會觸發兩次onConfigurationChanged(Configuration newConfig)回撥,而且兩次回撥的引數中 newConfig.screenHeightDp 會前後不一致,這裡需要做一下相容處理,否則會誤判為多次摺疊。
圖 13 摺疊屏導航條
圖 14 摺疊屏導航條2
不同的底部導航條
元素尺寸問題:
要解決此問題,就要在識別到螢幕尺寸發生變化時,及時通知到業務,有兩種方案:
1.區域性重新整理:通知業務自行重新整理
這種方案可以在一定程度上保留使用者操作流程的完整,但是也存在非當前頁面無法重新整理或者或退後再次重新整理等問題,對使用者來說體驗一般,而且需要小程式業務的開發者來監聽頁面變化,增加了開發者的業務複雜度。
2.整體重新整理:重啟小程式
這種方案是客戶端引擎監聽到裝置發生摺疊時,關閉小程式,並進行重新開啟。可以很好地保障頁面的重新適配,重啟行為會對使用者操作流程完整性有一定的損傷,對小程式開發者來說沒有工作量。
無法重啟問題:
針對此問題,引擎側需要避免殺死小程式所在程式,同時結合上面 2 個頁面重新整理方案,綜合考慮,採用在當前程式整體重新整理、重啟小程式方案。一方面解決了歷史任務棧無法重啟問題,另一方面避免了建立新程式的開銷,介面上給人的感官也更流程。
5.遇到的問題及解決方案
1.multiWindow、pictureInPicture問題
Android系統還有兩個功能就是多視窗和畫中畫模式,activity可以縮放為一個小視窗,在螢幕中顯示一小塊區域,能夠很靈活的拉伸縮放,對於此,小程式引擎忽略了視窗大小的變化,否則使用者只要一縮放就會重啟小程式,這是我們和使用者都無法接受的。這種情況下,保持不變是契合多視窗的設計初衷的,讀者在處理類似的適配方案時應當注意多視窗、畫中畫問題。
2.onConfigurationChanged多次回撥問題
不同的廠商或者不同的使用者配置,會在發生摺疊時,因為狀態列或者系統底部的虛擬按鍵等設定,觸發不同次數的onConfigurationChanged回撥,回撥下發的screenHeightDp數值不一致。上文已經提到,需要針對回撥引數下發的newConfig資料做真正的摺疊判斷,忽略“偽配置變更”。
3.onNewIntent問題
不考慮摺疊屏的情況下,京東小程式在多棧模式下返回時並不是真正的關閉小程式,而是壓到後臺,沒有觸發activity的finish。當使用者再次開啟時會觸發onNewIntent事件,這裡會進行小程式的重啟。
但是遇到摺疊屏,就會觸發onConfigurationChanged 和 onNewIntent 都回撥的情況,透過查閱原始碼和列印日誌方式,我們可以發現onConfigurationChanged的回撥早於onNewIntent的。所以onConfigurationChanged一旦識別到發生螢幕摺疊就會重啟小程式,在onNewIntent這裡應該避免再次重啟小程式。
4.webview和js引擎獲取螢幕寬高失真問題
在適配中我們遇到過在某些機器上“沒問題”,在其他機器上“很容易復現”的窘境。在理論和實際上,客戶端傳遞給邏輯層、檢視層的尺寸資料都沒問題,但是小程式表現上還是存在問題。經過細緻的排查,發現js引擎上有些資料的是來自於window物件的寬高資料,此資料與摺疊後的螢幕資料不一致,即webview和js引擎獲取到的裝置尺寸更新不及時,造成rpx計算失準。為此,我們替換了引擎中對window寬高的使用方式,替換為螢幕真正的資料。
6.修復效果展示
透過以上措施,經過驗證,我們小程式在摺疊屏上的相關體驗達到了比較令人滿意的效果。
內屏轉外屏:
圖 15 摺疊屏適配後-內屏轉外屏
外屏轉內屏:
圖 16 摺疊屏適配後-外屏轉內屏
外屏壓後臺,再轉內屏:
圖 17 摺疊屏適配後-後臺喚起
總結
摺疊屏作為未來Android螢幕發展的新趨勢,具有很大的發展前景,做好摺疊屏相關適配支援也勢在必行。小程式相關適配已經跟隨京東主站、小家App、小家三星預裝版等釋出上線,本文是作者進行相關適配的一些心得體會,如有不足敬請見諒,歡迎交流探討。
作者:京東零售 張磊
內容來源:京東雲開發者社群