最近在工作中遇到了屏幕適配的問題汹押,這里做個記錄,方便以后查看奏寨。
先貼出參考的文章:參考文章
場景是這樣的:做一個widget榛搔,他沒有activity application之類的,是由主應(yīng)用反射拿到對應(yīng)的view去做渲染说贝,我的工作是做皮膚應(yīng)用议惰,不涉及到主應(yīng)用的汹押。
那么問題來了形耗,這意味著有很多限制歹河,比如無法用那些UI適配框架去適配耍属。
先說一下分辨率相關(guān)的那幾個單位吧:
屏幕尺寸:即手機(jī)屏幕對角線的長度 單位是英寸 inch琳轿。
屏幕分辨率:手機(jī)屏幕的寬 高像素點(diǎn)數(shù) 單位是px蚯撩。一般設(shè)計給的設(shè)計稿也是以這個為單位的逗爹。
dpi:屏幕像素密度 1英寸有多少像素绑蔫。Android上會以這個來區(qū)分 mdpi hdpi xdpi 等資源文件夾蛋哭。
dp:密度無關(guān)像素 單位 dp县习,Android上推薦使用的單位
desity:密度,表示 1dp等于多少px
上面那些單位的計算方式:
-
dpi :
tip:真實(shí)的dpi其實(shí)是拿的系統(tǒng)配置項(xiàng)里的 ro.sf.lcd_density,可以通過adb shell命令查看:
$ cd system
$ cat build.prop|grep density
-
desity:
-
dp:
網(wǎng)上查閱了一些適配的方案躁愿,大概有以下幾種:
1. dp直接適配叛本,建 mdpi、hdpi彤钟、xdpi来候、xxdpi、xxxdpi 這幾個values文件 逸雹,分別去寫dimens营搅。這種方式太麻煩,要根據(jù)不同的手機(jī)一點(diǎn)點(diǎn)去調(diào)梆砸,而且適配效果也不好转质,對應(yīng)相同dpi,不同尺寸的手機(jī)也不能很好去適配帖世。
2. 寬高限定符適配休蟹,就是建不同手機(jī)寬高像素的values文件 如:values-480×320,然后找到一個基準(zhǔn)分辨率日矫,其他的分辨率都根據(jù)這個基準(zhǔn)去把寬高等分赂弓。這樣好處是我們只需要適配一個分辨率即可,缺點(diǎn)是命中率太低哪轿,如果沒有找到當(dāng)前運(yùn)行的手機(jī)分辨率的文件夾盈魁,就會去拿默認(rèn)dimens里的值,這樣UI就可能變形了缔逛。
比如以480x320為基準(zhǔn)分辨率
寬度為320备埃,將任何分辨率的寬度整分為320份,取值為x1到x320
高度為480褐奴,將任何分辨率的高度整分為480份按脚,取值為y1到y(tǒng)480
那么對于800480的分辨率的dimens文件來說,x1=(480/320)1=1.5px x2=(480/320)*2=3px
- UI適配框架敦冬,比如鴻洋的AutoLayout辅搬,這種使用簡單,前提是你有AndroidManifest和Activity脖旱,而且現(xiàn)在該項(xiàng)目已經(jīng)停止維護(hù)堪遂。(對于我來說并不適用??)
4. 今日頭條的適配方案,只要能拿到上下文環(huán)境context就行萌庆。他是修改當(dāng)前應(yīng)用獲取到的density值溶褪。主要方法是根據(jù)設(shè)計圖的寬度dp,重新計算出density践险,然后賦值給DisplayMetrics的density猿妈。不過我試了一下吹菱,效果不錯,就是導(dǎo)致在部分手機(jī)上主應(yīng)用的字體偏小彭则,可能是主應(yīng)用里有不同dpi的values文件導(dǎo)致的鳍刷。
感興趣的可以看看今日頭條適配方案,主要代碼如下:
DisplayMetrics appDisplayMetrics = mainPkgContext.getResources().getDisplayMetrics();
if (sNoncompatDensity == 0) {
sNoncompatDensity = appDisplayMetrics.density;
sNoncompatScaledDensity = appDisplayMetrics.scaledDensity;
// 當(dāng)修改系統(tǒng)字體時俯抖,會回調(diào)此方法
mainPkgContext.getApplicationContext().registerComponentCallbacks(new ComponentCallbacks() {
@Override
public void onConfigurationChanged(Configuration configuration) {
if (configuration != null && configuration.fontScale > 0) {
sNoncompatScaledDensity = mainPkgContext.getResources().getDisplayMetrics().scaledDensity;
}
}
@Override
public void onLowMemory() {
}
});
}
// 320是設(shè)計圖的寬度dp输瓜,可以根據(jù)自己的設(shè)計稿去修改
float targetDesity = appDisplayMetrics.widthPixels / 320;
float targetScaledDensity = targetDesity * (sNoncompatScaledDensity / sNoncompatDensity);
int targetDesityDpi = (int) (160 * targetDesity);
appDisplayMetrics.density = targetDesity;
appDisplayMetrics.scaledDensity = targetScaledDensity;
appDisplayMetrics.densityDpi = targetDesityDpi;
DisplayMetrics activityDisplayMetrics = context.getResources().getDisplayMetrics();
activityDisplayMetrics.density = targetDesity;
activityDisplayMetrics.scaledDensity = targetScaledDensity;
activityDisplayMetrics.densityDpi = targetDesityDpi;
5. 最小寬度限定符適配,和寬高限定符適配差不多芬萍,也是建不同手機(jī)寬度dp的values文件 如:values-sw320dp尤揣,系統(tǒng)會根據(jù)手機(jī)的寬度去拿不同文件夾下的值。
我們只需要根據(jù)設(shè)計稿的寬度計算出各個sw文件的px對應(yīng)多少dp柬祠,使用的時候直接根據(jù)設(shè)計稿去引用px就行芹缔。網(wǎng)上有很多自動生成這些文件的工具,貼出我使用的一個:自動生成sw工具 java項(xiàng)目
這種方式優(yōu)點(diǎn)是容錯性提高了瓶盛,如果沒有找到當(dāng)前運(yùn)行的手機(jī)寬度的文件夾,就會去拿與之相近的dimens里的值示罗,這樣UI就不會出現(xiàn)太大的差異惩猫。
最終我還是選擇了最小寬度限定符的方式,因?yàn)檫@種侵入性小蚜点,不過就是太多的values文件會增加應(yīng)用的大小轧房。
如果有不對的地方還望大佬指出。