android開發中機型適配問題解析終極篇

android開發中機型適配問題解析終極篇,Android的屏幕適配一直以來都在折磨著我們Android開發者,本文給你帶來一種全新、全面而邏輯清晰的Android屏幕適配思路,隻要你認真閱讀,保證你能解決Android的屏幕適配問題!


目錄


Android屏幕適配解決方案.png


定義

使得某一元素在Android不同尺寸、不同分辨率的手機上具備相同的顯示效果


相關重要概念

屏幕尺寸

含義:手機對角線的物理尺寸單位:英寸(inch),1英寸=2.54cm

Android手機常見的尺寸有5寸、5.5寸、6寸等等

屏幕分辨率

含義:手機在橫向、縱向上的像素點數總和

一般描述成屏幕的”寬x高”=AxB含義:屏幕在橫向方向(寬度)上有A個像素點,在縱向方向
(高)有B個像素點例子:1080×1920,即寬度方向上有1080個像素點,在高度方向上有1920個像素點

單位:px(pixel),1px=1像素點

UI設計師的設計圖會以px作為統一的計量單位

Android手機常見的分辨率:320×480、480×800、720×1280、1080×1920

屏幕像素密度

含義:每英寸的像素點數單位:dpi(dots per ich)

假設設備內每英寸有160個像素,那麼該設備的屏幕像素密度=160dpi

安卓手機對於每類手機屏幕大小都有一個相應的屏幕像素密度:

密度類型 代表的分辨率(px) 屏幕像素密度(dpi)
低密度(ldpi) 240×320 120
中密度(mdpi) 320×480 160
高密度(hdpi) 480×800 240
超高密度(xhdpi) 720×1280 320
超超高密度(xxhdpi) 1080×1920 480

屏幕尺寸、分辨率、像素密度三者關系

一部手機的分辨率是寬x高,屏幕大小是以寸為單位,那麼三者的關系是:


三者關系示意圖

數學不太差的人應該能懂…..吧?

不懂沒關系,在這裡舉個例子
假設一部手機的分辨率是1080×1920(px),屏幕大小是5寸,問密度是多少?
解:請直接套公式


解答過程

密度無關像素

含義:density-independent pixel,叫dp或dip,與終端上的實際物理像素點無關。

單位:dp,可以保證在不同屏幕像素密度的設備上顯示相同的效果

Android開發時用dp而不是px單位設置圖片大小,是Android特有的單位場景:假如同樣都是畫一條長度是屏幕一半的線,如果使用px作為計量單位,那麼在480×800分辨率手機上設置應為240px;在320×480的手機上應設置為160px,二者設置就不同瞭;如果使用dp為單位,在這兩種分辨率下,160dp都顯示為屏幕一半的長度。

dp與px的轉換
因為ui設計師給你的設計圖是以px為單位的,Android開發則是使用dp作為單位的,那麼我們需要進行轉換:

密度類型 代表的分辨率(px) 屏幕密度(dpi) 換算(px/dp) 比例
低密度(ldpi) 240×320 120 1dp=0.75px 3
中密度(mdpi) 320×480 160 1dp=1px 4
高密度(hdpi) 480×800 240 1dp=1.5px 6
超高密度(xhdpi) 720×1280 320 1dp=2px 8
超超高密度(xxhdpi) 1080×1920 480 1dp=3px 12

在Android中,規定以160dpi(即屏幕分辨率為320×480)為基準:1dp=1px

獨立比例像素

含義:scale-independent pixel,叫sp或sip單位:sp

Android開發時用此單位設置文字大小,可根據字體大小首選項進行縮放推薦使用12sp、14sp、18sp、22sp作為字體設置的大小,不推薦使用奇數和小數,容易造成精度的丟失問題;小於12sp的字體會太小導致用戶看不清

請把上面的概念記住,因為下面講解都會用到!


為什麼要進行Android屏幕適配

由於Android系統的開放性,任何用戶、開發者、OEM廠商、運營商都可以對Android進行定制,於是導致:

Android系統碎片化:小米定制的MIUI、魅族定制的flyme、華為定制的EMUI等等

當然都是基於Google原生系統定制的

Android機型屏幕尺寸碎片化:5寸、5.5寸、6寸等等Android屏幕分辨率碎片化:320×480、480×800、720×1280、1080×1920

據友盟指數顯示,統計至2015年12月,支持Android的設備共有27796種

當Android系統、屏幕尺寸、屏幕密度出現碎片化的時候,就很容易出現同一元素在不同手機上顯示不同的問題。

試想一下這麼一個場景:
為4.3寸屏幕準備的UI設計圖,運行在5.0寸的屏幕上,很可能在右側和下側存在大量的空白;而5.0寸的UI設計圖運行到4.3寸的設備上,很可能顯示不下。

為瞭保證用戶獲得一致的用戶體驗效果:

使得某一元素在Android不同尺寸、不同分辨率的手機上具備相同的顯示效果

於是,我們便需要對Android屏幕進行適配。


屏幕適配問題的本質

使得“佈局”、“佈局組件”、“圖片資源”、“用戶界面流程”匹配不同的屏幕尺寸

使得佈局、佈局組件自適應屏幕尺寸;
根據屏幕的配置來加載相應的UI佈局、用戶界面流程

使得“圖片資源”匹配不同的屏幕密度


解決方案

問題:如何進行屏幕尺寸匹配?答:
屏幕尺寸適配解決方案.png


“佈局”匹配

本質1:使得佈局元素自適應屏幕尺寸

做法
使用相對佈局(RelativeLayout),禁用絕對佈局(AbsoluteLayout)

開發中,我們使用的佈局一般有:

線性佈局(Linearlayout)相對佈局(RelativeLayout)幀佈局(FrameLayout)絕對佈局(AbsoluteLayout)

由於絕對佈局(AbsoluteLayout)適配性極差,所以極少使用。

對於線性佈局(Linearlayout)、相對佈局(RelativeLayout)和幀佈局(FrameLayout)需要根據需求進行選擇,但要記住:

RelativeLayout
佈局的子控件之間使用相對位置的方式排列,因為RelativeLayout講究的是相對位置,即使屏幕的大小改變,視圖之前的相對位置都不會變化,與屏幕大小無關,靈活性很強LinearLayout
通過多層嵌套LinearLayout和組合使
用”wrap_content”和”match_parent”已經可以構建出足夠復雜的佈局。但是LinearLayout無法準確地控制子視圖之間的位置關系,隻能簡單的一個挨著一個地排列

所以,對於屏幕適配來說,使用相對佈局(RelativeLayout)將會是更好的解決方案

本質2:根據屏幕的配置來加載相應的UI佈局

應用場景:需要為不同屏幕尺寸的設備設計不同的佈局

做法:使用限定符作用:通過配置限定符使得程序在運行時根據當前設備的配置(屏幕尺寸)自動加載合適的佈局資源 限定符類型: 尺寸(size)限定符最小寬度(Smallest-width)限定符佈局別名屏幕方向(Orientation)限定符

 

尺寸(size)限定符

使用場景:當一款應用顯示的內容較多,希望進行以下設置:

在平板電腦和電視的屏幕(>7英寸)上:實施“雙面板”模式以同時顯示更多內容在手機較小的屏幕上:使用單面板分別顯示內容

因此,我們可以使用尺寸限定符(layout-large)通過創建一個文件

res/layout-large/main.xml

來完成上述設定:

讓系統在屏幕尺寸>7英寸時采用適配平板的雙面板佈局反之(默認情況下)采用適配手機的單面板佈局

文件配置如下:

適配手機的單面板(默認)佈局:res/layout/main.xml



  

適配尺寸>7寸平板的雙面板佈局::res/layout-large/main.xml


    
    

請註意:

兩個佈局名稱均為main.xml,隻有佈局的目錄名不同:第一個佈局的目錄名為:layout,第二個佈局的目錄名為:layout-large,包含瞭尺寸限定符(large)被定義為大屏的設備(7寸以上的平板)會自動加載包含瞭large限定符目錄的佈局,而小屏設備會加載另一個默認的佈局

但要註意的是,這種方式隻適合Android 3.2版本之前。

最小寬度(Smallest-width)限定符

背景:上述提到的限定符“large”具體是指多大呢?似乎沒有一個定量的指標,這便意味著可能沒辦法準確地根據當前設備的配置(屏幕尺寸)自動加載合適的佈局資源例子:比如說large同時包含著5寸和7寸,這意味著使用“large”限定符的話我沒辦法實現為5寸和7寸的平板電腦分別加載不同的佈局

於是,在Android 3.2及之後版本,引入瞭最小寬度(Smallest-width)限定符

定義:通過指定某個最小寬度(以 dp 為單位)來精確定位屏幕從而加載不同的UI資源

使用場景

你需要為標準 7 英寸平板電腦匹配雙面板佈局(其最小寬度為 600 dp),在手機(較小的屏幕上)匹配單面板佈局

解決方案:您可以使用上文中所述的單面板和雙面板這兩種佈局,但您應使用sw600dp指明雙面板佈局僅適用於最小寬度為 600 dp 的屏幕,而不是使用 large 尺寸限定符。

sw xxxdp,即small width的縮寫,其不區分方向,即無論是寬度還是高度,隻要大於 xxxdp,就采用次此佈局例子:使用瞭layout-sw 600dp的最小寬度限定符,即無論是寬度還是高度,隻要大於600dp,就采用layout-sw 600dp目錄下的佈局

代碼展示:

適配手機的單面板(默認)佈局:res/layout/main.xml



  

適配尺寸>7寸平板的雙面板佈局:res/layout-sw600dp/main.xml


    
    

對於最小寬度≥ 600 dp 的設備
系統會自動加載 layout-sw600dp/main.xml(雙面板)佈局,否則系統就會選擇 layout/main.xml(單面板)佈局
(這個選擇過程是Android系統自動選擇的)

使用佈局別名

設想這麼一個場景

當你需要同時為Android 3.2版本前和Android 3.2版本後的手機進行屏幕尺寸適配的時候,由於尺寸限定符僅用於Android 3.2版本前,最小寬度限定符僅用於Android 3.2版本後,所以這會帶來一個問題,為瞭很好地進行屏幕尺寸的適配,你需要同時維護layout-sw600dp和layout-large的兩套main.xml平板佈局,如下:

適配手機的單面板(默認)佈局:res/layout/main.xml適配尺寸>7寸平板的雙面板佈局(Android 3.2前):res/layout-large/main.xml適配尺寸>7寸平板的雙面板佈局(Android 3.2後)res/layout-sw600dp/main.xml

最後的兩個文件的xml內容是完全相同的,這會帶來:文件名的重復從而帶來一些列後期維護的問題

於是為瞭要解決這種重復問題,我們引入瞭“佈局別名”

還是上面的例子,你可以定義以下佈局:

適配手機的單面板(默認)佈局:res/layout/main.xml適配尺寸>7寸平板的雙面板佈局:res/layout/main_twopanes.xml

然後加入以下兩個文件,以便進行Android 3.2前和Android 3.2後的版本雙面板佈局適配:

res/values-large/layout.xml(Android 3.2之前的雙面板佈局)


 @layout/main_twopanes

res/values-sw600dp/layout.xml(Android 3.2及之後的雙面板佈局)


@layout/main_twopanes

註:

最後兩個文件有著相同的內容,但是它們並沒有真正去定義佈局,它們僅僅隻是將main設置成瞭@layout/main_twopanes的別名由於這些文件包含 large 和 sw600dp 選擇器,因此,系統會將此文件匹配到不同版本的>7寸平板上:
a. 版本低於 3.2 的平板會匹配 large的文件
b. 版本高於 3.2 的平板會匹配 sw600dp的文件

這樣兩個layout.xml都隻是引用瞭@layout/main_twopanes,就避免瞭重復定義佈局文件的情況

屏幕方向(Orientation)限定符

使用場景:根據屏幕方向進行佈局的調整

取以下為例子:

小屏幕, 豎屏: 單面板小屏幕, 橫屏: 單面板7 英寸平板電腦,縱向:單面板,帶操作欄7 英寸平板電腦,橫向:雙面板,寬,帶操作欄10 英寸平板電腦,縱向:雙面板,窄,帶操作欄10 英寸平板電腦,橫向:雙面板,寬,帶操作欄電視,橫向:雙面板,寬,帶操作欄

方法是:

先定義類別:單/雙面板、是否帶操作欄、寬/窄

定義在 res/layout/ 目錄下的某個 XML 文件中

再進行相應的匹配:屏幕尺寸(小屏、7寸、10寸)、方向(橫、縱)

使用佈局別名進行匹配

在 res/layout/ 目錄下的某個 XML 文件中定義所需要的佈局類別
(單/雙面板、是否帶操作欄、寬/窄)
res/layout/onepane.xml:(單面板)

  

   

res/layout/onepane_with_bar.xml:(單面板帶操作欄)

  
      
          
        

res/layout/twopanes.xml:(雙面板,寬佈局)


    
    

res/layout/twopanes_narrow.xml:(雙面板,窄佈局)


    
    

2.使用佈局別名進行相應的匹配
(屏幕尺寸(小屏、7寸、10寸)、方向(橫、縱))
res/values/layouts.xml:(默認佈局)

  
    @layout/onepane_with_bar  
    false  

可為resources設置bool,通過獲取其值來動態判斷目前已處在哪個適配佈局

res/values-sw600dp-land/layouts.xml
(大屏、橫向、雙面板、寬-Andorid 3.2版本後)


    @layout/twopanes
    true

res/values-sw600dp-port/layouts.xml
(大屏、縱向、單面板帶操作欄-Andorid 3.2版本後)


    @layout/onepane
    false

res/values-large-land/layouts.xml
(大屏、橫向、雙面板、寬-Andorid 3.2版本前)


    @layout/twopanes
    true

res/values-large-port/layouts.xml
(大屏、縱向、單面板帶操作欄-Andorid 3.2版本前)


    @layout/onepane
    false

這裡沒有完全把全部尺寸匹配類型的代碼貼出來,大傢可以自己去嘗試把其補充完整


“佈局組件”匹配

本質:使得佈局組件自適應屏幕尺寸

做法
使用”wrap_content”、”match_parent”和”weight“來控制視圖組件的寬度和高度

”wrap_content”
相應視圖的寬和高就會被設定成所需的最小尺寸以適應視圖中的內容“match_parent”(在Android API 8之前叫作”fill_parent”)
視圖的寬和高延伸至充滿整個父佈局“weight”
1.定義:是線性佈局(Linelayout)的一個獨特比例分配屬性
2.作用:使用此屬性設置權重,然後按照比例對界面進行空間的分配,公式計算是:控件寬度=控件設置寬度+剩餘空間所占百分比寬幅
具體可以參考這篇文章,講解得非常詳細

通過使用”wrap_content”、”match_parent”和”weight”來替代硬編碼的方式定義視圖大小&位置,你的視圖要麼僅僅使用瞭需要的那邊一點空間,要麼就會充滿所有可用的空間,即按需占據空間大小,能讓你的佈局元素充分適應你的屏幕尺寸


“圖片資源”匹配

本質:使得圖片資源在不同屏幕密度上顯示相同的像素效果

做法:使用自動拉伸位圖:Nine-Patch的圖片類型
假設需要匹配不同屏幕大小,你的圖片資源也必須自動適應各種屏幕尺寸

使用場景:一個按鈕的背景圖片必須能夠隨著按鈕大小的改變而改變。
使用普通的圖片將無法實現上述功能,因為運行時會均勻地拉伸或壓縮你的圖片

解決方案:使用自動拉伸位圖(nine-patch圖片),後綴名是.9.png,它是一種被特殊處理過的PNG圖片,設計時可以指定圖片的拉伸區域和非拉伸區域;使用時,系統就會根據控件的大小自動地拉伸你想要拉伸的部分

1.必須要使用.9.png後綴名,因為系統就是根據這個來區別nine-patch圖片和普通的PNG圖片的;
2.當你需要在一個控件中使用nine-patch圖片時,如
android:background="@drawable/button"系統就會根據控件的大小自動地拉伸你想要拉伸的部分


”用戶界面流程“匹配

使用場景:我們會根據設備特點顯示恰當的佈局,但是這樣做,會使得用戶界面流程可能會有所不同。例如,如果應用處於雙面板模式下,點擊左側面板上的項即可直接在右側面板上顯示相關內容;而如果該應用處於單面板模式下,點擊相關的內容應該跳轉到另外一個Activity進行後續的處理。

本質:根據屏幕的配置來加載相應的用戶界面流程

做法
進行用戶界面流程的自適應配置: 確定當前佈局根據當前佈局做出響應重復使用其他活動中的片段處理屏幕配置變化

步驟1:確定當前佈局
由於每種佈局的實施都會稍有不同,因此我們需要先確定當前向用戶顯示的佈局。例如,我們可以先瞭解用戶所處的是“單面板”模式還是“雙面板”模式。要做到這一點,可以通過查詢指定視圖是否存在以及是否已顯示出來。

public class NewsReaderActivity extends FragmentActivity {
  boolean mIsDualPane;

  @Override
  public void onCreate(Bundle savedInstanceState) {
      super.onCreate(savedInstanceState);
      setContentView(R.layout.main_layout);

      View articleView = findViewById(R.id.article);
      mIsDualPane = articleView != null &&
                      articleView.getVisibility() == View.VISIBLE;
  }
}

這段代碼用於查詢“報道”面板是否可用,與針對具體佈局的硬編碼查詢相比,這段代碼的靈活性要大得多。

步驟2:根據當前佈局做出響應
有些操作可能會因當前的具體佈局而產生不同的結果。

例如,在新聞閱讀器示例中,如果用戶界面處於雙面板模式下,那麼點擊標題列表中的標題就會在右側面板中打開相應報道;但如果用戶界面處於單面板模式下,那麼上述操作就會啟動一個獨立活動:

@Override
public void onHeadlineSelected(int index) {
  mArtIndex = index;
  if (mIsDualPane) {
      /* display article on the right pane */
      mArticleFragment.displayArticle(mCurrentCat.getArticle(index));
  } else {
      /* start a separate activity */
      Intent intent = new Intent(this, ArticleActivity.class);
      intent.putExtra("catIndex", mCatIndex);
      intent.putExtra("artIndex", index);
      startActivity(intent);
  }
}

步驟3:重復使用其他活動中的片段
多屏幕設計中的重復模式是指,對於某些屏幕配置,已實施界面的一部分會用作面板;但對於其他配置,這部分就會以獨立活動的形式存在。

例如,在新聞閱讀器示例中,對於較大的屏幕,新聞報道文本會顯示在右側面板中;但對於較小的屏幕,這些文本就會以獨立活動的形式存在。

在類似情況下,通常可以在多個活動中重復使用相同的 Fragment 子類以避免代碼重復。例如,在雙面板佈局中使用瞭 ArticleFragment:


    
    

然後又在小屏幕的Activity佈局中重復使用瞭它 :

ArticleFragment frag = new ArticleFragment();
getSupportFragmentManager().beginTransaction().add(android.R.id.content, frag).commit();

步驟3:處理屏幕配置變化
如果我們使用獨立Activity實施界面的獨立部分,那麼請註意,我們可能需要對特定配置變化(例如屏幕方向的變化)做出響應,以便保持界面的一致性。

例如,在運行 Android 3.0 或更高版本的標準 7 英寸平板電腦上,如果新聞閱讀器示例應用運行在縱向模式下,就會在使用獨立活動顯示新聞報道;但如果該應用運行在橫向模式下,就會使用雙面板佈局。

也就是說,如果用戶處於縱向模式下且屏幕上顯示的是用於閱讀報道的活動,那麼就需要在檢測到屏幕方向變化(變成橫向模式)後執行相應操作,即停止上述活動並返回主活動,以便在雙面板佈局中顯示相關內容:

public class ArticleActivity extends FragmentActivity {
    int mCatIndex, mArtIndex;

    @Override
    protected void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        mCatIndex = getIntent().getExtras().getInt("catIndex", 0);
        mArtIndex = getIntent().getExtras().getInt("artIndex", 0);

        // If should be in two-pane mode, finish to return to main activity
        if (getResources().getBoolean(R.bool.has_two_panes)) {
            finish();
            return;
        }
        ...
}

通過上面一系列步驟,我們就完全可以建立一個可以根據用戶界面配置進行自適應的應用程序App瞭。


總結

經過上面的介紹,對於屏幕尺寸大小適配問題應該是不成問題瞭。


解決方案

問題:如何進行屏幕密度匹配?答:
屏幕密度匹配解決方案.png

“佈局控件”匹配

本質:使得佈局組件在不同屏幕密度上顯示相同的像素效果

做法1:使用密度無關像素
由於各種屏幕的像素密度都有所不同,因此相同數量的像素在不同設備上的實際大小也有所差異,這樣使用像素(px)定義佈局尺寸就會產生問題。
因此,請務必使用密度無關像素 dp或獨立比例像素 sp單位指定尺寸。相關概念介紹
密度無關像素含義:density-independent pixel,叫dp或dip,與終端上的實際物理像素點無關。

單位:dp,可以保證在不同屏幕像素密度的設備上顯示相同的效果

Android開發時用dp而不是px單位設置圖片大小,是Android特有的單位場景:假如同樣都是畫一條長度是屏幕一半的線,如果使用px作為計量單位,那麼在480x800分辨率手機上設置應為240px;在320x480的手機上應設置為160px,二者設置就不同瞭;如果使用dp為單位,在這兩種分辨率下,160dp都顯示為屏幕一半的長度。

dp與px的轉換
因為ui給你的設計圖是以px為單位的,Android開發則是使用dp作為單位的,那麼該如何轉換呢?

密度類型 代表的分辨率(px) 屏幕密度(dpi) 換算(px/dp) 比例
低密度(ldpi) 240x320 120 1dp=0.75px 3
中密度(mdpi) 320x480 160 1dp=1px 4
高密度(hdpi) 480x800 240 1dp=1.5px 6
超高密度(xhdpi) 720x1280 320 1dp=2px 8
超超高密度(xxhdpi) 1080x1920 480 1dp=3px 12

在Android中,規定以160dpi(即屏幕分辨率為320x480)為基準:1dp=1px

獨立比例像素

含義:scale-independent pixel,叫sp或sip單位:sp

Android開發時用此單位設置文字大小,可根據用戶的偏好文字大小/字體大小首選項進行縮放推薦使用12sp、14sp、18sp、22sp作為字體設置的大小,不推薦使用奇數和小數,容易造成精度的丟失問題;小於12sp的字體會太小導致用戶看不清

所以,為瞭能夠進行不同屏幕像素密度的匹配,我們推薦:

使用dp來代替px作為控件長度的統一度量單位使用sp作為文字的統一度量單位

可是,請看以下一種場景:

Nexus5的總寬度為360dp,我們現在在水平方向上放置兩個按鈕,一個是150dp左對齊,另外一個是200dp右對齊,那麼中間留有10dp間隔;但假如同樣地設置在Nexus S(屏幕寬度是320dp),會發現,兩個按鈕會重疊,因為320dp<200+150dp

從上面可以看出,由於Android屏幕設備的多樣性,如果使用dp來作為度量單位,並不是所有的屏幕的寬度都具備相同的dp長度

再次明確,屏幕寬度和像素密度沒有任何關聯關系

所以說,dp解決瞭同一數值在不同分辨率中展示相同尺寸大小的問題(即屏幕像素密度匹配問題),但卻沒有解決設備尺寸大小匹配的問題。(即屏幕尺寸匹配問題)

當然,我們一開始討論的就是屏幕尺寸匹配問題,使用match_parent、wrap_content和weight,盡可能少用dp來指定控件的具體長寬,大部分的情況我們都是可以做到適配的。

那麼該如何解決控件的屏幕尺寸和屏幕密度的適配問題呢?

從上面可以看出:

因為屏幕密度(分辨率)不一樣,所以不能用固定的px因為屏幕寬度不一樣,所以要小心的用dp

因為本質上是希望使得佈局組件在不同屏幕密度上顯示相同的像素效果,那麼,之前是繞瞭個彎使用dp解決這個問題,那麼到底能不能直接用px解決呢?

即根據不同屏幕密度,控件選擇對應的像素值大小

接下來介紹一種方法:百分比適配方法,步驟如下:

以某一分辨率為基準,生成所有分辨率對應像素數列表將生成像素數列表存放在res目錄下對應的values文件下根據UI設計師給出設計圖上的尺寸,找到對應像素數的單位,然後設置給控件即可

步驟1:以某一分辨率為基準,生成所有分辨率對應像素數列表

現在我們以320x480的分辨率為基準:

將屏幕的寬度分為320份,取值為x1~x320將屏幕的高度分為480份,取值為y1~y480

然後生成該分辨率對應像素數的列表,如下圖:

lay_x.xml(寬)


1.0px
2.0px
3.0px
4.0px
5.0px
6.0px
7.0px
8.0px
9.0px
10.0px
...
300.0px
301.0px
302.0px
303.0px
304.0px
305.0px
306.0px
307.0px
308.0px
309.0px
310.0px
311.0px
312.0px
313.0px
314.0px
315.0px
316.0px
317.0px
318.0px
319.0px
320px

lay_y.xml(高)


1.0px
2.0px
3.0px
4.0px
...
480px

找到基準後,是時候把其他分辨率補全瞭,現今以寫1080x1920的分辨率為例:

因為基準是320x480,所以1080/320=3.375px,1920/480=4px,所以相應文件應該是

lay_x.xml


3.375px
6.65px
10.125px
...
1080px

lay_y.xml


4px
8px
12px
16px
...
1920px

用上面的方法把你需要適配的分辨率的像素列表補全吧~

作為程序猿的我們當然不會做手寫的這些蠢事!!!多謝 @鴻洋大神 提供瞭自動生成工具(內置瞭常用的分辨率)。
註:工具默認基準為400*320,當然對於特殊需求,通過命令行指定即可:

java -jar 文件名.jar 基準寬 基準高 額外支持尺寸1的寬,額外支持尺寸1的高_額外支持尺寸2的寬,額外支持尺寸2的高:

例如:需要設置的基準是800x1280,額外支持尺寸:735x1152 ;3200x4500;

java -jar 文件名.jar 800 1280 735,1152_3200,4500

步驟2:把生成的各像素數列表放到對應的資源文件

將生成像素數列表(lay_x.xml和lay_y.xml)存放在res目錄下對應的values文件(註意寬、高要對應),如下圖:


res目錄下對應的values文件

註:

分辨率為480x320的資源文件應放在res/values-480x320文件夾中;同理分辨率為1920x1080的資源文件應放在res/values-1920x1080文件夾中。(其中values-480x320是分辨率限定符)必須在默認values裡面也創建對應默認lay_x.xml和lay_y.xml文件,如下圖
lay_x.xml



1.0dp
2.0dp
...

因為對於沒有生成對應分辨率文件的手機,會使用默認values文件夾,如果默認values文件夾沒有(即沒有對應的分辨率、沒有對應dimen)就會報錯,從而無法進行屏幕適配。
(註意對應單位改為dp,而不同於上面的px。因為不知道機型的分辨率,所以默認分辨率文件隻好默認為x1=1dp以保證盡量兼容(又回到dp老方法瞭),這也是這個解決方案的一個弊端)

步驟3:根據UI設計師給出某一分辨率設計圖上的尺寸,找到對應像素數的單位,然後設置給控件即可

如下圖:

<framelayout></framelayout>

總結

使用上述的適配方式,應該能進行90%的適配瞭,但其缺點還是很明顯:

由於實際上還是使用px作為長度的度量單位,所以和google的要求使用dp作為度量單位會有所背離必須盡可能多的包含所有分辨率,因為這個是使用這個方案的基礎,如果有某個分辨率缺少,將無法完成該屏幕的適配過多的分辨率像素描述xml文件會增加軟件包的大小和維護的難度


“圖片資源”匹配

本質:使得圖片資源在不同屏幕密度上顯示相同的像素效果

做法:提供備用位圖(符合屏幕尺寸的圖片資源)
由於 Android 可在各種屏幕密度的設備上運行,因此我們提供的位圖資源應該始終可以滿足各類密度的要求:

密度類型 代表的分辨率(px) 系統密度(dpi)
低密度(ldpi) 240x320 120
中密度(mdpi) 320x480 160
高密度(hdpi) 480x800 240
超高密度(xhdpi) 720x1280 320
超超高密度(xxhdpi) 1080x1920 480

步驟1:根據以下尺寸范圍針對各密度生成相應的圖片。

比如說,如果我們為 xhdpi 設備生成瞭 200x200 px尺寸的圖片,就應該按照相應比例地為 hdpi、mdpi 和 ldpi 設備分別生成 150x150、100x100 和 75x75 尺寸的圖片

即一套分辨率=一套位圖資源(這個當然是Ui設計師做瞭)

步驟2:將生成的圖片文件放在 res/ 下的相應子目錄中(mdpi、hdpi、xhdpi、xxhdpi),系統就會根據運行您應用的設備的屏幕密度自動選擇合適的圖片步驟3:通過引用 @drawable/id,系統都能根據相應屏幕的 屏幕密度(dpi)自動選取合適的位圖。

註:

如果是.9圖或者是不需要多個分辨率的圖片,放在drawable文件夾即可對應分辨率的圖片要正確的放在合適的文件夾,否則會造成圖片拉伸等問題。

更好地方案解決“圖片資源”適配問題

上述方案是常見的一種方案,這固然是一種解決辦法,但缺點在於:

每套分辨率出一套圖,為美工或者設計增加瞭許多工作量對Android工程文件的apk包變的很大

那麼,有沒有一種方法:

保證屏幕密度適配可以最小占用設計資源使得apk包不變大(隻使用一套分辨率的圖片資源)

下面我們就來介紹這個方法:

隻需選擇唯一一套分辨率規格的圖片資源

方法介紹

1. 先來理解下Android 加載資源過程
Android SDK會根據屏幕密度自動選擇對應的資源文件進行渲染加載(自動渲染)

比如說,SDK檢測到你手機的分辨率是320x480(dpi=160),會優先到drawable-mdpi文件夾下找對應的圖片資源;但假設你隻在xhpdi文件夾下有對應的圖片資源文件(mdpi文件夾是空的),那麼SDK會去xhpdi文件夾找到相應的圖片資源文件,然後將原有大像素的圖片自動縮放成小像素的圖片,於是大像素的圖片照樣可以在小像素分辨率的手機上正常顯示。

所以理論上來說隻需要提供一種分辨率規格的圖片資源就可以瞭。
那麼應該提供哪種分辨率規格呢?

如果隻提供ldpi規格的圖片,對於大分辨率(xdpi、xxdpi)的手機如果把圖片放大就會不清晰

所以需要提供一套你需要支持的最大dpi分辨率規格的圖片資源,這樣即使用戶的手機分辨率很小,這樣圖片縮小依然很清晰。那麼這一套最大dpi分辨率規格應該是哪種呢?是現在市面手機分辨率最大可達到1080X1920的分辨率(dpi=xxdpi=480)嗎?

2. xhdpi應該是首選

原因如下:

xhdpi分辨率以內的手機需求量最旺盛
目前市面上最普遍的高端機的分辨率還多集中在720X1080范圍內(xhdpi),所以目前來看xhpdi規格的圖片資源成為瞭首選節省設計資源&工作量
在現在的App開發中(iOS和Android版本),有些設計師為瞭保持App不同版本的體驗交互一致,可能會以iPhone手機為基礎進行設計,包括後期的切圖之類的。
設計師們一般都會用最新的iPhone6和iPhone5s(5s和5的尺寸以及分辨率都一樣)來做原型設計,所有參數請看下圖

機型 分辨率(px) 屏幕尺寸(inch) 系統密度(dpi)
iPhone 5s 640X1164 4 332
iPhone 6 1334x750 4.7 326
iPhone 6 Plus 1080x1920 5 400

iPhone主流的屏幕dpi約等於320, 剛好屬於xhdpi,所以選擇xhdpi作為唯一一套dpi圖片資源,可以讓設計師不用專門為Android端切圖,直接把iPhone的那一套切好的圖片資源放入drawable-xhdpi文件夾裡就好,這樣大大減少的設計師的工作量!

額外小tips

ImageView的ScaleType屬性
設置不同的ScaleType會得到不同的顯示效果,一般情況下,設置為centerCrop能獲得較好的適配效果。

動態設置

使用場景:有些情況下,我們需要動態的設置控件大小或者是位置,比如說popwindow的顯示位置和偏移量等

這時我們可以動態獲取當前的屏幕屬性,然後設置合適的數值

public class ScreenSizeUtil { 
  public static int getScreenWidth(Activity activity) { 
    return activity.getWindowManager().getDefaultDisplay().getWidth(); 
} 
  public static int getScreenHeight(Activity activity) { 
    return activity.getWindowManager().getDefaultDisplay().getHeight(); 
   }
}

總結

本文根據現今主流Android的適配方法,以邏輯清晰的方式進行瞭主流Android適配方法的全面整理,接下來我會介紹繼續介紹Android開發中的相關知識。

發佈留言

發佈留言必須填寫的電子郵件地址不會公開。 必填欄位標示為 *