2015年2月14日 星期六

(Android Training系列)用Fragment來建立動態UI(上)

話說從頭


  動態(Dynamic)網頁或是APP目前是一股潮流,帶著使用者前往更加自由與客製化的未來前進。在這股潮流之中,我想要為大家介紹一個Android為大家帶來的類別,它叫做Fragment(片段)。如果你想要創造一個動態且多層的介面,用Fragment Class就對啦~(至少目前我只知道它QQ)Fragment就像是Activity裡面的便利貼一樣,或者我們也可以把一個Activity加上許多的Fragments,讓我們畫面有拼貼的效果。



上圖呈現的就是這種拼貼感,讓設計人員可以因不同的螢幕大小去決定呈現畫面的方式,因此在寫介面時就能先預訂把畫面分為幾個小部分,並且讓它互相關聯。

  這些Fragments都各自有各自的佈局(layout)和生命週期(life cycle),因此我們也可以將之視為Activity中之模組化的子Activity區塊。

創造Fragment


  一個完整的Fragment除了擁有自己的生命周期之外,也能偵聽自己的傳入事件,也可以像子Activity一般可以讓你在母Activity運行時添加或移除,也可以重複地用在許多不同的Activities中。接下來的說明會教大家如何用Android內建的Support Library來建立最低可以相容於Android 1.6版的Fragment。在開始之前,必須先確定電腦裡有安裝Support Library的SDK(可以去SDK管理員確認)。

創造Fragment Class

步驟說明:
1.建立一個繼承(extend)Fragment類別的Class,接下來與建立Activity相同,可以覆寫(Override)其生命週期方法(onStop(), onResume()..等)以建立我們的程式邏輯。

2.與Activity不一樣的地方是,我們用onCreateView()來定義Fragment的佈局。事實上,這是讓一個fragment運作唯一需要的callback函數。其長相其實跟建立功能表選單Option Menu幾乎一致,都需要Inflater。(關於callback函數可以看這裡)

import android.os.Bundle;
import android.support.v4.app.Fragment;
import android.view.LayoutInflater;
import android.view.ViewGroup;
public class ArticleFragment extends Fragment {
    @Override
    public View onCreateView(LayoutInflater inflater, ViewGroup container,
        Bundle savedInstanceState) {
        // Inflate the layout for this fragment
        return inflater.inflate(R.layout.article_view, container, false);
    }
}
以上是使Fragment運行的必要callback方法。當我們有其他需要時,再去實做其他的生命週期方法就行了。關於更多關於Fragment的生命週期及callback方法:Fragments developer's guide.

用XML為Activity增加Fragment

  雖然fragments是可重複使用的模組化UI元件,但每一個屬於Fragment Class的instance都必須要與其母類別FragmentActivity相關聯(註:在API Level 11以後我們可以直接繼承Activity即可,但這樣一來就可能會失去Level 11以下的相容性)。我們可以在佈局xml檔中完成這個關聯。
  以下是用佈局檔在activity中產生兩個fragments的例子,請讀者留意螢幕尺寸修飾詞為-large。黑字是我認為可以稍加注意的地方。

路徑:res/layout-large/news_articles.xml
<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
    android:orientation="horizontal"
    android:layout_width="fill_parent"
    android:layout_height="fill_parent">

    <fragment android:name="com.example.android.fragments.HeadlinesFragment"
              android:id="@+id/headlines_fragment"
              android:layout_weight="1"
              android:layout_width="0dp"
              android:layout_height="match_parent" />

    <fragment android:name="com.example.android.fragments.ArticleFragment"
              android:id="@+id/article_fragment"
              android:layout_weight="2"
              android:layout_width="0dp"
              android:layout_height="match_parent" />
</LinearLayout>
Tip:如果你想要支援不同螢幕尺寸,請讀Supporting Different Screen Sizes
(其實也只是多弄幾個不同的layout .xml讓系統自己判斷而已)

  再把剛剛的news_article.xml定義的layout套用至activity中:
import android.os.Bundle;
import android.support.v4.app.FragmentActivity;
public class MainActivity extends FragmentActivity {
    @Override
    public void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        setContentView(R.layout.news_articles);
    }
}
留意如果你是用 v7 appcompat library版本的Support Library,將會extends ActionBarActivity,一個 FragmentActivity 的子類別。想知道更多請看Adding the Action Bar

  到此即大功告成,各位可以自己試試(按這裡可以下載Sample code,下載完成後解壓縮,打開Android Studio-->Import Project,選剛剛解壓縮後產生之資料夾)。

  注意:如果用以上介紹的方法在Activity中添加fragment,將無法在運行時移除該fragment。因為我們是直接在MainActivitiy中套用R.layout.news_articles。如果你想要讓fragments能夠來去自如,我們必須在Activity啟動後再添加,此方法將會在下篇文章中介紹。

2 則留言:

  1. 在HeadlinesFragment底下的onListItemClick裏,
    getListView().setItemChecked(position, true);這動作有什麼意義嗎?把這行隱藏才是也還是可以執行的。

    回覆刪除
    回覆
    1. 你好:
      其實你可以直接丟Google查詢比較快喔,有很多答案~
      setItemChecked(int position, boolean value)此方法會把使用者選中的Item(項目)的位置(position)設為"已選(Checked)"的狀態,在你貼的那行是設定為true。如果是在單選模式(預設)下,這個動作會引發其他的Item的"Checked"欄位被設定為false;在複選模式下則只是標記你已經選取某些項目,方便之後對這些項目處理。

      所以我的認識是:它的意義就在於你有沒有要透過標記使用者選取的項目來進行後續處理,如果沒有,這個動作就沒什麼意思了。

      有甚麼想法也歡迎再回覆討論!

      刪除