移動設備關於顯示效果的問題尤其突出,分辨率、屏幕尺寸各異,處理起來相對比較麻煩,好在Adobe本身已進行了相關處理,它主要是根據3個DPI來進行相應處理的。(用 Google Android SDK 的 Project 中的draw 目錄默認也是按 low、medium、high 區分的,所以按3個DPI處理應該是比較正確的方法啦)。
相關資料:
手機屏幕技術淺述(TFT、SLCD、AMOLED、NOVA、IPS、ASV)
Adobe官方文 檔:在一個手機應用程序中支持多個屏幕大小和 DPI 值
Mobile Development: scaling content depending on the screen resolution
Define a mobile application and a splash screen (適用於Flex SDK v4.6.0)
一、簡單小結
官方給出的解決方法主要有以 下3種:(殊途同歸:最終都是靠DPI定位的)
1. 使用自動縮放:(方便和確保像素精確的視覺保真度之間的一種折衷方法)
設置 application 的 applicationDPI 屬性值爲某個DPI;
只需要針對目標DPI值創建外觀和組件佈局;
位圖需最好要有多個版本,使用 MultiDPIBitmapSource 類進行設置;
css中不需要使用@media。
2. 不使用自動縮放:
需要針對3個DPI來創建外觀或組件佈局或支持根據DPI動態調整的外觀和佈局;
在css中通過@media分別設置3套值(主要要設置字體大小、內邊距)。
3. 自定義設備的DPI歸類:(無論是否自動縮放均可)
編寫一個 extends RuntimeDPIProvider 的類;
設置 application 的 runtimeDPIProvider 爲該類;
該類 override get runtimeDPI 方法,然後在該方法中可以根據Capabilities.screenDPI、Capabilities.screenResolutionY 等自定義設備的DPI 屬於哪一個。
其他幾篇文章提供的思路:
1. 儘量根據%、stage.stageWidth、stage.stageHeight 來設置組件寬度、高度;
2. 如果一定要用絕對值,則最好使用物理單位(如inch、cm),然後將該值轉化爲像素。
3. 使用絕對值時,在分辨率較高的屏幕上,組件會顯得較小,此時可以根據分辨率計算比例來對組件進行縮放。
二 、UI設計要點
1. 圖標大小:
1) 類似置於狀態欄、標題欄位置的圖標,比較合適的大小分別爲24、32(似乎36更合適)、48px;
2) 另一套比 1) 稍大的圖標大小標準分別爲36、48、72px。
2. itemrenderer 的高度,查看SDK源碼得知其默認被處理爲44、66、88px,可以按此標準考慮應用的某些尺寸。(調試發現,實際輸出高度分別爲:52、72、104)
三 、與分辨率、DPI相關的API
Capabilities.screenResolutionX 設備的橫向分辨率,在移動設備該值通常是較小的那個,如480*800的480。
Capabilities.screenResolutionY 設備的豎向分辨率。
Capabilities.screenDPI 設備的實際DPI。
FlexGlobals.topLevelApplication.runtimeDPI 設備的近似DPI,只有160、240、320這3個值。
DPIClassification.DPI_160, DPIClassification.DPI_240, DPIClassification.DPI_320 160,240,320對應的常量。
FlexGlobals.topLevelApplication.applicationDPI 應用的DPI,如未指定,其值=runtimeDPI。
四、實踐記錄(FlashBuilder4.5.1 + Flex SDK4.5.1 +AIRSDK2.7)
1. 桌面環境調試mobile應用時,Capabilities.screenResolutionX, Capabilities.screenResolutionY 返回的值是當前PC的分辨率,此時如果需要用到這2個值時只能hardcode。
2. 如果 view 的 actionBarVisible=true,則標題欄在3個DPI下的高度會有所不同,DPI320下會比較高,此時可能會影響到這個view最底部的元素的可見度,另外navigationContent、actionContent的寬度也與DPI有關(1024*600 DPI160時點擊範圍略微有些小),所以最好不要使用標題欄,若使用則需要測試下view內容是否能完整顯示,要不然只能給整個view加上scroll(僅爲了這一點加的話似乎不太划算)。可以考慮做個仿真的標題欄。
3. 實測我的應用,之前開發時未指定 applicationDPI,主要以480*800 240DPI(目前Android設備市場主流)爲參考進行UI設置的,字體大小爲28px,在 3.7 inch 寸設備上看起來大小比較適中。
然後分別在不同分辨率和DPI下進行測試,除了 320*480 DPI160下由於字體過大無法顯示完整的整屏內容外,在其他設備上均顯示良好,當然在分辨率高於480*800的設備上,原剛好一屏的內容會顯得有些“營養不良” 。
指定 applicationDPI=240 之後再次測試,分辨率在 600*1024 及其以上的設備上時,由於其默認DPI被歸於160,所以字體等被縮小了,顯得有些過小,不太合適。
4. 我的應用最終所採用的方案,共有以下幾點(幾乎把能用的都用上了):
1) 不使用自動縮放;(自動縮放存在一些問題)
2) 自定義 extends RuntimeDPIProvider 類,改寫默認的 runtimeDPI 策略;
主要改寫之處:
a) 分辨率在600*1024以上的設備的DPI,默認情況下均近似爲160,而這些設備的物理尺寸相對大部分手機來說是絕對夠大的,所以將其改寫爲歸於 240之列;
b) Motorola 的幾款手機分辨率高於480*800,如 Atrix 4G,其分辨率爲540*960,實際DPI爲275,默認情況下被歸於240之列,但由於其物理尺寸並未明顯加大,導致應用的字體等看起來偏小,所以將其改寫爲歸於320之列。
注:Moto Atrix 4G真機下運行測試,發現所得到的screenDPI居然是240,其桌面模擬環境是275,於是自定義的RuntimeDPIProvider 還得略作調整。
3) 位圖資源按ldpi、mdpi、hdpi分別準備3套 (這個無論是否自動縮放均需要);
4) css中使用@media 設置fontsize;
5) 使用物理單位,設置時將其換算成pixels,主要用在需要固定高度的地方,如view的頂部、底部、某些item等;
6) 根據分辨率scale組件,主要用在彈出窗口的寬度設置上,當分辨率較高時適當加寬以保證較佳的顯示效果;
7) 自定義style屬性,將諸如高度、間距等既需要固定值又需要根據DPI調整的地方,以這種方式綁定到css中,通過@media方式設置其值。(查看SDK源碼發現其對Button組件icon的處理也是採用style方式實現的 , 所以還是得多爬爬源碼啊 )
8) 啓動時所顯示的圖片,如果在應用程序mxml文件裏通過 splashScreenImage 指定1個圖片的話(同時通過 splashScreenScaleMode 設置拉伸方式),則在某幾個分辨率下,圖片拉伸後的效果是欠佳的。
Flex移動skin – 第2部分:處理不同的像素密度 和 Dynamic Splash Screen Improvements 中提到的方法經過實踐,無法通過編譯,可能是SDK(版本應該是4.6.0的)不同的緣故。根據文章提供的思路,目前我採用以下方法來解決:
注:FlexSDK v4.6.0中已經添加了 SplashScreenImage、SplashScreenImageSource,以下方法僅適用於 4.5.x 版本,當運行於 4.6.0 環境時,啓動界面會出現2次,第2次所顯示的圖片有點不正確,有空白出現(這個問題MS是另外寫了個首頁導致的)。
a) 複製spark.preloaders.SplashScreen 爲1個新類,application 的 preloader 屬性設爲該新類,splashScreenScaleMode 屬性可用可不用(依賴於你對SplashScreen所進行的改動);
b) 新類相較於原SplashScreen類,主要改動 initialize() 方法中 if ("splashScreenImage" in info) 處,原 splashScreenImage 值是1個固定的圖片source,這裏要把它改成根據不同的dpi、分辨率來得到其圖片source,所以我在 if ("splashScreenImage" in info) 之前增加了獲取圖片source的邏輯,同時對 if ("splashScreenImage" in info) 這個語句也進行了相應修改,修改後的代碼片段示例如下:
public function initialize():void { ... if (!info) return; // 以下是我增加的 var runtimeDPIClass:Class = info["runtimeDPIProvider"]; var runtimeDPIProvider:RuntimeDPIProvider = new runtimeDPIClass(); buildSplashImage(runtimeDPIProvider.runtimeDPI); // 原if改爲如下 // if ("splashScreenImage" in info) if (splashImage != null) { // 註釋掉以下5行 /*var SplashImageClass:Class = info["splashScreenImage"]; this.splashImage = new SplashImageClass(); this.splashImageWidth = splashImage.width; this.splashImageHeight = splashImage.height; addChild(splashImage as DisplayObject);*/ ... } // 根據不同的dpi獲取不同的圖片源 private function buildSplashImage(dpi:Number):void { // 具體的圖片源邏輯主要通過類 MultiDPISplashScreen 的 getImageClass 方法中進行處理,這裏就不貼源碼了 var splashImageClass:Class = new MultiDPISplashScreen().getImageClass(dpi); splashImage = new splashImageClass(); if (splashImage != null) { splashImageWidth = splashImage.width; splashImageHeight = splashImage.height; addChild(splashImage as DisplayObject); } }
總之,Adobe 對開發mobile應用的支持還是很不錯的,本身提供的模擬器在調試UI方面比較完善,常見的設備均有,並且也可以自行添加,顯示效果與真機相仿,爲我們調試在不同設備的顯示效果提供了良好的支持。
另外,這個模擬器雖然只是提供了一個顯示屏幕和menu、back、search按鈕、旋轉屏幕功能,但也是因爲這樣使得它在PC上運行的速度較快,不像Android模擬器那樣耗資源。