Android 如何支持平板和手機

概述:

Android平臺可以運行在各種屏幕尺寸的設備上, 系統會合適地調整APP的UI來適應各種屏幕. 通常情況下, 作爲開發者我們要做的就是靈活的設計UI並使用替代資源來對不同的屏幕尺寸進行優化. 但是, 有時候可能我們會想要更進一步地提升不同屏幕尺寸的用戶體驗. 比如, 平板電腦提供了更大的屏幕空間, 讓我們可以一次展示更多的信息給用戶. 手機設備通常屏幕比較小, 我們需要獨立地顯示這些信息, 這就對信息的分佈提出了更高的要求. 而不僅僅是將它們變大這麼簡單. 單純的變大會讓設計看起來很傻, 用戶體驗也不好.

在Android 3.0中, Android引入了一組新的framework API讓我們可以更加有效地設計activity來支持大屏幕: 就是Fragment API. Fragment讓我們可以獨立地處理組件的行爲, 這就讓我們可以在平板設備上創建多面板的layout或者在手機設備上使用獨立的activity. Android 3.0還引入了ActionBar, 它在屏幕的頂端提供了一個專用UI來爲用戶識別APP以及提供操作和導航.

本文提供指導可以幫助大家創建一個使用fragment和action bar來優化平板和手機上的用戶體驗的APP.

基本原則:

這裏是一些可以讓我們在平板和手機上提供良好用戶體驗的基本原則:

l  基於fragment來設計activity, 這讓我們可以很好的重用組件.特別是在平板上的複合面板layout和手機上的單面板layout之間重用. 一個Fragment代表activity中的一個特性或者一部分用戶接口. 我們可以將fragment作爲一個activity的模塊化部分, 它擁有自己的生命週期, 並且在運行的時候我們可以靈活的添加或者刪除它們.

l  使用action bar,但要確定設計的靈活性, 以便在不同屏幕尺寸下可以調整action bar的layout. ActionBar是一個爲activity提供的UI組件, 它用於代替傳統的屏幕頂端的標題欄. 默認情況下, action bar在左邊會包含一個APP的logo, 然後是activity標題, 右邊則是訪問options menu的條目.

我們可以從options menu直接啓用菜單條目使其出現在action bar中. 還可以在action bar中添加導航功能, 比如tabs和下拉列表, 可以使用APP圖標來補充系統的返回鍵行爲, 來導航APP的home activity或者”up”鍵.

l  實現靈活的layout, 並像一個web開發者那樣思考. 一個靈活的layout設計讓我們的APP適應屏幕尺寸的變化. 並非所有的平板都是同樣尺寸的, 也不是所有的手機都是同樣的尺寸. 即便當我們爲平板和手機提供了不同的fragment, 依然很有必要設計適合的layout來調整尺寸以適應屏幕.

注意: 除了action bar, 所有其它的功能都可以在Android 3.0中實現, 此外, 甚至可以通過支持庫在Android 1.6中實現fragment的設計.

創建單面板和複合面板的layout:

最有效的在平板和手機下創建不同用戶體驗的方法是使用不同的fragment組合, 我們可以爲平板設計複合面板layout, 併爲手機設計單面板的layout. 比如, 一個在平板上運行的APP可以在左邊展示一個標題列表, 並在右邊展示完整的內容– 在左邊選擇一個標題可以讓右邊的內容更新. 但是在手機上兩種組件應該獨立的顯示在屏幕上– 選擇一個標題會使得整個屏幕開始顯示內容. 有兩種技術可以通過fragment實現這種設計:

l  多fragment, 一個activity: 不管設備尺寸如何, 只使用一個activity,但是會在運行時決定是否在一個layout中組合使用fragment(來創建一個複合面板設計)或者替換fragment(來創建一個單面板設計).

l  多fragment, 多activity: 平板上, 在一個activity中放置多個fragment;在手機上, 則使用獨立的activity來顯示每個fragment. 慄如, 當設計平板的時候使用兩個fragment在一個activity中, 爲手機也使用相同的activity, 卻替換其layout只包含第一個fragment. 當運行在手機上的時候, 我們需要轉換fragment(比如用戶選擇了一個條目), 啓動另一個activity來顯示第二個fragment.

選擇哪種方案取決於我們的設計和個人偏好. 第一種選擇要求我們在運行時確定屏幕尺寸並動態的添加每個fragment – 而不是在xml文件中聲明每個fragment – 因爲我們不能從activity中移除一個在XML layout中聲明的fragment. 當使用第一種方法的時候, 我們可能還需要在每次fragment改變的時候根據操作和導航模式更新action bar. 在某些情況下, 這些因素可能不會影響我們的設計, 所以使用一個activity並切換fragment可以工作的很好(特別是如果平板設計需要動態添加fragment). 然而其它時候, 在手機上動態切換fragment會讓代碼變得更加複雜, 因爲我們必須在activity的代碼中管理所有的fragment動作(而不是使用layout資源文件來定義fragment)並且要自己管理fragment的後退棧(而不是讓普通的activity棧來處理後退導航).

本文主要關注第二種選擇, 小屏幕時, 它會在獨立的activity中顯示每個fragment. 使用這種方法意味着我們可以使用替代layout文件爲不同的屏幕尺寸定義不同的fragment, 保持fragment代碼模塊化, 管理action bar簡單, 並讓系統處理手機上所有的返回棧工作. 下圖展示了這一行爲是如何在平板和手機上工作的:


在上圖中顯示的APP裏, Activity A是”主Activity”, 它根據屏幕尺寸使用不同的layout來同時顯示一個或者兩個fragment:

l  在一個大屏幕上, ActivityA layout包含Fragment A和Fragment B.

l  在小屏幕上, ActivityA layout只包含Fragment A. 爲了在Fragment B中顯示詳情, 必須打開Activity B. 在這裏Activity B永遠不會在一個大屏幕中顯示. 它只是用來展示Fragment B的, 所以只有在小屏幕設備上纔會出現兩個Fragment分別顯示的情況.

根據屏幕尺寸, 系統使用了不同的main.xml layout文件:

手機使用res/layout/main.xml:

<?xml version="1.0" encoding="utf-8"?>
<FrameLayout xmlns:android="http://schemas.android.com/apk/res/android"
    android:layout_width="match_parent"
    android:layout_height="match_parent">
    <!--"Fragment A" -->
    <fragment class="com.example.android.TitlesFragment"
              android:id="@+id/list_frag"
              android:layout_width="match_parent"
              android:layout_height="match_parent"/>
</FrameLayout>

平板上則使用res/layout-large/main.xml:

<?xml version="1.0" encoding="utf-8"?>
<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
  android:orientation="horizontal"
  android:layout_width="match_parent"
  android:layout_height="match_parent"
  android:id="@+id/frags">
    <!-- "Fragment A" -->
  <fragment class="com.example.android.TitlesFragment"
            android:id="@+id/list_frag"
            android:layout_width="@dimen/titles_size"
            android:layout_height="match_parent"/>
    <!-- "Fragment B" -->
  <fragment class="com.example.android.DetailsFragment"
            android:id="@+id/details_frag"
            android:layout_width="match_parent"
            android:layout_height="match_parent" />
</LinearLayout>

當用戶選擇了一個條目的時候APP的反應取決於Fragment B在layout中是否可用:

l  如果Fragment B在layout中, 那麼Activity A提醒Fragment B更新它自己.

l  如果Fragment B不在layout中, 那麼Activity A會啓動ActivityB.

想要實現該功能的話, 模塊化fragment十分重要. 特別是應該遵循這兩條原則:

l  不要從一個fragment直接操作另一個.

l  確保所有fragment關注的內容都在fragment內部而不是由它所在的Activity來處理.

爲了避免直接從一個fragment調用另一個fragment, 可以在每個fragment中定義一個回調interface, 它可以用來傳遞事件給它的Activity. 當Activity收到一個回調的時候(比如用戶選擇了一個條目), 它會根據當前的fragment配置做出合適的相應. 慄如, Activity A可以這樣處理條目選擇:

public class MainActivity extends Activity implements TitlesFragment.OnItemSelectedListener {
    ...

    /** This is a callback that the list fragment (Fragment A)
        calls when a list item is selected */
    public void onItemSelected(int position) {
        DisplayFragment displayFrag = (DisplayFragment) getFragmentManager()
                                    .findFragmentById(R.id.display_frag);
        if (displayFrag == null) {
            // DisplayFragment (Fragment B) is not in the layout (handset layout),
            // so start DisplayActivity (Activity B)
            // and pass it the info about the selected item
            Intent intent = new Intent(this, DisplayActivity.class);
            intent.putExtra("position", position);
            startActivity(intent);
        } else {
            // DisplayFragment (Fragment B) is in the layout (tablet layout),
            // so tell the fragment to update
            displayFrag.updateContent(position);
        }
    }
}

當DisplayActivity(也就是Activity B)啓動的時候, 它會讀取intent中的數據並傳遞給DisplayFragment(Fragment B). 如果Fragment B需要傳回一個結果給FragmentA(因爲Activity B是由startActivityForResult()啓動的), 那麼操作流程類似於FragmentB和Activity B之間的接口. 也就是Activity B實現一個不同的由Fragment B定義的接口. 當Activity B從fragment收到一個回調的時候, 它可以爲activity設置該結果(使用setResult())並結束自己. Activity A就可以接收結果並傳遞給Fragment A.

使用Action Bar:

Action Bar不管在平板還是手機上都是Android重要的UI組件. 爲了確保actionbar的行爲在所有尺寸的屏幕上都可以合理工作, 在使用ActionBar API的時候不應該使用複雜的自定義功能. 在使用標準的ActionBar API來設計action bar的時候, Android會在不同尺寸的屏幕下完美地處理所有的工作. 這是一些在使用action bar的時候應該遵守的重要原則:

l  當設置一個菜單條目到action條目的時候, 避免使用”always”值. 在菜單資源中, 如果希望菜單條目出現在actionbar中, 應爲android:showAsAction屬性指定”ifRoom”值. 但是我們可能會在actionview不爲溢出菜單提供默認action的時候(就是必須作爲一個action view出現的時候)使用”always”. 但是不應該多於一次或者兩次的使用”always”. 大多數其它情況下如果我們希望條目作爲action item出現的時候應該使用”ifRoom”作爲android:showAsAction的值. 強行堆積太多的action item在action bar中, 會使得UI顯得雜亂無章, 也會使得action item跟action bar中的其它組件堆疊在一起, 比如action title.

l  當向action bar添加一個帶有texttitle的action item的時候, 同樣也應該提供一個圖標, 並聲明showAsAction=”ifRoom|withText”.這樣, 如果沒有足夠的空間給title, 但是會有足夠的空間給圖標, 這時只有圖標會被使用.

l  總是爲actionitem提供一個title, 甚至不啓用”withText”的時候, 因爲用戶可以通過一個長按操作看到title – title中的文字會以一個toast的形式出現.

如果可以的話避免使用自定義導航模式. 應該使用內置的tab和和下拉導航模式 – 它們被設計可以適應不同的屏幕尺寸. 慄如, 當寬度對tabs和其他action item來說太窄的時候(比如手機豎屏的時候), tabs將會出現在action bar下面. 如果我們必須在action bar中創建一個自定義導航模式或者其它自定義view, 需要仔細的在小屏幕上測試它們.

比如, 下面演示了系統如何基於屏幕尺寸來適配action bar. 在手機上, 只有兩個action item可以顯示, 這樣剩下的menu item就得在溢出菜單上才能看到(因爲android:showAsAction被設置爲了”ifRoom”), tabs會出現在一個獨立的行中. 在平板上, 可以容納更多的action item, tabs也是.


使用splitaction bar:

當我們的APP運行在Android 4.0及更高版本中, 有一個額外的模式可以用在action bar上, 叫做”split action bar”. 當我們啓用split action bar的時候, 一個獨立的bar將會出現在屏幕底部, 當activity運行在一個較窄的屏幕上的時候, 它可以用來顯示所有的action選項. Split action bar確保了在較窄的屏幕上有合理的空間來顯示action item, 並且還爲導航和title留下了空間. 要使用split action bar, 只需要簡單的添加uiOptions=”splitActionBarWhenNarrow”到<activitiy>或者<application>中就可以了.


左邊是啓用了導航tabs的split action bar. 右邊是禁用了app icon和title的split action bar.

如果我們想要隱藏頂上的main action bar, 因爲正在使用split action bar, 所以可以調用setDisplayShowHomeEnabled(false)來禁用app圖標. 這種情況下, 在主action bar中已經沒剩下什麼東西了, 所以它會消失, 剩下的只有在上面的導航tabs和下面的actionitem(上面右圖).

使用”up”導航:

我們可以在action bar中使用APP圖標, 在合適的時候來方便用戶導航– 可以作爲一個方法返回到home activity(就像在網站中點擊了logo那樣)活着作爲一種方法來導航到前一個頁面. 儘管它看起來跟返回鍵有些相似, up導航選項爲那些直接從外部進入APP的情況提供了一個更加可預測的方法, 比如從一個notification, app widget或者其它的APP.

當在不同的設備上用不同的組合使用fragment的時候, 考慮到每種情況下的up行爲是很重要的. 比如, 在一個手機上, 我們的APP同一時刻只顯示了一個fragment, 有一個可以導航到父屏幕的up導航是很合理的, 而在大屏幕的複合面板上則不需要. 更多關於up導航的信息可以參考ActionBar.

其它設計建議:

當使用一個ListView的時候, 要考慮到如何利用有限的空間來顯示更多或者更少的信息. 也就是說, 我們可以在list adapter中爲item創建替代layout, 這樣可以在更大的屏幕中顯示更多的信息.

爲values創建替代資源文件, 比如integers, dimensions, Boolean. 爲這些資源使用sizequalifiers, 這樣我們就可以根據當前的屏幕尺寸來簡單的應用不同的layout size, font size或者啓用/禁用功能.


總結:

這裏介紹的內容是經常可以在書本上看到的知識, 主要是如何利用fragment和action bar來兼容不同尺寸的屏幕.


參考: https://developer.android.com/guide/practices/tablets-and-handsets.html

發佈了81 篇原創文章 · 獲贊 4 · 訪問量 7萬+
發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章