1. 程式人生 > >Android 螢幕適配:最全面的解決方案

Android 螢幕適配:最全面的解決方案

螢幕尺寸適配解決方案.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
<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
    android:orientation="vertical"
    android:layout_width="match_parent"
    android:layout_height="match_parent">

    <fragment android:id="@+id/headlines"
              android:layout_height="fill_parent"
              android:name="com.example.android.newsreader.HeadlinesFragment"
              android:layout_width="match_parent" />
</LinearLayout>
  • 適配尺寸>7寸平板的雙面板佈局::res/layout-large/main.xml
<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
    android:layout_width="fill_parent"
    android:layout_height="fill_parent"
    android:orientation="horizontal">
    <fragment android:id="@+id/headlines"
              android:layout_height="fill_parent"
              android:name="com.example.android.newsreader.HeadlinesFragment"
              android:layout_width="400dp"
              android:layout_marginRight="10dp"/>
    <fragment android:id="@+id/article"
              android:layout_height="fill_parent"
              android:name="com.example.android.newsreader.ArticleFragment"
              android:layout_width="fill_parent" />
</LinearLayout>

請注意:

  • 兩個佈局名稱均為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
<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
    android:orientation="vertical"
    android:layout_width="match_parent"
    android:layout_height="match_parent">

    <fragment android:id="@+id/headlines"
              android:layout_height="fill_parent"
              android:name="com.example.android.newsreader.HeadlinesFragment"
              android:layout_width="match_parent" />
</LinearLayout>
  • 適配尺寸>7寸平板的雙面板佈局:res/layout-sw600dp/main.xml
<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
    android:layout_width="fill_parent"
    android:layout_height="fill_parent"
    android:orientation="horizontal">
    <fragment android:id="@+id/headlines"
              android:layout_height="fill_parent"
              android:name="com.example.android.newsreader.HeadlinesFragment"
              android:layout_width="400dp"
              android:layout_marginRight="10dp"/>
    <fragment android:id="@+id/article"
              android:layout_height="fill_parent"
              android:name="com.example.android.newsreader.ArticleFragment"
              android:layout_width="fill_parent" />
</LinearLayout>
  • 對於最小寬度≥ 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後的版本雙面板佈局適配:

  1. res/values-large/layout.xml(Android 3.2之前的雙面板佈局)
<resources>
    <item name="main" type="layout">@layout/main_twopanes</item>
</resources>
  1. res/values-sw600dp/layout.xml(Android 3.2及之後的雙面板佈局)
<resources>
<item name="main" type="layout">@layout/main_twopanes</item>
</resources>

注:

  • 最後兩個檔案有著相同的內容,但是它們並沒有真正去定義佈局,它們僅僅只是將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寸)、方向(橫、縱)

使用佈局別名進行匹配

  1. 在 res/layout/ 目錄下的某個 XML 檔案中定義所需要的佈局類別
    (單/雙面板、是否帶操作欄、寬/窄)
    res/layout/onepane.xml:(單面板)
<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"  
    android:orientation="vertical"  
    android:layout_width="match_parent"  
    android:layout_height="match_parent">  
  
    <fragment android:id="@+id/headlines"  
              android:layout_height="fill_parent"  
              android:name="com.example.android.newsreader.HeadlinesFragment"  
              android:layout_width="match_parent" />  
</LinearLayout>  

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

<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"  
    android:orientation="vertical"  
    android:layout_width="match_parent"  
    android:layout_height="match_parent">  
    <LinearLayout android:layout_width="match_parent"   
                  android:id="@+id/linearLayout1"    
                  android:gravity="center"  
                  android:layout_height="50dp">  
        <ImageView android:id="@+id/imageView1"   
                   android:layout_height="wrap_content"  
                   android:layout_width="wrap_content"  
                   android:src="@drawable/logo"  
                   android:paddingRight="30dp"  
                   android:layout_gravity="left"  
                   android:layout_weight="0" />  
        <View android:layout_height="wrap_content"   
              android:id="@+id/view1"  
              android:layout_width="wrap_content"  
              android:layout_weight="1" />  
        <Button android:id="@+id/categorybutton"  
                android:background="@drawable/button_bg"  
                android:layout_height="match_parent"  
                android:layout_weight="0"  
                android:layout_width="120dp"  
                style="@style/CategoryButtonStyle"/>  
    </LinearLayout>  
  
    <fragment android:id="@+id/headlines"   
              android:layout_height="fill_parent"  
              android:name="com.example.android.newsreader.HeadlinesFragment"  
              android:layout_width="match_parent" />  
</LinearLayout> 

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

<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
    android:layout_width="fill_parent"
    android:layout_height="fill_parent"
    android:orientation="horizontal">
    <fragment android:id="@+id/headlines"
              android:layout_height="fill_parent"
              android:name="com.example.android.newsreader.HeadlinesFragment"
              android:layout_width="400dp"
              android:layout_marginRight="10dp"/>
    <fragment android:id="@+id/article"
              android:layout_height="fill_parent"
              android:name="com.example.android.newsreader.ArticleFragment"
              android:layout_width="fill_parent" />
</LinearLayout>

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

<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
    android:layout_width="fill_parent"
    android:layout_height="fill_parent"
    android:orientation="horizontal">
    <fragment android:id="@+id/headlines"
              android:layout_height="fill_parent"
              android:name="com.example.android.newsreader.HeadlinesFragment"
              android:layout_width="200dp"
              android:layout_marginRight="10dp"/>
    <fragment android:id="@+id/article"
              android:layout_height="fill_parent"
              android:name="com.example.android.newsreader.ArticleFragment"
              android:layout_width="fill_parent" />
</LinearLayout>

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

<resources>  
    <item name="main_layout" type="layout">@layout/onepane_with_bar</item>  
    <bool name="has_two_panes">false</bool>  
</resources> 

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

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

<resources>
    <item name="main_layout" type="layout">@layout/twopanes</item>
    <bool name="has_two_panes">true</bool>
</resources>

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

<resources>
    <item name="main_layout" type="layout">@layout/onepane</item>
    <bool name="has_two_panes">false</bool>
</resources>

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

<resources>
    <item name="main_layout" type="layout">@layout/twopanes</item>
    <bool name="has_two_panes">true</bool>
</resources>

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

<resources>
    <item name="main_layout" type="layout">@layout/onepane</item>
    <bool name="has_two_panes">false</bool>
</resources>

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

“佈局元件”匹配

本質:使得佈局元件自適應螢幕尺寸

  • 做法
    使用"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圖片時,如

```系統就會根據控制元件的大小自動地拉伸你想要拉伸的部分
***
###”使用者介面流程“匹配
>- 使用場景:我們會根據裝置特點顯示恰當的佈局,但是這樣做,會使得使用者介面流程可能會有所不同。
>- 例如,如果應用處於雙面板模式下,點選左側面板上的項即可直接在右側面板上顯示相關內容;而如果該應用處於單面板模式下,點選相關的內容應該跳轉到另外一個Activity進行後續的處理。

**本質:根據螢幕的配置來載入相應的使用者介面流程**
- 做法
進行使用者介面流程的自適應配置:


1. 確定當前佈局
2. 根據當前佈局做出響應
3. 重複使用其他活動中的片段
4. 處理螢幕配置變化






- 步驟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:

<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
android:layout_width="fill_parent"
android:layout_height="fill_parent"
android:orientation="horizontal">
<fragment android:id="@+id/headlines"
android:layout_height="fill_parent"
android:name="com.example.android.newsreader.HeadlinesFragment"
android:layout_width="400dp"
android:layout_marginRight="10dp"/>
<fragment android:id="@+id/article"
android:layout_height="fill_parent"
android:name="com.example.android.newsreader.ArticleFragment"
android:layout_width="fill_parent" />
</LinearLayout>

然後又在小螢幕的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](http://upload-images.jianshu.io/upload_images/944365-04e4124d0daf0a69.png?imageMogr2/auto-orient/strip%7CimageView2/2/w/1240)

###“佈局控制元件”匹配
**本質:使得佈局元件在不同螢幕密度上顯示相同的畫素效果**
- 做法1:使用密度無關畫素
由於各種螢幕的畫素密度都有所不同,因此相同數量的畫素在不同裝置上的實際大小也有所差異,這樣使用畫素(px)定義佈局尺寸就會產生問題。
因此,請務必使用**密度無關畫素 dp** 或**獨立比例畫素 sp **單位指定尺寸。
- 相關概念介紹
**密度無關畫素**
- 含義:density-independent pixel,叫dp或dip,與終端上的實際物理畫素點無關。
- 單位:dp,可以保證在不同螢幕畫素密度的裝置上顯示相同的效果
>1. Android開發時用dp而不是px單位設定圖片大小,是Android特有的單位
>2. 場景:假如同樣都是畫一條長度是螢幕一半的線,如果使用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
>1. Android開發時用此單位設定文字大小,可根據使用者的偏好文字大小/字型大小首選項進行縮放
>2. 推薦使用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解決呢?
>即根據不同螢幕密度,控制元件選擇對應的畫素值大小

接下來介紹一種方法:**百分比適配方法**,步驟如下:
1. 以某一解析度為基準,生成所有解析度對應畫素數列表
2. 將生成畫素數列表存放在res目錄下對應的values檔案下
3. 根據UI設計師給出設計圖上的尺寸,找到對應畫素數的單位,然後設定給控制元件即可


#步驟1:以某一解析度為基準,生成所有解析度對應畫素數列表

現在我們以320x480的解析度為基準:
- 將螢幕的寬度分為320份,取值為x1~x320
- 將螢幕的高度分為480份,取值為y1~y480

然後生成該解析度對應畫素數的列表,如下圖:
- lay_x.xml(寬)

<?xml version="1.0" encoding="utf-8"?>
<resources><dimen name="x1">1.0px</dimen>
<dimen name="x2">2.0px</dimen>
<dimen name="x3">3.0px</dimen>
<dimen name="x4">4.0px</dimen>
<dimen name="x5">5.0px</dimen>
<dimen name="x6">6.0px</dimen>
<dimen name="x7">7.0px</dimen>
<dimen name="x8">8.0px</dimen>
<dimen name="x9">9.0px</dimen>
<dimen name="x10">10.0px</dimen>
...
<dimen name="x300">300.0px</dimen>
<dimen name="x301">301.0px</dimen>
<dimen name="x302">302.0px</dimen>
<dimen name="x303">303.0px</dimen>
<dimen name="x304">304.0px</dimen>
<dimen name="x305">305.0px</dimen>
<dimen name="x306">306.0px</dimen>
<dimen name="x307">307.0px</dimen>
<dimen name="x308">308.0px</dimen>
<dimen name="x309">309.0px</dimen>
<dimen name="x310">310.0px</dimen>
<dimen name="x311">311.0px</dimen>
<dimen name="x312">312.0px</dimen>
<dimen name="x313">313.0px</dimen>
<dimen name="x314">314.0px</dimen>
<dimen name="x315">315.0px</dimen>
<dimen name="x316">316.0px</dimen>
<dimen name="x317">317.0px</dimen>
<dimen name="x318">318.0px</dimen>
<dimen name="x319">319.0px</dimen>
<dimen name="x320">320px</dimen>
</resources>


- lay_y.xml(高)

<?xml version="1.0" encoding="utf-8"?>
<resources><dimen name="y1">1.0px</dimen>
<dimen name="y2">2.0px</dimen>
<dimen name="y3">3.0px</dimen>
<dimen name="y4">4.0px</dimen>
...
<dimen name="y480">480px</dimen>
</resources>