和手機屏幕一樣,高分屏在PC上使用越來越多。傳統的桌面程序都是像素為單位進行UI布局,而且是適配傳統的96dpi的顯示器的。這就導致這些程序在高分屏上顯示很小,用戶用起來很難受。
雖然windows系統提供了桌面程序自動放大功能,但這個放大效果是以犧牲顯示效果為代價的,一個在普屏上顯示很好的軟件到了高分屏上顯示變得非常粗糙。
要解決這個問題非常麻煩,對於有些項目甚至變成不可能完成的任務:
如果使用同一套布局,布局系統需要支持放大時使用的字體的重建,還需要支持放大時使用的圖片資源的重新獲取,還需要支持放大時布局位置的自動調整,然而上述3條實現任意一條都不容易。
如果使用不同的布局,就是說為普屏配置一套布局,為高分屏配置另一套布局,盡管麻煩,理論上也是可行的,但還有新的問題:維護成本高,一個地方UI元素的調整可能需要修改兩個不同的布局文件;不適應動態調整,對於多顯示器有不同DPI的情況,很難做到DPI的動態切換。
Android系統對於DPI的支持比較理想(IOS沒有研究):
首先Android的布局語言使用的坐標一般都用dp,sp為單位,也就是說在布局的時候,UI的位置,字體的大小都是dpi無關的,無論在高分屏還是普屏下顯示的大小都是一樣的。
對於圖片,Android系統提供適配多種DPI的圖片資源包,如果開發者只提供一種資源包,則使用這個資源包適配目標屏幕的dpi進行縮放,同樣達到類似dpi無關的顯示效果。
當然Android系統適配手機通常只有一個屏幕,不需要做動態DPI切換,這一點是和PC不同的地方。
鑒於我對於Android還有些經驗,SOUI的dpiaware工作主要是參考Android的設計。
如前所述,要在一套UI布局中支持dpiaware,SOUI需要支持DPI變化時的字體重建,布局坐標重排,圖片資源重新獲取3部分工作。
首先明確DPI是布局元素相關的,SOUI中最基本的布局元素就是SWindow對象,要在SOUI實現DpiAware, 我們在DPI變化時給每個SWindow發一個UM_DIPCHANGED消息,SWindow對象收到這個消息后處理自己持有的字體,圖片的重建,完成后請求宿主重新執行布局操作。
1 字體重建:SOUI有一個UI中使用的字體Pool,在窗口中保存有使用的字體的描述,DPI變化后直接使用保存的字體描述重新創建新的字體。
2 圖片重建:SOUI中使用ISkinObj來代表一個繪制對象,系統內的繪制對象保存在SkinPool中,每一個繪制對象有兩個屬性:name, scale (scale屬性是新增加的),DPI變化時,一個窗口持有的ISkinObj對象到SkinPool中查詢name相同但scale不同的ISkinObj,找到了就使用這個新對象繪制,如果沒有找到則使用當前圖片自動縮放出一個Clone對象,並把它加入到SkinPool里去。
3 重新布局:相對於2.6以前版本的SOUI的布局,2.6版本的坐標描述增加了坐標單位dp,在窗口的位置及大小描述中加上dp就代碼這個值是dpi相關的坐標,窗口的DPI變化后自動使用新DPI重新布局。
上面是DPIAware的基本解決方案,當然細節還有很多,具體參見系統代碼。
下面我們看一下MultiLangs這個demo如何處理dpiaware。
首先演示圖片的自動dpi適配。
1 在資源中增加適配不同DPI的4個圖片資源,如下圖:

2 在uires.idx里引入這4個圖片。
<img> <file name="pic_100" path="image\pic_100.png"/> <file name="pic_125" path="image\pic_125.png"/> <file name="pic_150" path="image\pic_150.png"/> <file name="pic_200" path="image\pic_200.png"/> </img>
3 在skin.xml中增加4個skin對象, 注意它們使用相同的name,不同的scale
<skin> <imglist name="scale_pic" scale="100" src="img:pic_100"/> <imglist name="scale_pic" scale="125" src="img:pic_125"/> <imglist name="scale_pic" scale="150" src="img:pic_150"/> <imglist name="scale_pic" scale="200" src="img:pic_200"/> </skin>
4 最后就是在布局XML中引用該skin對象,直接使用name引用即可,不需要關注scale
<page title="page1"> <img skin="scale_pic" pos="|0,|0" offset="-0.5,-0.5"/> <window pos="20,20,@100dp,@30dp" colorBkgnd="rgba(255,0,0,128)">100dp*30dp</window> </page>
到此完成了圖片的配置。
字體和布局的DPIAware很簡單,只需要在那些需要dpi自動縮放的位置或者字體大小數字后加上dp即可,可參考上面的XML, pos,size,width, height等屬性都可以通過加dp來實現dpiaware。
DpiAware至此完成!
啟程軟件 2017年5月4日
