需要的基本知識(shí):
px:
一個(gè)像素點(diǎn)
dpi:
用于印刷行業(yè),表示一英寸內(nèi)的墨點(diǎn)數(shù)
ppi:
表示一英寸內(nèi)的像素點(diǎn)舔腾,對(duì)于Android,dpi=ppi
dp:
1dp在不同大小溪胶,分辨率的屏幕上是同長的(前提是系統(tǒng)要把px,dp比例算對(duì)稳诚,下面的方案二哗脖,正是使用了非標(biāo)準(zhǔn)px,dp比例)
三者轉(zhuǎn)換關(guān)系:
px = dp * (dpi / 160)
舉個(gè)例子:
若一個(gè)屏幕的分辨率為640*480,大小為5寸扳还,那這個(gè)屏幕的ppi(dpi)為160才避,那么根據(jù)以上公式,在這個(gè)屏幕上正好1dp=1px
若一個(gè)屏幕的分辨率為1280*960,大小為5寸氨距,那這個(gè)屏幕的ppi(dpi)為320桑逝,那么根據(jù)以上公式,在這個(gè)屏幕上1dp=2px
對(duì)于一個(gè)640dp*360dp的設(shè)計(jì)圖俏让。我們有各種各樣dp屏幕的適配楞遏,
如果對(duì)view設(shè)定固定的dp茬暇,會(huì)導(dǎo)致這個(gè)view在各個(gè)屏幕的大小一樣,而不是占據(jù)屏幕的比例一樣寡喝,這顯然與設(shè)計(jì)圖不符
屏幕適配方案一:
針對(duì)不同大小的屏幕設(shè)置不同的dp而钞,
通過Android限定符來實(shí)現(xiàn),創(chuàng)建多個(gè)dimen文件拘荡,分別放在xh臼节,xxh這些文件夾下,
這個(gè)方案不能完美適配珊皿,因?yàn)檫@些限定符無法指代所有Android手機(jī)网缝,有很大局限性,不推薦蟋定。
屏幕適配方案二(字節(jié)跳動(dòng)的方案粉臊,推薦):
更改1dp所指代的px數(shù),使得不同大小屏幕的寬度的dp數(shù)驶兜,總是和設(shè)計(jì)圖寬度的dp數(shù)一致扼仲,
比如說,設(shè)計(jì)師給到我們的設(shè)計(jì)圖寬度為360dp抄淑,
實(shí)際上因?yàn)槭謾C(jī)屏幕大小不一屠凶,手機(jī)屏幕寬度可能有360dp,420dp肆资,480dp的矗愧,
此時(shí)一張?jiān)O(shè)計(jì)圖去適配這些屏幕,就會(huì)出現(xiàn)效果差異郑原,
而方案二就是通過改變手機(jī)的dp/px比唉韭,來實(shí)現(xiàn)各個(gè)手機(jī)寬度都是360dp,
這樣一個(gè)View展示在不同的屏幕上時(shí)犯犁,其和ui的差距只是等比變大或者縮小了属愤,
但是這個(gè)View的寬度和屏幕寬度比,不會(huì)因?yàn)槠聊淮笮〔町惏l(fā)生變化酸役,
顯然這是可接受的住诸,下面我們來看下怎么實(shí)現(xiàn)吧。
Android中簇捍,DisplayMetrics.density 這個(gè)字段表示1dp對(duì)應(yīng)的px數(shù)
app中所有dp轉(zhuǎn)px都是通過這個(gè)字段來計(jì)算的只壳,我們可以設(shè)置這個(gè)字段,以我們?cè)O(shè)計(jì)圖寬度為360dp為例
在setContentView()方法前調(diào)用:
private static void setCustomDensity(@NonNull Activity activity, @NonNull Application application){
final DisplayMetrics appDisplayMetrics=application.getResources().getDisplayMetrics();
//360表示這個(gè)項(xiàng)目設(shè)計(jì)圖的寬度為360dp
final float targetDensity=appDisplayMetrics.widthPixels/360;//每dp等于targetDensity px
final int targetDensityDpi=(int)(160*targetDensity);//重新計(jì)算設(shè)備的dpi
appDisplayMetrics.density=appDisplayMetrics.scaledDensity=targetDensity;
appDisplayMetrics.densityDpi=targetDensityDpi;
final DisplayMetrics activityDisplayMetrics=activity.getResources().getDisplayMetrics();
activityDisplayMetrics.density=activityDisplayMetrics.scaledDensity=targetDensity;
activityDisplayMetrics.densityDpi=targetDensityDpi;
}
驗(yàn)證:
新建一個(gè)activity暑塑,使用以下布局
<LinearLayout xmlns:android="[http://schemas.android.com/apk/res/android](http://schemas.android.com/apk/res/android)"
android:layout_width="match_parent"
android:layout_height="match_parent"
android:orientation="vertical">
<TextView
android:layout_width="180dp"
android:layout_height="match_parent"
android:text="我的寬度正好是屏幕一半"
android:textSize="50dp"
android:textColor="#ff0000"
android:background="#000000"/>
</LinearLayout>
運(yùn)行在不同手機(jī)上吼句,可以看到,即使手機(jī)屏幕的大小不同事格,TextView寬度總是屏幕寬度的一半
上述代碼存在缺陷惕艳,因?yàn)橛脩艨赡芡ㄟ^系統(tǒng)設(shè)置設(shè)置了默認(rèn)字體大小搞隐,
這會(huì)改變 DisplayMetrics.scaledDensity的值,這個(gè)值和DisplayMetrics.density的比例應(yīng)當(dāng)保持不變
所以正確的計(jì)算方法為:
final float targetScaledDensity=targetDensity*(appDisplayMetrics.scaledDensity/appDisplayMetrics.density);
同時(shí)我們還要監(jiān)聽字體改變事件远搪,在字體改變時(shí)重新計(jì)算這個(gè)值劣纲,最終代碼參考:
private static float sNoncompatDensity=0;
private static float sNoncompatScaldeDensity=0;
private static void setCustomDensity(@NonNull Activity activity, @NonNull final Application application){
final DisplayMetrics appDisplayMetrics=application.getResources().getDisplayMetrics();
if (sNoncompatDensity==0){
sNoncompatDensity=appDisplayMetrics.density;
sNoncompatScaldeDensity=appDisplayMetrics.scaledDensity;
application.registerComponentCallbacks(new ComponentCallbacks() {
@Override
public void onConfigurationChanged(Configuration newConfig) {
if (newConfig!=null&&newConfig.fontScale>0){
sNoncompatScaldeDensity=application.getResources().getDisplayMetrics().scaledDensity;
}
}
@Override
public void onLowMemory() {
}
});
}
//360表示這個(gè)項(xiàng)目設(shè)計(jì)圖的寬度為360dp
final float targetDensity=appDisplayMetrics.widthPixels/360;//每dp等于targetDensity px
final float targetScaledDensity=(sNoncompatScaldeDensity/sNoncompatDensity)*targetDensity;
final int targetDensityDpi=(int)(160*targetDensity);//重新計(jì)算設(shè)備的dpi
appDisplayMetrics.density=targetDensity;
appDisplayMetrics.scaledDensity=targetScaledDensity;
appDisplayMetrics.densityDpi=targetDensityDpi;
final DisplayMetrics activityDisplayMetrics=activity.getResources().getDisplayMetrics();
activityDisplayMetrics.density=targetDensity;
activityDisplayMetrics.scaledDensity=targetScaledDensity;
activityDisplayMetrics.densityDpi=targetDensityDpi;
}
參考:
https://99designs.com/blog/tips/ppi-vs-dpi-whats-the-difference/
https://www.csdn.net/gather_20/MtTakg4sNzIxNS1ibG9n.html
https://mp.weixin.qq.com/s/d9QCoBP6kV9VSWvVldVVwA?tdsourcetag=s_pctim_aiomsg
end
如果你覺得這篇文章對(duì)你有所幫助,不妨點(diǎn)一個(gè)贊谁鳍,作者會(huì)非常高興的癞季。