看完不會(huì)Android屏幕適配我跪搓板

本篇文章已授權(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)系:


    屏幕尺寸桅滋、分辨率慧耍、像素密度三者關(guān)系.png
  • 舉例說(shuō)明:屏幕分辨率為:1920*1080,屏幕尺寸為5吋的話丐谋,那么dpi為440:


    example.png

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)單厉碟。

  • 適配效果:


    適配效果.png

    說(shuō)明:雖然圖片是今日頭條博客里面的,但是我也是親身測(cè)過(guò)后才推薦大家的屠缭,至于沒(méi)有發(fā)實(shí)測(cè)圖片箍鼓,公司項(xiàng)目多有不便,還請(qǐng)理解呵曹。

6. 后續(xù)

如果大家喜歡這篇文章款咖,歡迎點(diǎn)贊何暮;如果想看更多移動(dòng)端方面的技術(shù),歡迎關(guān)注铐殃!

最后編輯于
?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請(qǐng)聯(lián)系作者
  • 序言:七十年代末海洼,一起剝皮案震驚了整個(gè)濱河市,隨后出現(xiàn)的幾起案子背稼,更是在濱河造成了極大的恐慌贰军,老刑警劉巖,帶你破解...
    沈念sama閱讀 206,723評(píng)論 6 481
  • 序言:濱河連續(xù)發(fā)生了三起死亡事件蟹肘,死亡現(xiàn)場(chǎng)離奇詭異词疼,居然都是意外死亡,警方通過(guò)查閱死者的電腦和手機(jī)帘腹,發(fā)現(xiàn)死者居然都...
    沈念sama閱讀 88,485評(píng)論 2 382
  • 文/潘曉璐 我一進(jìn)店門(mén)贰盗,熙熙樓的掌柜王于貴愁眉苦臉地迎上來(lái),“玉大人阳欲,你說(shuō)我怎么就攤上這事舵盈。” “怎么了球化?”我有些...
    開(kāi)封第一講書(shū)人閱讀 152,998評(píng)論 0 344
  • 文/不壞的土叔 我叫張陵秽晚,是天一觀的道長(zhǎng)。 經(jīng)常有香客問(wèn)我筒愚,道長(zhǎng)赴蝇,這世上最難降的妖魔是什么? 我笑而不...
    開(kāi)封第一講書(shū)人閱讀 55,323評(píng)論 1 279
  • 正文 為了忘掉前任巢掺,我火速辦了婚禮句伶,結(jié)果婚禮上,老公的妹妹穿的比我還像新娘陆淀。我一直安慰自己考余,他們只是感情好,可當(dāng)我...
    茶點(diǎn)故事閱讀 64,355評(píng)論 5 374
  • 文/花漫 我一把揭開(kāi)白布轧苫。 她就那樣靜靜地躺著楚堤,像睡著了一般。 火紅的嫁衣襯著肌膚如雪含懊。 梳的紋絲不亂的頭發(fā)上钾军,一...
    開(kāi)封第一講書(shū)人閱讀 49,079評(píng)論 1 285
  • 那天,我揣著相機(jī)與錄音绢要,去河邊找鬼吏恭。 笑死,一個(gè)胖子當(dāng)著我的面吹牛重罪,可吹牛的內(nèi)容都是我干的樱哼。 我是一名探鬼主播哀九,決...
    沈念sama閱讀 38,389評(píng)論 3 400
  • 文/蒼蘭香墨 我猛地睜開(kāi)眼,長(zhǎng)吁一口氣:“原來(lái)是場(chǎng)噩夢(mèng)啊……” “哼搅幅!你這毒婦竟也來(lái)了阅束?” 一聲冷哼從身側(cè)響起,我...
    開(kāi)封第一講書(shū)人閱讀 37,019評(píng)論 0 259
  • 序言:老撾萬(wàn)榮一對(duì)情侶失蹤茄唐,失蹤者是張志新(化名)和其女友劉穎息裸,沒(méi)想到半個(gè)月后,有當(dāng)?shù)厝嗽跇?shù)林里發(fā)現(xiàn)了一具尸體沪编,經(jīng)...
    沈念sama閱讀 43,519評(píng)論 1 300
  • 正文 獨(dú)居荒郊野嶺守林人離奇死亡呼盆,尸身上長(zhǎng)有42處帶血的膿包…… 初始之章·張勛 以下內(nèi)容為張勛視角 年9月15日...
    茶點(diǎn)故事閱讀 35,971評(píng)論 2 325
  • 正文 我和宋清朗相戀三年,在試婚紗的時(shí)候發(fā)現(xiàn)自己被綠了蚁廓。 大學(xué)時(shí)的朋友給我發(fā)了我未婚夫和他白月光在一起吃飯的照片访圃。...
    茶點(diǎn)故事閱讀 38,100評(píng)論 1 333
  • 序言:一個(gè)原本活蹦亂跳的男人離奇死亡,死狀恐怖相嵌,靈堂內(nèi)的尸體忽然破棺而出腿时,到底是詐尸還是另有隱情,我是刑警寧澤饭宾,帶...
    沈念sama閱讀 33,738評(píng)論 4 324
  • 正文 年R本政府宣布批糟,位于F島的核電站,受9級(jí)特大地震影響看铆,放射性物質(zhì)發(fā)生泄漏跃赚。R本人自食惡果不足惜,卻給世界環(huán)境...
    茶點(diǎn)故事閱讀 39,293評(píng)論 3 307
  • 文/蒙蒙 一性湿、第九天 我趴在偏房一處隱蔽的房頂上張望。 院中可真熱鬧满败,春花似錦肤频、人聲如沸。這莊子的主人今日做“春日...
    開(kāi)封第一講書(shū)人閱讀 30,289評(píng)論 0 19
  • 文/蒼蘭香墨 我抬頭看了看天上的太陽(yáng)。三九已至净嘀,卻和暖如春报咳,著一層夾襖步出監(jiān)牢的瞬間,已是汗流浹背挖藏。 一陣腳步聲響...
    開(kāi)封第一講書(shū)人閱讀 31,517評(píng)論 1 262
  • 我被黑心中介騙來(lái)泰國(guó)打工暑刃, 沒(méi)想到剛下飛機(jī)就差點(diǎn)兒被人妖公主榨干…… 1. 我叫王不留,地道東北人膜眠。 一個(gè)月前我還...
    沈念sama閱讀 45,547評(píng)論 2 354
  • 正文 我出身青樓岩臣,卻偏偏與公主長(zhǎng)得像溜嗜,于是被迫代替她去往敵國(guó)和親。 傳聞我的和親對(duì)象是個(gè)殘疾皇子架谎,可洞房花燭夜當(dāng)晚...
    茶點(diǎn)故事閱讀 42,834評(píng)論 2 345

推薦閱讀更多精彩內(nèi)容