Android官方开发文档Training系列课程中文版:多样屏幕之支持不同的屏幕尺寸
原文地址:http://android.xsoftlab.net/training/multiscreen/index.html
引言
Android運行于數以百計不同尺寸的設備上。范圍小到手持移動電話,大到電視設備。因此,在設計APP時應當兼顧到盡可能多的屏幕尺寸。這樣才能照顧到較多的潛在用戶。
但是僅僅考慮不同的設備類型還不夠。每一種尺寸為用戶提供了不同的可能性與挑戰,所以為了使用戶感到滿意,應用程序需要做的不單單是支持多樣的屏幕:它還必須對每種屏幕結構將用戶體驗優化到最佳。
這節課將會學習如何實現針對屏幕結構優化的用戶界面。
Note: 這節課與相關示例程序均使用的是support library。
支持不同的屏幕尺寸
這節課將會學習通過以下方式來支持不同的屏幕尺寸:
- 確保布局可以靈活的調整尺寸。
- 對不同的屏幕結構提供適當的UI布局。
- 確保在正確的屏幕中使用了正確的布局。
- 提供可以正常縮放的位圖。
使用”wrap_content”及”match_parent”
為了使布局可以靈活的適配不同的屏幕尺寸,應當對某些View組件的width,height屬性使用”wrap_content”或”match_parent”。如果使用了”wrap_content”,那么View的高寬會被設置為View內容所需的最小尺寸。然而”match_parent”會使View的高寬擴展到父布局的尺寸大小。
通過使用”wrap_content”或”match_parent”可以使View高寬擴展到View所需要的大小或者擴展到父布局的可用空間:
<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>注意示例中是如何使用”wrap_content”及”match_parent”的。這可以使布局正確的適配不同的屏幕尺寸及方向。
下圖是布局在垂直及水平方向的示例。注意View的尺寸會自動適配屏幕的高寬:
使用RelativeLayout
你可以使用LinearLayout結合”wrap_content”或”match_parent”構造相對復雜的布局。然而,LinearLayout不能夠精確的控制子View的相對關系。在LinearLayout中View只能簡單的被線性排列。如果需要調整View間的相對關系,一種較好的解決方式就是使用RelativeLayout,它允許指定View間的相對關系。下面的示例中,你可以指定一個View靠著另一個View的左邊,而另一個View的右邊則靠著屏幕的右邊。
<?xml version="1.0" encoding="utf-8"?> <RelativeLayout xmlns:android="http://schemas.android.com/apk/res/android"android:layout_width="match_parent"android:layout_height="match_parent"><TextView android:id="@+id/label"android:layout_width="match_parent"android:layout_height="wrap_content"android:text="Type here:"/><EditText android:id="@+id/entry"android:layout_width="match_parent"android:layout_height="wrap_content"android:layout_below="@id/label"/><Button android:id="@+id/ok"android:layout_width="wrap_content"android:layout_height="wrap_content"android:layout_below="@id/entry"android:layout_alignParentRight="true"android:layout_marginLeft="10dp"android:text="OK" /><Button android:layout_width="wrap_content"android:layout_height="wrap_content"android:layout_toLeftOf="@id/ok"android:layout_alignTop="@id/ok"android:text="Cancel" /> </RelativeLayout>下圖是該布局在QVGA屏幕中的顯示效果:
下圖是該布局在大屏幕中的顯示效果:
要注意雖然這些View的尺寸發生了改變,但是其它之間的相對關系還是保留了下來。
使用尺寸限定符
上面我們學習了如何利用靈活布局或者相對布局來匹配不同的屏幕,然而這對于匹配任何屏幕來說還不夠好。因此,應用程序不單單只是實現靈活的布局,還應該對不同的屏幕配置提供相應的布局。可以通過configuration qualifiers中所描述的內容學習具體細節,它可以使程序在運行時根據當前的屏幕配置來自動選擇對應的資源。
比如說,很多應用程序針對于大屏幕實現了”two pane”的模式。平板與電視大到足以同時顯示兩個面板,但是移動電話只能同時顯示其中一個。所以,要實現這種布局,項目中應當含有以下文件:
- res/layout/main.xml,單面板布局(默認):
- res/layout-large/main.xml,雙面板布局:
要注意第二個布局的目錄路徑的large標識符。這個布局會在屏幕類型為large時被采用(比如,7英寸的平板或者更大的設備)。其它布局則會被小型設備所采用。
使用最小寬度標識符
開發者會遇到的困難之一就是在3.2之前Android的設備只有”large”屏幕尺寸,這包括了Dell Streak、Galaxy Tab以及常規的7英寸平板。然而,很多應用程序希望可以在這個范圍下不同尺寸的設備中展示不同的布局,比如5英寸的設備或者7英寸的設備,甚至是所有的”large”設備都想考慮在內。這就是為什么Android會3.2的版本中引入”最小寬度(Smallest-width)”標識符的原因。
最小寬度限定符允許將最小寬度為給定的dp寬度的設備作為目標。比如說,經典的7英寸平板的最小寬度為600dp,如果希望可以在這塊屏幕上同時放置兩個面板的話,可以直接使用上面部分中所介紹的雙面板布局。不過這里則不是large尺寸標識符,而是使用sw600dp尺寸標識符,用于指明該布局運行于最小寬度為600dp的設備上。
- res/layout/main.xml,默認的單面板布局:
- res/layout-sw600dp/main.xml,雙面板布局:
這意味著只有設備的最小寬度大于或等于600dp時才會選擇layout-sw600dp/main.xml,再稍微小點的布局則會選擇layout/main.xml。
然而,以上部分在3.2之前并不會有什么效果,因為3.2之前的系統識別不出sw600dp這種尺寸標識符,所以最好還是保留large標識符,所以會有一個名為res/layout-large/main.xml的布局文件,其中的內容與res/layout-sw600dp/main.xml保持一致。下面的部分將會介紹一種技術來避免重復的布局文件。
使用布局別稱
最小寬度限定符只在Android 3.2上開始可用。因此,開發者還應當繼續使用抽象尺寸標志(small, normal, large及xlarge)來兼容較早的版本。所以,如果希望在移動電話中顯示單面板UI,在其它較大的屏幕中采用多面板UI,那么項目中應該含有以下文件:
- res/layout/main.xml:單面板布局
- res/layout-large:多面板布局
- res/layout-sw600dp:多面板布局
這后面兩個文件是完全相同的,因為其中一個是用來匹配Android 3.2的設備的,而另一個是用來匹配較早版本的設備的。
為了避免存在這種重復的文件,可以使用別名文件技術。比如,你可以定義如下布局文件:
- res/layout/main.xml,單面板布局
- res/layout/main_twopanes.xml,雙面板布局
然后添加兩個文件:
- res/values-large/layout.xml:
- res/values-sw600dp/layout.xml:
后面這兩個文件含有相同的內容,但是它們實際上并沒有定義布局。它們只是將main_twopanes的別名設置為了main而已。一旦這些文件包含了large 或sw600dp,那么所有的系統則不會再專門區分版本。
使用方向標識符
有些布局在垂直及水平方向上均表現良好。但在新聞閱讀示例APP中,針對每一種屏幕尺寸與方向均專門定義了布局:
- small screen, portrait: 單面板,帶Logo
- small screen, landscape : 單面板,帶Logo
- 7” tablet, portrait : 單面板,帶ActionBar
- 7” tablet, landscape : 多面板,帶ActionBar
- 10” tablet, portrait : 多窄面板,帶ActionBar
- 10” tablet, landscape : 多面板,帶ActionBar
- TV, landscape : 多面板,帶ActionBar
上面所有的布局文件都被放置在res/layout/目錄下。為了使每一種布局與相關的屏幕配置產生關聯,App使用布局別名的方式來匹配每一項配置:
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>以上對所有可能的布局均作了定義,它們會與相關的屏幕配置產生映射關系:
res/values/layouts.xml:
res/values-sw600dp-land/layouts.xml:
<resources><item name="main_layout" type="layout">@layout/twopanes</item><bool name="has_two_panes">true</bool> </resources>res/values-sw600dp-port/layouts.xml:
<resources><item name="main_layout" type="layout">@layout/onepane</item><bool name="has_two_panes">false</bool> </resources>res/values-large-land/layouts.xml:
<resources><item name="main_layout" type="layout">@layout/twopanes</item><bool name="has_two_panes">true</bool> </resources>res/values-large-port/layouts.xml:
<resources><item name="main_layout" type="layout">@layout/twopanes_narrow</item><bool name="has_two_panes">true</bool> </resources>使用九宮格位圖
支持不同的屏幕尺寸同樣意味著圖片資源同樣也需要自動適配不同的尺寸。比如,一張按鈕的背景圖必須匹配按鈕的形狀。
如果要將一張簡圖片應用在組件中,必須敏銳的意識到結果可能不是想象中那樣,因為在運行時將會拉伸或者壓縮圖片。解決辦法就是使用九宮格位圖,它是一種特殊的PNG格式的文件,它內部指明了哪部分區域可以被拉伸,哪部分不可以。
因此,在設計位圖時應當首先選用九宮格。為了將位圖轉化為九宮格位圖,你可以從一張有規律的圖片開始(下圖被放大了4倍)。
然后通過draw9patch工具將該圖片打開,該工具位于tools/目錄下,它可以用來標記哪塊區域可以被拉伸。拉伸標記位于圖片的左邊和頂部。你也可以通過在右邊及底部繪點的方式來定義內容區域,如下圖所示:
注意邊上那些黑色的像素點。左邊和頂部的點指明了圖像可以被拉伸的區域,右邊和頂部的點指明了內容區域。
最后還要注意.9.png的擴展名。必須使用該擴展名,因為這是框架將其與普通圖片區分的一種方式。
當在使用這張圖片作為背景時,框架會將圖片拉伸以適應按鈕的尺寸,如下圖所示:
總結
以上是生活随笔為你收集整理的Android官方开发文档Training系列课程中文版:多样屏幕之支持不同的屏幕尺寸的全部內容,希望文章能夠幫你解決所遇到的問題。
- 上一篇: Tensorflow2.0模型构建与训练
- 下一篇: 基于法律罪行知识图谱的智能预判与客服问答