京東小程式近年來支持了越來越多的業務和應用,做好小程式的摺疊屏的適配也是符合未來的發展趨勢,能為用戶和業務方提供更好的體驗和價值。 ...
前言
隨著近年來手機行業的飛速發展,手機從功能機進入到智能機,手機屏幕占比也隨著技術和系統的進步越來越大,特別是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、小家三星預裝版等發佈上線,本文是作者進行相關適配的一些心得體會,如有不足敬請見諒,歡迎交流探討。
作者:京東零售 張磊
內容來源:京東雲開發者社區