《屏幕適配: Android屏幕適配簡單且十分輕量級的解決方案(今日頭條)》

問題

通常在寫布局的時候,我們用相對布局宴咧、權(quán)重比來解決因為屏幕尺寸大小不一帶來的控件擺放問題,而大控件的寬高度量使用dp毕源、文字大小使用sp它抱,通常情況下dp和sp是一樣的秕豫、例如10dp和10sp在屏幕上顯示效果一樣,只不過當系統(tǒng)修改文字大小時观蓄,使用sp標量的控件都跟隨系統(tǒng)發(fā)生變化混移。不論是dp還是sp,最終要在頁面上渲染出來前都會被轉(zhuǎn)成像素單位px侮穿。
1歌径、哪為什么一開始不使用像素作為控件的寬高的單位呢?
2亲茅、dp和px換算關(guān)系呢沮脖?
3金矛、同等dp在不同設(shè)備上能保證控件大小一致嗎?

解決這些疑問前先來復(fù)習(xí)一下Android屏幕的幾個概念勺届。

概念

屏幕分辨率:1920*1080標識高上有1920個像素點、寬度上有1080個像素點

1080x1920

屏幕尺寸 屏幕對角線的長度(單位inch)

屏幕尺寸

屏幕像素密度dpi 計算公式

計算公式

dip為160娶耍,則剛好 1dp = 1px免姿。

接下來就可以解決上訴提出的三個問題

1、dp和px換算關(guān)系
px = dp * (dip / 160)
安卓中定義了一個系數(shù)density
density = (dip / 160)
px = density * dp

2榕酒、假設(shè)屏幕大小相同胚膊,若屏幕分別率越高,那么在相同的區(qū)域內(nèi)就得放下更多的像素點想鹰、意味著屏幕密度越大紊婉,像素點就得越小,反之辑舷,像素點就越大喻犁,密度越小,像素點就越大何缓;所以同樣畫一段長度100px的線段在高分辨率下肢础,看上去就比在低分辨率屏幕下短,所以在寫布局時不會采取像素來度量控件寬高碌廓。

3传轰、最后一個問題 “同等dp在不同設(shè)備上能保證控件大小一致嗎?”
理想狀態(tài)狀態(tài)下是可以基本保持一致谷婆。但是安卓手機碎片化嚴重慨蛙,有很多奇怪尺寸出現(xiàn)。通過對比纪挎,可以明顯發(fā)現(xiàn)同樣的相同的dp華為設(shè)備上UI顯示比較粗大
由于屏幕尺寸期贫、分辨率和像素密度的關(guān)系,很多設(shè)備并沒有按此規(guī)則來實現(xiàn)廷区, 因此dpi的值非常亂


我現(xiàn)在手上有兩臺pad設(shè)備,屏幕分辨率1920*1200唯灵,一臺是華為C5pad,另一臺是三星pad隙轻。三星是9寸多埠帕,華為是8寸多。

image.png
image.png

今日頭條對此給出分析和解決方案玖绿,造成這樣問題是UI設(shè)計假設(shè)按寬度360dp這一尺寸設(shè)計的敛瓷,而有的設(shè)備實際上寬度比360dp還大,有的比360小斑匪。這種情況下呐籽, 即使使用dp也是無法在不同設(shè)備上顯示為同樣效果的。 同時還存在部分設(shè)備屏幕寬度不足360dp,這時就會導(dǎo)致按360dp寬度來開發(fā)實際顯示不全的情況狡蝶。

梳理需求
首先來梳理下我們的需求庶橱,一般我們設(shè)計圖都是以固定的尺寸來設(shè)計的。比如以分辨率1920px * 1080px來設(shè)計贪惹,以density為3來標注苏章,也就是屏幕其實是640dp * 360dp。如果我們想在所有設(shè)備上顯示完全一致奏瞬,其實是不現(xiàn)實的枫绅,因為屏幕高寬比不是固定的,16:9硼端、4:3甚至其他寬高比層出不窮并淋,寬高比不同,顯示完全一致就不可能了珍昨。但是通常下县耽,我們只需要以寬或高一個維度去適配,比如我們Feed是上下滑動的曼尊,只需要保證在所有設(shè)備中寬的維度上顯示一致即可酬诀,再比如一個不支持上下滑動的頁面,那么需要保證在高這個維度上都顯示一致骆撇,尤其不能存在某些設(shè)備上顯示不全的情況瞒御。同時考慮到現(xiàn)在基本都是以dp為單位去做的適配,如果新的方案不支持dp神郊,那么遷移成本也非常高肴裙。

因此,總結(jié)下大致需求如下:

支持以寬或者高一個維度去適配涌乳,保持該維度上和設(shè)計圖一致蜻懦;

支持dp和sp單位,控制遷移成本到最小夕晓。

解決突破口

從dp和px的轉(zhuǎn)換公式 :px = dp * density

可以看出宛乃,如果設(shè)計圖寬為360dp,想要保證在所有設(shè)備計算得出的px值都正好是屏幕寬度的話蒸辆,我們只能修改 density 的值征炼。

通過閱讀源碼,我們可以得知躬贡,density 是 DisplayMetrics 中的成員變量谆奥,而 DisplayMetrics 實例通過 Resources#getDisplayMetrics 可以獲得,而Resouces通過Activity或者Application的Context獲得拂玻。

先來熟悉下 DisplayMetrics 中和適配相關(guān)的幾個變量:

DisplayMetrics#density 就是上述的density

DisplayMetrics#densityDpi 就是上述的dpi

DisplayMetrics#scaledDensity 字體的縮放因子酸些,正常情況下和density相等宰译,但是調(diào)節(jié)系統(tǒng)字體大小后會改變這個值

下面給出代碼,只有一個方法魄懂,對dp進行修改沿侈。

//三星pad寬度1280dp (是dp,是不px) 市栗,SCREEN_WIDTH_DP根據(jù)不同的設(shè)計圖修改肋坚,手機一般是360
private final static int SCREEN_WIDTH_DP = 1280;
        private static float sNoncompatDensity;
    private static float sNoncompatScaleDensity;
    public static void setCusomDensity(final Activity activity,final Application application){
        final DisplayMetrics appDisplayMetrics = application.getResources().getDisplayMetrics();
        if (sNoncompatDensity == 0){
            sNoncompatDensity = appDisplayMetrics.density;
            sNoncompatScaleDensity = appDisplayMetrics.scaledDensity;
             //監(jiān)聽系統(tǒng)改變字體的大小
            application.registerComponentCallbacks(new ComponentCallbacks() {
                @Override
                public void onConfigurationChanged(Configuration newConfig) {
                    if (newConfig !=null &&newConfig.fontScale>0){
                        sNoncompatScaleDensity = application.getResources().getDisplayMetrics().scaledDensity;
                        Log.e("tag","sNoncompatScaleDensity:"+sNoncompatScaleDensity);
                    }
                }

                @Override
                public void onLowMemory() {

                }
            });
        }

        //根據(jù)參考的適配寬度 計算新的Density、ScaleDensity肃廓、DensityDpi
        float targetDensity = (float) appDisplayMetrics.widthPixels/SCREEN_WIDTH_DP;
        float targetScaleDensity = (float)targetDensity*(sNoncompatScaleDensity/sNoncompatDensity);
        int targetDensityDpi = (int) (160*targetDensity);
        
        //修改全局的
        appDisplayMetrics.density = targetDensity;
        appDisplayMetrics.scaledDensity = targetScaleDensity;
        appDisplayMetrics.densityDpi = targetDensityDpi;

        //修改當前activity
        final DisplayMetrics activityDisplayMetrics = activity.getResources().getDisplayMetrics();
        activityDisplayMetrics.density = targetDensity;
        activityDisplayMetrics.scaledDensity = targetScaleDensity;
        activityDisplayMetrics.densityDpi = targetDensityDpi;
    }

在要在UI布局渲染前調(diào)用即可

 @Override
    protected void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        ScreanAdapterUtils.setCusomDensity(this,MyAplication.getApplication());
        setContentView(R.layout.activity_main);
}

今日頭天適配方案https://mp.weixin.qq.com/s/d9QCoBP6kV9VSWvVldVVwA

問題

但是這個方法會導(dǎo)致一些比較大的對話框出現(xiàn)暫時問題,對話框不能全名展示诲泌。需要從新計算對話寬的寬度和高度盲赊,當然一些小對話的的展示不受影響。
直接給出代碼

public static void dialogAdapter(WindowManager windowManager, Dialog dialog,float heightScale,float widthScale){

        Point point = new Point();

        //獲得代表當前window屬性的對象
        Window window = dialog.getWindow();
        WindowManager.LayoutParams params = window.getAttributes();


        //獲取window的寬高信息
        Display display = windowManager.getDefaultDisplay();
        display.getSize(point);

        // 將設(shè)置后的大小賦值給window的寬高
        if (widthScale!=0){
            params.width = (int) (point.x * widthScale);
        }

        if (heightScale!=0){
            params.height = (int) (point.y * heightScale);
        }

        //設(shè)置屬性
        window.setAttributes(params);
    }

在UI渲染之后調(diào)用


image.png
最后編輯于
?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請聯(lián)系作者
  • 序言:七十年代末敷扫,一起剝皮案震驚了整個濱河市哀蘑,隨后出現(xiàn)的幾起案子,更是在濱河造成了極大的恐慌葵第,老刑警劉巖绘迁,帶你破解...
    沈念sama閱讀 216,744評論 6 502
  • 序言:濱河連續(xù)發(fā)生了三起死亡事件,死亡現(xiàn)場離奇詭異卒密,居然都是意外死亡缀台,警方通過查閱死者的電腦和手機,發(fā)現(xiàn)死者居然都...
    沈念sama閱讀 92,505評論 3 392
  • 文/潘曉璐 我一進店門哮奇,熙熙樓的掌柜王于貴愁眉苦臉地迎上來膛腐,“玉大人,你說我怎么就攤上這事鼎俘≌苌恚” “怎么了?”我有些...
    開封第一講書人閱讀 163,105評論 0 353
  • 文/不壞的土叔 我叫張陵贸伐,是天一觀的道長勘天。 經(jīng)常有香客問我,道長捉邢,這世上最難降的妖魔是什么脯丝? 我笑而不...
    開封第一講書人閱讀 58,242評論 1 292
  • 正文 為了忘掉前任,我火速辦了婚禮歌逢,結(jié)果婚禮上巾钉,老公的妹妹穿的比我還像新娘。我一直安慰自己秘案,他們只是感情好砰苍,可當我...
    茶點故事閱讀 67,269評論 6 389
  • 文/花漫 我一把揭開白布潦匈。 她就那樣靜靜地躺著,像睡著了一般赚导。 火紅的嫁衣襯著肌膚如雪茬缩。 梳的紋絲不亂的頭發(fā)上,一...
    開封第一講書人閱讀 51,215評論 1 299
  • 那天吼旧,我揣著相機與錄音凰锡,去河邊找鬼。 笑死圈暗,一個胖子當著我的面吹牛掂为,可吹牛的內(nèi)容都是我干的。 我是一名探鬼主播员串,決...
    沈念sama閱讀 40,096評論 3 418
  • 文/蒼蘭香墨 我猛地睜開眼勇哗,長吁一口氣:“原來是場噩夢啊……” “哼!你這毒婦竟也來了寸齐?” 一聲冷哼從身側(cè)響起欲诺,我...
    開封第一講書人閱讀 38,939評論 0 274
  • 序言:老撾萬榮一對情侶失蹤,失蹤者是張志新(化名)和其女友劉穎渺鹦,沒想到半個月后扰法,有當?shù)厝嗽跇淞掷锇l(fā)現(xiàn)了一具尸體,經(jīng)...
    沈念sama閱讀 45,354評論 1 311
  • 正文 獨居荒郊野嶺守林人離奇死亡毅厚,尸身上長有42處帶血的膿包…… 初始之章·張勛 以下內(nèi)容為張勛視角 年9月15日...
    茶點故事閱讀 37,573評論 2 333
  • 正文 我和宋清朗相戀三年塞颁,在試婚紗的時候發(fā)現(xiàn)自己被綠了。 大學(xué)時的朋友給我發(fā)了我未婚夫和他白月光在一起吃飯的照片卧斟。...
    茶點故事閱讀 39,745評論 1 348
  • 序言:一個原本活蹦亂跳的男人離奇死亡殴边,死狀恐怖,靈堂內(nèi)的尸體忽然破棺而出珍语,到底是詐尸還是另有隱情锤岸,我是刑警寧澤,帶...
    沈念sama閱讀 35,448評論 5 344
  • 正文 年R本政府宣布板乙,位于F島的核電站是偷,受9級特大地震影響,放射性物質(zhì)發(fā)生泄漏募逞。R本人自食惡果不足惜蛋铆,卻給世界環(huán)境...
    茶點故事閱讀 41,048評論 3 327
  • 文/蒙蒙 一、第九天 我趴在偏房一處隱蔽的房頂上張望放接。 院中可真熱鬧刺啦,春花似錦、人聲如沸纠脾。這莊子的主人今日做“春日...
    開封第一講書人閱讀 31,683評論 0 22
  • 文/蒼蘭香墨 我抬頭看了看天上的太陽。三九已至糊渊,卻和暖如春右核,著一層夾襖步出監(jiān)牢的瞬間,已是汗流浹背渺绒。 一陣腳步聲響...
    開封第一講書人閱讀 32,838評論 1 269
  • 我被黑心中介騙來泰國打工贺喝, 沒想到剛下飛機就差點兒被人妖公主榨干…… 1. 我叫王不留,地道東北人宗兼。 一個月前我還...
    沈念sama閱讀 47,776評論 2 369
  • 正文 我出身青樓躏鱼,卻偏偏與公主長得像,于是被迫代替她去往敵國和親殷绍。 傳聞我的和親對象是個殘疾皇子挠他,可洞房花燭夜當晚...
    茶點故事閱讀 44,652評論 2 354

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