Android開發(fā)一步到位屏幕適配解決方案

看簡書上和CSDN都沒有一篇特別有針對性的Android屏幕適配解決方案澎灸,大部分都是基于官方的基本的唾糯,或者挪來挪去的爽撒,最近自己剛好處理一個老項目需要適配大小屏,這里就貼一個自己的屏幕適配解決方案腊徙,有問題大家一起溝通。

大家都知道Android屏幕適配是件非常頭疼的事檬某,目前全世界安卓設(shè)備的大大小小分辨率撬腾,大大小小的尺寸,最終形成的設(shè)備屏幕大小種類不計其數(shù)恢恼,但就這一項就給開發(fā)者造成了不少困難民傻,總是照顧住這種屏幕照顧不了那種屏幕。

先來放一種很熟悉的圖:

image

針對Android屏幕適配场斑,除了我們按照“靈活運用布局”漓踢、“尺寸限定符”、“布局相關(guān)屬性”漏隐、“.9圖”喧半、“屏幕密度適配”等官方標(biāo)準(zhǔn),但是卻發(fā)現(xiàn)還遠(yuǎn)遠(yuǎn)不夠青责,要么添加很多圖片(從加載性能說似乎是必要的)使得apk變得很大挺据,要么有多套布局工作量變大難以維護。針對屏幕適配脖隶,google官方也是系統(tǒng)默認(rèn)支持的是采用屏幕像素密度來進(jìn)行匹配相關(guān)資源和長度的扁耐,這一塊不做過多的闡述了,建議參看官方文檔产阱。

這里我提供一個最牛婉称、最簡單、一步到位的方案心墅,github地址:https://github.com/toperc/ScreenAdaptation

大概其優(yōu)點如下:

  • 只需要提供一套最優(yōu)圖片酿矢,用于減小apk包大小榨乎。當(dāng)然為了加載性能和圖片更精細(xì)化顯示采用多套也可以怎燥。
  • 無論大小屏如果布局確定只需要一套布局,當(dāng)然如果橫豎屏變換不同的屏幕展示蜜暑,多套也是必要的铐姚。
  • 沒有重疊的現(xiàn)象發(fā)生,按照默認(rèn)布局方法可能在大屏上適配很好肛捍,到小屏上卻出現(xiàn)重疊的現(xiàn)象隐绵,很常見,但這里我不允許他有拙毫!
  • 無論大小屏整體看過去布局幾乎相同依许,因為是按比例來的。
  • 在大屏上展示不會隨著完全比例放大而顯得傻缀蹄、大峭跳、憨膘婶。

其原理也很簡單,就是根據(jù)基準(zhǔn)屏幕像素密度來進(jìn)行適當(dāng)縮放后得到相對屏幕像素蛀醉,然后給系統(tǒng)的像素密度重新賦值悬襟。

準(zhǔn)備工作:
確定一套基準(zhǔn)屏幕參數(shù),然后布局時根據(jù)這套參數(shù)布局拯刁,單位仍然采用dp脊岳,并盡可能確定其布局控件的長度。

        //繪制頁面時參照的設(shè)計圖尺寸
        final float DESIGN_WIDTH = 1080f;
        final float DESIGN_HEIGHT = 1920f;
        final float DESTGN_INCH = 5.0f;

三步走:

  1. 確定放大縮小比例
  2. 確定參考屏幕密度
  3. 確定相對屏幕密度并重新賦值給系統(tǒng)的像素密度

主要方法:

    /**
     * 重置屏幕密度
     */
    public static void resetDensity(Context context) {
        //繪制頁面時參照的設(shè)計圖尺寸
        final float DESIGN_WIDTH = 800f;
        final float DESIGN_HEIGHT = 1280f;
        final float DESTGN_INCH = 5.0f;
        //大屏調(diào)節(jié)因子垛玻,范圍0~1割捅,因屏幕同比例放大視圖顯示非常傻大憨,用于調(diào)節(jié)感官度
        final float BIG_SCREEN_FACTOR = 0.8f;

        DisplayMetrics dm = context.getResources().getDisplayMetrics();

        //確定放大縮小比率
        float rate = Math.min(dm.widthPixels, dm.heightPixels) / Math.min(DESIGN_WIDTH, DESIGN_HEIGHT);
        //確定參照屏幕密度比率
        float referenceDensity = (float) Math.sqrt(DESIGN_WIDTH * DESIGN_WIDTH + DESIGN_HEIGHT * DESIGN_HEIGHT) / DESTGN_INCH / DisplayMetrics.DENSITY_DEFAULT;
        //確定最終屏幕密度比率
        float relativeDensity = referenceDensity * rate;

        if (ORIGINAL_DENSITY == -1) {
            ORIGINAL_DENSITY = dm.density;
        }
        if (relativeDensity > ORIGINAL_DENSITY) {
            relativeDensity = ORIGINAL_DENSITY + (relativeDensity - ORIGINAL_DENSITY) * BIG_SCREEN_FACTOR;
        }
        dm.density = relativeDensity;
        dm.densityDpi = (int) (relativeDensity * DisplayMetrics.DENSITY_DEFAULT);
        dm.scaledDensity = relativeDensity;
    }

上邊放大縮小比例是根據(jù)屏幕的長寬和參考屏幕的長寬對應(yīng)設(shè)定的帚桩,這樣計算出來的屏幕密度是固定的棺牧。
注意最終賦值的三個成員變量的含義:

  • dm.density
    屏幕密度比率,不同設(shè)備的屏幕視圖的長寬大小都是根據(jù)屏幕密度比率與屏幕密度基準(zhǔn)值(160dp)的乘積朗儒。此成員變量控制擁有固定長寬值視圖的縮放颊乘。
  • dm.densityDpi
    實際屏幕密度比率,但是單獨設(shè)定此值并不一定起到縮放效果醉锄,需要配合density一起設(shè)定乏悄,此成員變量控制長寬自動wrapConent形式的縮放,因為wrapContent形式下系統(tǒng)自動為其分配默認(rèn)的屏幕密度恳不,如果不對其重新賦值檩小,則不能根據(jù)規(guī)則進(jìn)行縮放。
  • dm.scaledDensity
    獨立像素密度烟勋,主要處理字體大小縮放规求,目前比較流行的字體單位為dp,第一能隨著應(yīng)用適應(yīng)不同的設(shè)備縮放卵惦,第二避免了跟隨系統(tǒng)縮放使得布局展示錯亂阻肿。但是某些系統(tǒng)機開發(fā)者不能控制的例如toast字體大小等默認(rèn)使用的單位仍然是sp,所以此成員變量也需要進(jìn)行重新賦值。系統(tǒng)默認(rèn)應(yīng)用sp和dp二者的比率為1沮尿,但是某些情況下又想根據(jù)系統(tǒng)縮放丛塌,還要保持整體縮放比率,這是就要系統(tǒng)縮放和應(yīng)用縮放結(jié)合著得出一個比率來最終賦值了畜疾。

配置地方:
DisplayMetrics相關(guān)參數(shù)的值有兩種赴邻,一種是默認(rèn)的,一種是動態(tài)調(diào)整后的啡捶。
獲取默認(rèn)的:

        DisplayMetrics display = new DisplayMetrics();
        getWindowManager().getDefaultDisplay().getMetrics(display);

獲取動態(tài)調(diào)整后的:

        DisplayMetrics dm = context.getResources().getDisplayMetrics();

應(yīng)用展示最終使用的DisplayMetrics相關(guān)參數(shù)就是調(diào)整后的姥敛。android8.0之前,整個應(yīng)用長寬縮放比率均是采用一套瞎暑,無論時Activity和Application兩者任意其一配置均可彤敛,但是在Android8.0的時候忿偷,系統(tǒng)架構(gòu)調(diào)整,由原來的統(tǒng)一現(xiàn)在重點分配到每個Activity中和Application中臊泌,為了兼顧全局鲤桥,兩者都要設(shè)置,尤其是Activity渠概。Activity中設(shè)置的時候要注意一定要設(shè)置在setContentView()之前茶凳,Application設(shè)置在onCreat()即可。
還應(yīng)注意一點播揪,當(dāng)屏幕旋轉(zhuǎn)時有時我們?yōu)榱吮4嫦嚓P(guān)狀態(tài)贮喧,所以不想讓Activity的onCreat()方法重走一遍,但是屏幕旋轉(zhuǎn)會使得屏幕密度參數(shù)進(jìn)行重置猪狈,不得已我們還要在屏幕旋轉(zhuǎn)的時候在重新設(shè)置一下屏幕密度箱沦。
源碼中屏幕旋轉(zhuǎn)所作的工作:

    @Override
    public void onConfigurationChanged(Configuration newConfig) {
        super.onConfigurationChanged(newConfig);
        getDelegate().onConfigurationChanged(newConfig);
        if (mResources != null) {
            // The real (and thus managed) resources object was already updated
            // by ResourcesManager, so pull the current metrics from there.
            final DisplayMetrics newMetrics = super.getResources().getDisplayMetrics();
            mResources.updateConfiguration(newConfig, newMetrics);
        }
    }

為了使這個應(yīng)用產(chǎn)生效果,建議將Activity形式的配置在BaseActivity中雇庙。
Activity中:

    @Override
    protected void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        //一定要寫在setContentView之前
        ScreenUtil.resetDensity(this);
        setContentView(R.layout.activity_main);
    }

    @Override
    public void onConfigurationChanged(Configuration newConfig) {
        super.onConfigurationChanged(newConfig);
        //屏幕旋轉(zhuǎn)時會使一些參數(shù)初始化谓形,所以也需要在此重置一下
        ScreenUtil.resetDensity(this);
    }

Application中:

    @Override
    public void onCreate() {
        super.onCreate();
        ScreenUtil.resetDensity(this);
    }

這些配置完畢后,一旦app啟動優(yōu)先處理這件事疆前,其他均按照默認(rèn)處理寒跳,就是這么簡單。

使用和不使用截圖直觀感受
采用默認(rèn)布局方式截圖對比:

image

采用此方案截圖對比:
image

轉(zhuǎn)載請注明出處竹椒,我是toperc.

最后編輯于
?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請聯(lián)系作者
  • 序言:七十年代末童太,一起剝皮案震驚了整個濱河市,隨后出現(xiàn)的幾起案子胸完,更是在濱河造成了極大的恐慌书释,老刑警劉巖,帶你破解...
    沈念sama閱讀 216,402評論 6 499
  • 序言:濱河連續(xù)發(fā)生了三起死亡事件赊窥,死亡現(xiàn)場離奇詭異爆惧,居然都是意外死亡,警方通過查閱死者的電腦和手機誓琼,發(fā)現(xiàn)死者居然都...
    沈念sama閱讀 92,377評論 3 392
  • 文/潘曉璐 我一進(jìn)店門检激,熙熙樓的掌柜王于貴愁眉苦臉地迎上來肴捉,“玉大人腹侣,你說我怎么就攤上這事〕菟耄” “怎么了傲隶?”我有些...
    開封第一講書人閱讀 162,483評論 0 353
  • 文/不壞的土叔 我叫張陵,是天一觀的道長窃页。 經(jīng)常有香客問我跺株,道長复濒,這世上最難降的妖魔是什么? 我笑而不...
    開封第一講書人閱讀 58,165評論 1 292
  • 正文 為了忘掉前任乒省,我火速辦了婚禮巧颈,結(jié)果婚禮上,老公的妹妹穿的比我還像新娘袖扛。我一直安慰自己砸泛,他們只是感情好,可當(dāng)我...
    茶點故事閱讀 67,176評論 6 388
  • 文/花漫 我一把揭開白布蛆封。 她就那樣靜靜地躺著唇礁,像睡著了一般。 火紅的嫁衣襯著肌膚如雪惨篱。 梳的紋絲不亂的頭發(fā)上盏筐,一...
    開封第一講書人閱讀 51,146評論 1 297
  • 那天,我揣著相機與錄音砸讳,去河邊找鬼琢融。 笑死,一個胖子當(dāng)著我的面吹牛簿寂,可吹牛的內(nèi)容都是我干的吏奸。 我是一名探鬼主播,決...
    沈念sama閱讀 40,032評論 3 417
  • 文/蒼蘭香墨 我猛地睜開眼陶耍,長吁一口氣:“原來是場噩夢啊……” “哼奋蔚!你這毒婦竟也來了?” 一聲冷哼從身側(cè)響起烈钞,我...
    開封第一講書人閱讀 38,896評論 0 274
  • 序言:老撾萬榮一對情侶失蹤泊碑,失蹤者是張志新(化名)和其女友劉穎,沒想到半個月后毯欣,有當(dāng)?shù)厝嗽跇淞掷锇l(fā)現(xiàn)了一具尸體馒过,經(jīng)...
    沈念sama閱讀 45,311評論 1 310
  • 正文 獨居荒郊野嶺守林人離奇死亡,尸身上長有42處帶血的膿包…… 初始之章·張勛 以下內(nèi)容為張勛視角 年9月15日...
    茶點故事閱讀 37,536評論 2 332
  • 正文 我和宋清朗相戀三年酗钞,在試婚紗的時候發(fā)現(xiàn)自己被綠了腹忽。 大學(xué)時的朋友給我發(fā)了我未婚夫和他白月光在一起吃飯的照片。...
    茶點故事閱讀 39,696評論 1 348
  • 序言:一個原本活蹦亂跳的男人離奇死亡砚作,死狀恐怖窘奏,靈堂內(nèi)的尸體忽然破棺而出,到底是詐尸還是另有隱情葫录,我是刑警寧澤着裹,帶...
    沈念sama閱讀 35,413評論 5 343
  • 正文 年R本政府宣布,位于F島的核電站米同,受9級特大地震影響骇扇,放射性物質(zhì)發(fā)生泄漏摔竿。R本人自食惡果不足惜,卻給世界環(huán)境...
    茶點故事閱讀 41,008評論 3 325
  • 文/蒙蒙 一少孝、第九天 我趴在偏房一處隱蔽的房頂上張望继低。 院中可真熱鬧,春花似錦稍走、人聲如沸郁季。這莊子的主人今日做“春日...
    開封第一講書人閱讀 31,659評論 0 22
  • 文/蒼蘭香墨 我抬頭看了看天上的太陽梦裂。三九已至,卻和暖如春盖淡,著一層夾襖步出監(jiān)牢的瞬間年柠,已是汗流浹背。 一陣腳步聲響...
    開封第一講書人閱讀 32,815評論 1 269
  • 我被黑心中介騙來泰國打工褪迟, 沒想到剛下飛機就差點兒被人妖公主榨干…… 1. 我叫王不留冗恨,地道東北人。 一個月前我還...
    沈念sama閱讀 47,698評論 2 368
  • 正文 我出身青樓味赃,卻偏偏與公主長得像掀抹,于是被迫代替她去往敵國和親。 傳聞我的和親對象是個殘疾皇子心俗,可洞房花燭夜當(dāng)晚...
    茶點故事閱讀 44,592評論 2 353

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