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

**版權(quán)聲明:本文為Carson_Ho原創(chuàng)文章,轉(zhuǎn)載請(qǐng)注明出處!

目錄

一、定義

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

二、相關(guān)重要概念

屏幕尺寸

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

Android手機(jī)常見的尺寸有5寸、5.5寸、6寸等等

屏幕分辨率

  • 含義:手機(jī)橫向、縱向上的像素點(diǎn)數(shù)總和
    一般描述成屏幕的"寬x高”=AxB
    含義:屏幕在橫向方向(寬度)上有A個(gè)像素點(diǎn),在縱向方向
    (高)有B個(gè)像素點(diǎn)
    例子:1080x1920,即寬度方向上有1080個(gè)像素點(diǎn),在高度方向上有1920個(gè)像素點(diǎn)
  • 單位:px(pixel),1px=1像素點(diǎn)
    UI設(shè)計(jì)師的設(shè)計(jì)圖會(huì)以px作為統(tǒng)一的計(jì)量單位,Android手機(jī)常見的分辨率:320x480、480x800、720x1280、1080x1920。

屏幕像素密度

  • 含義:每英寸的像素點(diǎn)數(shù)
  • 單位:dpi(dots per ich)
    假設(shè)設(shè)備內(nèi)每英寸有160個(gè)像素,那么該設(shè)備的屏幕像素密度=160dpi
  • 安卓手機(jī)對(duì)于每類手機(jī)屏幕大小都有一個(gè)相應(yīng)的屏幕像素密度:
密度類型 代表的分辨率(px) 屏幕像素密度(dpi)
低密度(ldpi) 240x320 120
中密度(mdpi) 320x480 160
高密度(hdpi) 480x800 240
超高密度(xhdpi) 720x1280 320
超超高密度(xxhdpi) 1080x1920 480

屏幕尺寸、分辨率、像素密度三者關(guān)系
一部手機(jī)的分辨率是寬x高,屏幕大小是以寸為單位,那么三者的關(guān)系是:


假設(shè)一部手機(jī)的分辨率是1080x1920(px),屏幕大小是5寸,問密度是多少?
解:請(qǐng)直接套公式
密度無(wú)關(guān)像素

  • 含義:density-independent pixel,叫dp或dip,與終端上的實(shí)際物理像素點(diǎn)無(wú)關(guān)。
  • 單位: dp,可以保證在不同屏幕像素密度的設(shè)備上顯示相同的效果
    Android開發(fā)時(shí)用dp而不是px單位設(shè)置圖片大小,是Android特有的單位
    場(chǎng)景:假如同樣都是畫一條長(zhǎng)度是屏幕一半的線,如果使用px作為計(jì)量單位,那么在480x800分辨率手機(jī)上設(shè)置應(yīng)為240px;在320x480的手機(jī)上應(yīng)設(shè)置為160px,二者設(shè)置就不同了;如果使用dp為單位,在這兩種分辨率下,160dp都顯示為屏幕一半的長(zhǎng)度。
  • dp與px的轉(zhuǎn)換
    因?yàn)閡i設(shè)計(jì)師給你的設(shè)計(jì)圖是以px為單位的,Android開發(fā)則是使用dp作為單位的,那么我們需要進(jìn)行轉(zhuǎn)換:
密度類型 代表的分辨率(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中,規(guī)定以160dpi(即屏幕分辨率為320x480)為基準(zhǔn):1dp=1px
獨(dú)立比例像素

  • 含義:scale-independent pixel,叫sp或sip
  • 單位: sp
    Android開發(fā)時(shí)用此單位設(shè)置文字大小,可根據(jù)字體大小首選項(xiàng)進(jìn)行縮放,推薦使用12sp、14sp、18sp、22sp作為字體設(shè)置的大小,不推薦使用奇數(shù)和小數(shù),容易造成精度的丟失問題;小于12sp的字體會(huì)太小導(dǎo)致用戶看不清。
三、為什么要進(jìn)行Android屏幕適配

由于Android系統(tǒng)的開放性,任何用戶、開發(fā)者、OEM廠商、運(yùn)營(yíng)商都可以對(duì)Android進(jìn)行定制,于是導(dǎo)致:

  • Android系統(tǒng)碎片化:小米定制的MIUI、魅族定制的flyme、華為定制的EMUI等等
    當(dāng)然都是基于Google原生系統(tǒng)定制的
  • Android機(jī)型屏幕尺寸碎片化:5寸、5.5寸、6寸等等
  • Android屏幕分辨率碎片化:320x480、480x800、720x1280、1080x1920
    據(jù)友盟指數(shù)顯示,統(tǒng)計(jì)至2015年12月,支持Android的設(shè)備共有27796種

當(dāng)Android系統(tǒng)、屏幕尺寸、屏幕密度出現(xiàn)碎片化的時(shí)候,就很容易出現(xiàn)同一元素在不同手機(jī)上顯示不同的問題。
試想一下這么一個(gè)場(chǎng)景:為4.3寸屏幕準(zhǔn)備的UI設(shè)計(jì)圖,運(yùn)行在5.0寸的屏幕上,很可能在右側(cè)和下側(cè)存在大量的空白;而5.0寸的UI設(shè)計(jì)圖運(yùn)行到4.3寸的設(shè)備上,很可能顯示不下。
為了保證用戶獲得一致的用戶體驗(yàn)效果: 使得某一元素在Android不同尺寸、不同分辨率的手機(jī)上具備相同的顯示效果。于是,我們便需要對(duì)Android屏幕進(jìn)行適配。

四、屏幕適配問題的本質(zhì)
  • 使得“布局”、“布局組件”、“圖片資源”、“用戶界面流程”匹配不同的屏幕尺寸
    使得布局、布局組件自適應(yīng)屏幕尺寸;根據(jù)屏幕的配置來(lái)加載相應(yīng)的UI布局、用戶界面流程。
  • 使得"圖片資源"匹配不同的屏幕密度
五、解決方案
  • 問題:如何進(jìn)行屏幕尺寸匹配?
  • 答:

    布局匹配
    本質(zhì)1:使得布局元素自適應(yīng)屏幕尺寸
  • 做法:使用相對(duì)布局(RelativeLayout),禁用絕對(duì)布局(AbsoluteLayout)
    開發(fā)中,我們使用的布局一般有:
  • 線性布局(Linearlayout)
  • 相對(duì)布局(RelativeLayout)
  • 幀布局(FrameLayout)
  • 絕對(duì)布局(AbsoluteLayout)

由于絕對(duì)布局(AbsoluteLayout)適配性極差,所以極少使用。
對(duì)于線性布局(Linearlayout)、相對(duì)布局(RelativeLayout)和幀布局(FrameLayout)需要根據(jù)需求進(jìn)行選擇,但要記住:

  • RelativeLayout
    布局的子控件之間使用相對(duì)位置的方式排列,因?yàn)镽elativeLayout講究的是相對(duì)位置,即使屏幕的大小改變,視圖之前的相對(duì)位置都不會(huì)變化,與屏幕大小無(wú)關(guān),靈活性很強(qiáng)
  • LinearLayout
    通過多層嵌套LinearLayout和組合使
    用"wrap_content"和"match_parent"已經(jīng)可以構(gòu)建出足夠復(fù)雜的布局。但是LinearLayout無(wú)法準(zhǔn)確地控制子視圖之間的位置關(guān)系,只能簡(jiǎn)單的一個(gè)挨著一個(gè)地排列。
    所以,對(duì)于屏幕適配來(lái)說,使用相對(duì)布局(RelativeLayout)將會(huì)是更好的解決方案
    本質(zhì)2:根據(jù)屏幕的配置來(lái)加載相應(yīng)的UI布局
    應(yīng)用場(chǎng)景:需要為不同屏幕尺寸的設(shè)備設(shè)計(jì)不同的布局
  • 做法:使用限定符
  • 作用:通過配置限定符使得程序在運(yùn)行時(shí)根據(jù)當(dāng)前設(shè)備的配置(屏幕尺寸)自動(dòng)加載合適的布局資源
  • 限定符類型:
    尺寸(size)限定符
    最小寬度(Smallest-width)限定符
    布局別名
    屏幕方向(Orientation)限定符
六、尺寸(size)限定符
  • 使用場(chǎng)景:當(dāng)一款應(yīng)用顯示的內(nèi)容較多,希望進(jìn)行以下設(shè)置:
    在平板電腦和電視的屏幕(>7英寸)上:實(shí)施“雙面板”模式以同時(shí)顯示更多內(nèi)容
    在手機(jī)較小的屏幕上:使用單面板分別顯示內(nèi)容

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

res/layout-large/main.xml

來(lái)完成上述設(shè)定:讓系統(tǒng)在屏幕尺寸>7英寸時(shí)采用適配平板的雙面板布局,反之(默認(rèn)情況下)采用適配手機(jī)的單面板布局。
文件配置如下:

  • 適配手機(jī)的單面板(默認(rèn))布局: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>

請(qǐng)注意:兩個(gè)布局名稱均為main.xml,只有布局的目錄名不同:第一個(gè)布局的目錄名為:layout,第二個(gè)布局的目錄名為:layout-large,包含了尺寸限定符(large),被定義為大屏的設(shè)備(7寸以上的平板)會(huì)自動(dòng)加載包含了large限定符目錄的布局,而小屏設(shè)備會(huì)加載另一個(gè)默認(rèn)的布局。
但要注意的是,這種方式只適合Android 3.2版本之前。

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

背景:上述提到的限定符“l(fā)arge”具體是指多大呢?似乎沒有一個(gè)定量的指標(biāo),這便意味著可能沒辦法準(zhǔn)確地根據(jù)當(dāng)前設(shè)備的配置(屏幕尺寸)自動(dòng)加載合適的布局資源。
例子:比如說large同時(shí)包含著5寸和7寸,這意味著使用“l(fā)arge”限定符的話我沒辦法實(shí)現(xiàn)為5寸和7寸的平板電腦分別加載不同的布局
于是,在Android 3.2及之后版本,引入了最小寬度(Smallest-width)限定符
定義:通過指定某個(gè)最小寬度(以 dp 為單位)來(lái)精確定位屏幕從而加載不同的UI資源

  • 使用場(chǎng)景
    你需要為標(biāo)準(zhǔn) 7 英寸平板電腦匹配雙面板布局(其最小寬度為 600 dp),在手機(jī)(較小的屏幕上)匹配單面板布局

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

  • sw xxxdp,即small width的縮寫,其不區(qū)分方向,即無(wú)論是寬度還是高度,只要大于 xxxdp,就采用次此布局
  • 例子:使用了layout-sw 600dp的最小寬度限定符,即無(wú)論是寬度還是高度,只要大于600dp,就采用layout-sw 600dp目錄下的布局
    代碼展示:
  • 適配手機(jī)的單面板(默認(rèn))布局: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>

對(duì)于最小寬度≥ 600 dp 的設(shè)備:系統(tǒng)會(huì)自動(dòng)加載 layout-sw600dp/main.xml(雙面板)布局,否則系統(tǒng)就會(huì)選擇 layout/main.xml(單面板)布局(這個(gè)選擇過程是Android系統(tǒng)自動(dòng)選擇的)

八、使用布局別名

設(shè)想這么一個(gè)場(chǎng)景
當(dāng)你需要同時(shí)為Android 3.2版本前和Android 3.2版本后的手機(jī)進(jìn)行屏幕尺寸適配的時(shí)候,由于尺寸限定符僅用于Android 3.2版本前,最小寬度限定符僅用于Android 3.2版本后,所以這會(huì)帶來(lái)一個(gè)問題,為了很好地進(jìn)行屏幕尺寸的適配,你需要同時(shí)維護(hù)layout-sw600dp和layout-large的兩套main.xml平板布局,如下:

  • 適配手機(jī)的單面板(默認(rèn))布局:res/layout/main.xml
  • 適配尺寸>7寸平板的雙面板布局(Android 3.2前):res/layout-large/main.xml
  • 適配尺寸>7寸平板的雙面板布局(Android 3.2后)res/layout-sw600dp/main.xml

最后的兩個(gè)文件的xml內(nèi)容是完全相同的,這會(huì)帶來(lái):文件名的重復(fù)從而帶來(lái)一些列后期維護(hù)的問題。
于是為了要解決這種重復(fù)問題,我們引入了“布局別名”
還是上面的例子,你可以定義以下布局:

  • 適配手機(jī)的單面板(默認(rèn))布局:res/layout/main.xml
  • 適配尺寸>7寸平板的雙面板布局:res/layout/main_twopanes.xml

然后加入以下兩個(gè)文件,以便進(jìn)行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>
  • 2 res/values-sw600dp/layout.xml(Android 3.2及之后的雙面板布局)
<resources>
    <item name="main" type="layout">@layout/main_twopanes</item>
</resources>
  • 最后兩個(gè)文件有著相同的內(nèi)容,但是它們并沒有真正去定義布局,它們僅僅只是將main設(shè)置成了@layout/main_twopanes的別名
  • 由于這些文件包含 large 和 sw600dp 選擇器,因此,系統(tǒng)會(huì)將此文件匹配到不同版本的>7寸平板上:
    版本低于 3.2 的平板會(huì)匹配 large的文件,. 版本高于 3.2 的平板會(huì)匹配 sw600dp的文件。

這樣兩個(gè)layout.xml都只是引用了@layout/main_twopanes,就避免了重復(fù)定義布局文件的情況

九、屏幕方向(Orientation)限定符
  • 使用場(chǎng)景:根據(jù)屏幕方向進(jìn)行布局的調(diào)整
    取以下為例子:
  • 小屏幕, 豎屏: 單面板
  • 小屏幕, 橫屏: 單面板
  • 7 英寸平板電腦,縱向:?jiǎn)蚊姘?,帶操作?/li>
  • 7 英寸平板電腦,橫向:雙面板,寬,帶操作欄
  • 10 英寸平板電腦,縱向:雙面板,窄,帶操作欄
  • 10 英寸平板電腦,橫向:雙面板,寬,帶操作欄
  • 電視,橫向:雙面板,寬,帶操作欄

方法是:

  • 先定義類別:單/雙面板、是否帶操作欄、寬/窄
    定義在 res/layout/ 目錄下的某個(gè) XML 文件中
  • 再進(jìn)行相應(yīng)的匹配:屏幕尺寸(小屏、7寸、10寸)、方向(橫、縱)
    使用布局別名進(jìn)行匹配
  • 1.在 res/layout/ 目錄下的某個(gè) 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.使用布局別名進(jìn)行相應(yīng)的匹配,(屏幕尺寸(小屏、7寸、10寸)、方向(橫、縱))res/values/layouts.xml:(默認(rèn)布局)

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

可為resources設(shè)置bool,通過獲取其值來(lái)動(dòng)態(tài)判斷目前已處在哪個(gè)適配布局

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-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>
十、"布局組件"匹配

本質(zhì):使得布局組件自適應(yīng)屏幕尺寸

  • 做法
    使用"wrap_content"、"match_parent"和"weight“來(lái)控制視圖組件的寬度和高度
    "wrap_content"
    相應(yīng)視圖的寬和高就會(huì)被設(shè)定成所需的最小尺寸以適應(yīng)視圖中的內(nèi)容
    "match_parent"(在Android API 8之前叫作"fill_parent")
    視圖的寬和高延伸至充滿整個(gè)父布局
    "weight"
    1.定義:是線性布局(Linelayout)的一個(gè)獨(dú)特比例分配屬性
    2.作用:使用此屬性設(shè)置權(quán)重,然后按照比例對(duì)界面進(jìn)行空間的分配,公式計(jì)算是:控件寬度=控件設(shè)置寬度+剩余空間所占百分比寬幅

通過使用"wrap_content"、"match_parent"和"weight"來(lái)替代硬編碼的方式定義視圖大小&位置,你的視圖要么僅僅使用了需要的那邊一點(diǎn)空間,要么就會(huì)充滿所有可用的空間,即按需占據(jù)空間大小,能讓你的布局元素充分適應(yīng)你的屏幕尺寸

十一、“圖片資源”匹配

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

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

使用場(chǎng)景:一個(gè)按鈕的背景圖片必須能夠隨著按鈕大小的改變而改變。
使用普通的圖片將無(wú)法實(shí)現(xiàn)上述功能,因?yàn)檫\(yùn)行時(shí)會(huì)均勻地拉伸或壓縮你的圖片。
解決方案:使用自動(dòng)拉伸位圖(nine-patch圖片),后綴名是.9.png,它是一種被特殊處理過的PNG圖片,設(shè)計(jì)時(shí)可以指定圖片的拉伸區(qū)域和非拉伸區(qū)域;使用時(shí),系統(tǒng)就會(huì)根據(jù)控件的大小自動(dòng)地拉伸你想要拉伸的部分

1.必須要使用.9.png后綴名,因?yàn)橄到y(tǒng)就是根據(jù)這個(gè)來(lái)區(qū)別nine-patch圖片和普通的PNG圖片的;
2.當(dāng)你需要在一個(gè)控件中使用nine-patch圖片時(shí),如

android:background="@drawable/button"

系統(tǒng)就會(huì)根據(jù)控件的大小自動(dòng)地拉伸你想要拉伸的部分

十二、”用戶界面流程“匹配

使用場(chǎng)景:我們會(huì)根據(jù)設(shè)備特點(diǎn)顯示恰當(dāng)?shù)牟季?,但是這樣做,會(huì)使得用戶界面流程可能會(huì)有所不同。
例如,如果應(yīng)用處于雙面板模式下,點(diǎn)擊左側(cè)面板上的項(xiàng)即可直接在右側(cè)面板上顯示相關(guān)內(nèi)容;而如果該應(yīng)用處于單面板模式下,點(diǎn)擊相關(guān)的內(nèi)容應(yīng)該跳轉(zhuǎn)到另外一個(gè)Activity進(jìn)行后續(xù)的處理。
本質(zhì):根據(jù)屏幕的配置來(lái)加載相應(yīng)的用戶界面流程

  • 做法
    進(jìn)行用戶界面流程的自適應(yīng)配置:
  • 1.確定當(dāng)前布局
  • 2.根據(jù)當(dāng)前布局做出響應(yīng)
    1. 重復(fù)使用其他活動(dòng)中的片段
    1. 處理屏幕配置變化

步驟1:確定當(dāng)前布局
由于每種布局的實(shí)施都會(huì)稍有不同,因此我們需要先確定當(dāng)前向用戶顯示的布局。例如,我們可以先了解用戶所處的是“單面板”模式還是“雙面板”模式。要做到這一點(diǎn),可以通過查詢指定視圖是否存在以及是否已顯示出來(lái)。

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;
}
}

這段代碼用于查詢“報(bào)道”面板是否可用,與針對(duì)具體布局的硬編碼查詢相比,這段代碼的靈活性要大得多。
步驟2:根據(jù)當(dāng)前布局做出響應(yīng)
有些操作可能會(huì)因當(dāng)前的具體布局而產(chǎn)生不同的結(jié)果。
例如,在新聞閱讀器示例中,如果用戶界面處于雙面板模式下,那么點(diǎn)擊標(biāo)題列表中的標(biāo)題就會(huì)在右側(cè)面板中打開相應(yīng)報(bào)道;但如果用戶界面處于單面板模式下,那么上述操作就會(huì)啟動(dòng)一個(gè)獨(dú)立活動(dòng):

@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:重復(fù)使用其他活動(dòng)中的片段
多屏幕設(shè)計(jì)中的重復(fù)模式是指,對(duì)于某些屏幕配置,已實(shí)施界面的一部分會(huì)用作面板;但對(duì)于其他配置,這部分就會(huì)以獨(dú)立活動(dòng)的形式存在。
例如,在新聞閱讀器示例中,對(duì)于較大的屏幕,新聞報(bào)道文本會(huì)顯示在右側(cè)面板中;但對(duì)于較小的屏幕,這些文本就會(huì)以獨(dú)立活動(dòng)的形式存在。
在類似情況下,通??梢栽诙鄠€(gè)活動(dòng)中重復(fù)使用相同的 Fragment 子類以避免代碼重復(fù)。例如,在雙面板布局中使用了 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布局中重復(fù)使用了它 :

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

步驟3:處理屏幕配置變化
如果我們使用獨(dú)立Activity實(shí)施界面的獨(dú)立部分,那么請(qǐng)注意,我們可能需要對(duì)特定配置變化(例如屏幕方向的變化)做出響應(yīng),以便保持界面的一致性。

例如,在運(yùn)行 Android 3.0 或更高版本的標(biāo)準(zhǔn) 7 英寸平板電腦上,如果新聞閱讀器示例應(yīng)用運(yùn)行在縱向模式下,就會(huì)在使用獨(dú)立活動(dòng)顯示新聞報(bào)道;但如果該應(yīng)用運(yùn)行在橫向模式下,就會(huì)使用雙面板布局。

也就是說,如果用戶處于縱向模式下且屏幕上顯示的是用于閱讀報(bào)道的活動(dòng),那么就需要在檢測(cè)到屏幕方向變化(變成橫向模式)后執(zhí)行相應(yīng)操作,即停止上述活動(dòng)并返回主活動(dòng),以便在雙面板布局中顯示相關(guān)內(nèi)容:

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;
    }
    ...
}

通過上面一系列步驟,我們就完全可以建立一個(gè)可以根據(jù)用戶界面配置進(jìn)行自適應(yīng)的應(yīng)用程序App了。

?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請(qǐng)聯(lián)系作者
【社區(qū)內(nèi)容提示】社區(qū)部分內(nèi)容疑似由AI輔助生成,瀏覽時(shí)請(qǐng)結(jié)合常識(shí)與多方信息審慎甄別。
平臺(tái)聲明:文章內(nèi)容(如有圖片或視頻亦包括在內(nèi))由作者上傳并發(fā)布,文章內(nèi)容僅代表作者本人觀點(diǎn),簡(jiǎn)書系信息發(fā)布平臺(tái),僅提供信息存儲(chǔ)服務(wù)。

相關(guān)閱讀更多精彩內(nèi)容

友情鏈接更多精彩內(nèi)容