本篇文章已授權(quán)微信公眾號(hào) guolin_blog (郭霖)獨(dú)家發(fā)布
1. 序言
- 屏幕適配哪家強(qiáng):
① 到底哪一種屏幕適配最合適,仁者見(jiàn)仁智者見(jiàn)智.
② 開(kāi)始我推薦dimens基于px的適配,后來(lái)我推薦dimens dp的適配孤里,而如今我推薦今日頭條適配(修改手機(jī)的設(shè)備密度 density)菜枷。 - 效果圖多大才更好:
推薦1倍效果圖察郁,即采用 720 * 360 大小( 1280 *720:兩倍圖 \ 1920 * 1080: 三倍圖),最主要的原因就是1px = 1dp汗洒,效果圖標(biāo)多大的px,布局就寫(xiě)多大dp。
2. 屏幕各項(xiàng)參數(shù)
- 像素 - px:
一個(gè)小黑點(diǎn)就是像素解取。
- 尺寸:
屏幕的對(duì)角線的長(zhǎng)度。
- 分辨率:
整個(gè)屏幕一共有多少個(gè)點(diǎn)返顺,也就是像素禀苦。
- 像素密度 - dpi:
1. 每英寸中的像素?cái)?shù)蔓肯。
2. 假如設(shè)備分辨率為320*240,屏幕長(zhǎng)2英寸寬1.5英寸振乏,dpi=320/2 = 240/1.5 =160蔗包。
3. 對(duì)應(yīng)于DisplayMetrics類(lèi)中屬性densityDpi的值。
4. 當(dāng)然這種寬和高的dpi都相同的情況現(xiàn)在已經(jīng)很少見(jiàn)慧邮。
- 密度 - density:
1. 每平方英寸中的像素?cái)?shù)调限。
2. density = dpi / 160 。
3. 對(duì)應(yīng)于DisplayMetrics類(lèi)中屬性density的值 误澳。
4. 可用于px與px與dip的互相轉(zhuǎn)換 :dp = px / density 耻矮。
- 設(shè)備獨(dú)立像素 - dip - dp:
- 不同設(shè)備有不同的顯示效果,不依賴(lài)像素忆谓。
- dp = px / density
- dp = px / (dpi / 160)
- dpi(像素密度)為160 的設(shè)備上1dp = 1px裆装。
- 放大像素 - sp:
用于字體顯示。
- dp轉(zhuǎn)px倡缠、px轉(zhuǎn)dp
public class Dp2Px {
public static int dp2px(Context context, int dp) {
return (int) (dp * context.getResources().getDisplayMetrics().density + 0.5);
}
public static int px2dp(Context context, int px) {
return (int) (px / context.getResources().getDisplayMetrics().density + 0.5);
}
}
說(shuō)明:0.5 是為了避免損失精度哨免。
- 常見(jiàn)設(shè)備的dp、px毡琉、density的關(guān)系
① ldpi:
density:0.75
分辨率:240*320
關(guān)系:dp = px / 0.75
② mdpi:
density:1
分辨率:320 * 480
關(guān)系:dp = px / 1
③ hdpi:
density:1.5
分辨率:480 * 800
關(guān)系:dp = px / 1.5
④ xhdpi:
density:2.0
分辨率:720 * 1280
關(guān)系:dp = px / 2
⑤ xxhdpi:
density:3
分辨率:1080 * 1920
關(guān)系:dp = px / 3
- 代碼獲取參數(shù)值:
//以1280*720為基準(zhǔn):
//獲取手機(jī)屏幕的寬和高
int widthPixels = getResources().getDisplayMetrics().widthPixels;
int heightPixels = getResources().getDisplayMetrics().heightPixels;
//density
float density = getResources().getDisplayMetrics().density;
//dpi
int densityDpi = getResources().getDisplayMetrics().densityDpi;
//1dp = 多少px
int px = Dp2Px.dp2px(this, 1);
//1px = 多少dp
int dp = Dp2Px.px2dp(this, 1);
04-30 23:29:15.606 2484-2484/com.example.testscreen E/MainActivity: widthPixels: 720
04-30 23:29:15.606 2484-2484/com.example.testscreen E/MainActivity: heightPixels: 1280
04-30 23:29:15.606 2484-2484/com.example.testscreen E/MainActivity: density: 2.0
04-30 23:29:15.606 2484-2484/com.example.testscreen E/MainActivity: densityDpi: 320
04-30 23:29:15.606 2484-2484/com.example.testscreen E/MainActivity: px:2
04-30 23:29:15.606 2484-2484/com.example.testscreen E/MainActivity: dp:1
-
詳解屏幕尺寸铁瞒、分辨率、像素密度三者關(guān)系:
-
舉例說(shuō)明:屏幕分辨率為:1920*1080,屏幕尺寸為5吋的話丐谋,那么dpi為440:
3. 適配分類(lèi)
3.1. 圖片適配
- 在AndroidStudio的資源目錄res下有五個(gè)層級(jí)圖片文件夾芍碧,分別用來(lái)存放不同分辨率的圖片:
① drawable-ldpi :低分辨率(用的少了,一般不再用)
② drawable-mdpi:中分辨率
③ drawable-hdpi:高分辨率
④ drawable-xdpi:較高分辨率
⑤ drawable-xxdpi:超級(jí)高分辨率
⑥ drawable-xxxhpi:頂級(jí)分辨率
- 在對(duì)應(yīng)的文件夾下放置不同分辨率的圖片就可以很好的對(duì)圖片進(jìn)行適配号俐。
- 隨著屏幕越來(lái)越大泌豆,推薦xxdpi的一套切圖,這樣就可以向下和向上兼容吏饿,節(jié)省資源踪危。
- 對(duì)于圖標(biāo)使用svg格式,對(duì)于圖片仍然使用png猪落,svg的圖標(biāo)大小約是png的1/4贞远,在很大的項(xiàng)目中,圖標(biāo)有很多笨忌,這個(gè)時(shí)候svg的優(yōu)勢(shì)就凸顯無(wú)疑了蓝仲。
3.2. 布局適配
- 不同分辨率,創(chuàng)建不同的布局文件夾:
① layout-800 * 480
② layout-1280 * 720
......
- 手機(jī)會(huì)根據(jù)分辨率去找設(shè)定的不同大小的layout的布局。
- 實(shí)際開(kāi)發(fā)中這種使用的情況非常少袱结,因?yàn)檎加锰噘Y源亮隙,慎用。
3.3. 權(quán)重適配
- 當(dāng)兩個(gè)或者更多布局占滿屏幕寬或高的時(shí)候垢夹,子布局可以使用權(quán)重適配溢吻,常見(jiàn)于LinearLayout線性布局中。
3.4. 尺寸適配(dimens百分比適配)
- 這種適配的原理就是將寬和高通過(guò)百分比的原理分割為若干份:
以400 * 320為基準(zhǔn)果元,以寬舉例:寬分為了320份:每份1.0px煤裙,那1280*720的寬的每份就是2.25px。
- 創(chuàng)建values-400*320文件夾噪漾,創(chuàng)建dimens.xml文件,設(shè)置代碼如下:
<resources>
<dimen name="x2">2.0px</dimen>
</resources>
- 創(chuàng)建values-800*480文件夾且蓬,創(chuàng)建dimens.xml文件欣硼,設(shè)置代碼如下:
<resources>
<dimen name="x2">3.0px</dimen>
</resources>
- 代碼中使用:
R.dimen.x2
- 布局中使用:
@dimen/x2
- 詳解dimens-px 適配:
原理:根據(jù)市面上手機(jī)分辨率的占比分析,我們把1280和720設(shè)定為一個(gè)基準(zhǔn)恶阴,然后其他分辨率根據(jù)這個(gè)基準(zhǔn)做適配诈胜。基準(zhǔn)的意思(比如320*480的分辨率為基準(zhǔn))是:
① 寬為320冯事,將任何分辨率的寬度分為320份焦匈,取值為x1到x320
② 長(zhǎng)為480,將任何分辨率的高度分為480份昵仅,取值為y1到y(tǒng)480
例如對(duì)于800 * 480的寬度480:
<?xml version="1.0" encoding="utf-8"?>
<resources><dimen name="x1">1.5/dimen>
<dimen name="x2">3.0px</dimen>
<dimen name="x3">4.5</dimen>
<dimen name="x4">6.0px</dimen>
<dimen name="x5">7.5px</dimen>
可以看到x1 = 480 / 基準(zhǔn) = 480 / 320 = 1.5 ;它的意思就是同樣的1px缓熟,在320/480分辨率的手機(jī)上是1px,在480/800的分辨率的手機(jī)上就是1*1.5px摔笤,px會(huì)根據(jù)我們指定的不同values文件夾自動(dòng)適配為合適的大小够滑。
- 自動(dòng)生成 dimen-px 文件夾
① 首先下載jar包:
鏈接: http://pan.baidu.com/s/1crbwwI 密碼: dxwa
② 其次解壓查看jar運(yùn)行說(shuō)明.txt文件,定制以1280/720為基準(zhǔn)的分辨率,操作方法:
在你下載后的文件夾里面 按住Shift+鼠標(biāo)右擊進(jìn)入命令行對(duì)話框吕世,輸入
java -jar autolayout.jar 720 1280就會(huì)自動(dòng)生成res文件夾(默認(rèn)的是以1080/1280為基準(zhǔn)彰触,所以需要自己設(shè)置),假如你覺(jué)得這些value文件夾里面沒(méi)有你想要的分辨率可以在制定基準(zhǔn)分辨率的同時(shí)命辖,添加額外的分辨率(比如400/600)况毅,輸入java -jar autolayout.jar 720 1280 400,600,假如想多添加幾個(gè)額外的分辨率(又想添加500*700)只需把額外的分辨率用下劃線隔開(kāi)即可尔艇,輸入java -jar autolayout.jar 720 1280 400,600_500,700
③ 接著把res里面的value文件夾放到res下面即可
④ 標(biāo)注圖假如寬50px尔许,高80px,我們只需要把寬高寫(xiě)為@dimen/x50,@dimen/y80即可 - 已經(jīng)生成的dimen-px文件夾地址:
鏈接:https://pan.baidu.com/s/1jLXpQXs898hzIensZKV7gg 密碼:6xgs
3.5 . 代碼適配
Button button = (Button) findViewById(R.id.bt_main_button);
//獲取手機(jī)屏幕的寬和高
int widthPixels = getResources().getDisplayMetrics().widthPixels;
int heightPixels = getResources().getDisplayMetrics().heightPixels;
//給button設(shè)置寬和高
ViewGroup.LayoutParams layoutParams = button.getLayoutParams();
layoutParams.width = widthPixels / 2;
layoutParams.height = heightPixels / 2;
button.setLayoutParams(layoutParams);
舉例:資源是圖片寬和長(zhǎng)不定的一些圖片漓帚,需求是寬度一定母债,對(duì)圖片進(jìn)行展示,思路就是:根據(jù)圖片的寬高比,以及效果圖的高度毡们,來(lái)設(shè)置ImageView控件的長(zhǎng)度迅皇。
4. demins 基于px和dp適配的缺點(diǎn)
- dimen 基于px:
寬和高都經(jīng)過(guò)百分比的計(jì)算得到對(duì)應(yīng)的值,通過(guò)手機(jī)分辨率進(jìn)行適配衙熔,個(gè)人看來(lái)存在的問(wèn)題是:
第一登颓,Android不同分辨率的手機(jī)實(shí)在太多了,可能你說(shuō)主流就可以红氯,的確小公司主流就可以框咙,淘寶這種App肯定不能只適配主流手機(jī)。
第二痢甘,控件在設(shè)計(jì)圖上顯示的大小以及控件之間的間隙在小分辨率和大分辨率手機(jī)上天壤之別喇嘱,你會(huì)發(fā)現(xiàn)大屏幕手機(jī)上控件超級(jí)大∪ぃ可能你會(huì)覺(jué)得正常者铜,畢竟分辨率不同。但實(shí)際效果大的有些夸張放椰。
第三作烟,設(shè)計(jì)圖(比如360640)上的內(nèi)容占據(jù)屏幕的2/3,按照這種適配砾医,特長(zhǎng)手機(jī)(29601440 S8)上的內(nèi)容也會(huì)占據(jù)2/3,這肯定不合理拿撩,控件之間的間隙會(huì)特別大,一看就不符合設(shè)計(jì)效果如蚜,手機(jī)長(zhǎng)压恒,內(nèi)容占據(jù)低于2/3才正常,比如可能占據(jù)1/3.第四怖亭,占據(jù)資源大:好幾百KB涎显,甚至多達(dá)1M或跟多。 - dimen 基于dp:
這種適配依據(jù)的是最小寬度限定符兴猩。個(gè)人看來(lái)存在的問(wèn)題是:
第一期吓,Android 私人訂制的原因,寬度方面參差不齊倾芝,不可能適配所有的手機(jī)讨勤。
第二,sp和dp值有些值不全(這個(gè)應(yīng)該是可以解決的)晨另,姑且算是一個(gè)小問(wèn)題潭千。
第三,項(xiàng)目中增加了N個(gè)文件夾借尿,上拉下拉查看文件非常不方便:想看string或者color資源文件需要拉很多再能到達(dá)刨晴。
第四屉来,通過(guò)寬度限定符就近查找的原理,可以看出來(lái)匹配出來(lái)的大小不夠準(zhǔn)確狈癞。
5. 推薦今日頭條適配:
- 現(xiàn)狀:
UI設(shè)計(jì)圖是按屏幕寬度為360dp來(lái)設(shè)計(jì)的茄靠,那么在上述設(shè)備上,屏幕寬度其實(shí)為1080/(440/160)=392.7dp蝶桶,也就是屏幕是比設(shè)計(jì)圖要寬的慨绳。這種情況下, 即使使用dp也是無(wú)法在不同設(shè)備上顯示為同樣效果的真竖。 同時(shí)還存在部分設(shè)備屏幕寬度不足360dp脐雪,這時(shí)就會(huì)導(dǎo)致按360dp寬度來(lái)開(kāi)發(fā)實(shí)際顯示不全。加上16:9恢共、4:3甚至其他寬高比層出不窮战秋,寬高比不同,顯示完全一致就不可能了讨韭,不信你去看看pixel 2的density就明白了获询。 - 解決方案:
通常下,我們只需要以寬或高一個(gè)維度去適配拐袜,比如我們Feed是上下滑動(dòng)的,只需要保證在所有設(shè)備中寬的維度上顯示一致即可梢薪,再比如一個(gè)不支持上下滑動(dòng)的頁(yè)面蹬铺,那么需要保證在高這個(gè)維度上都顯示一致,尤其不能存在某些設(shè)備上顯示不全的情況秉撇。 - 實(shí)質(zhì):
① 支持以寬或者高一個(gè)維度去適配甜攀,保持該維度上和設(shè)計(jì)圖一致;
② 支持dp和sp單位琐馆,控制遷移成本到最小规阀。 - 具體代碼:
px = dp * density dp是360dp,想要px正好是屏幕寬度的話瘦麸,只能修改density谁撼。
/**
* 適配:修改設(shè)備密度
*/
private static float sNoncompatDensity;
private static float sNoncompatScaledDensity;
public static void setCustomDensity(@NonNull Activity activity, @NonNull final Application application) {
DisplayMetrics appDisplayMetrics = application.getResources().getDisplayMetrics();
if (sNoncompatDensity == 0) {
sNoncompatDensity = appDisplayMetrics.density;
sNoncompatScaledDensity = appDisplayMetrics.scaledDensity;
// 防止系統(tǒng)切換后不起作用
application.registerComponentCallbacks(new ComponentCallbacks() {
@Override
public void onConfigurationChanged(Configuration newConfig) {
if (newConfig != null && newConfig.fontScale > 0) {
sNoncompatScaledDensity = application.getResources().getDisplayMetrics().scaledDensity;
}
}
@Override
public void onLowMemory() {
}
});
}
float targetDensity = appDisplayMetrics.widthPixels / 360;
// 防止字體變小
float targetScaleDensity = targetDensity * (sNoncompatScaledDensity / sNoncompatDensity);
int targetDensityDpi = (int) (160 * targetDensity);
appDisplayMetrics.density = targetDensity;
appDisplayMetrics.scaledDensity = targetScaleDensity;
appDisplayMetrics.densityDpi = targetDensityDpi;
final DisplayMetrics activityDisplayMetrics = activity.getResources().getDisplayMetrics();
activityDisplayMetrics.density = targetDensity;
activityDisplayMetrics.scaledDensity = targetScaleDensity;
activityDisplayMetrics.densityDpi = targetDensityDpi;
}
DisplayUtil.setCustomDensity(this, getApplication());
說(shuō)明:今日頭條的這種適配,只需要在baseActivity中添加一句話即可滋饲。適配就是這么簡(jiǎn)單厉碟。
-
適配效果:
說(shuō)明:雖然圖片是今日頭條博客里面的,但是我也是親身測(cè)過(guò)后才推薦大家的屠缭,至于沒(méi)有發(fā)實(shí)測(cè)圖片箍鼓,公司項(xiàng)目多有不便,還請(qǐng)理解呵曹。
6. 后續(xù)
如果大家喜歡這篇文章款咖,歡迎點(diǎn)贊何暮;如果想看更多移動(dòng)端方面的技術(shù),歡迎關(guān)注铐殃!