Android文檔Training之Fragment

要在android上創建一個動態的多窗口用戶界面,你需要封裝UI組件和activity行爲到模塊中,這樣你就能放入activity或者從activity中拿出. 你可以用Fragment類來創造這些模塊,這些module有點像一個嵌套的activity,在它裏面你也能定義它自己的佈局和管理它自己的生命週期.

當一個fragment制定了它的佈局,它可以與其他的fragment進行組合放在activity中來修改在不同屏幕尺寸下的佈局配置(小屏幕一次只顯示一個fragment,大屏幕顯示兩個或更多的fragment).

這個類向你展示了怎麼樣用fragment創建一個動態的用戶體驗和怎麼樣對不同的屏幕尺寸的設備最優化你的app的用戶體驗,fragment最低支持android1.6版本.

創建一個Fragment

你可以把fragment看作一個activity的一個擁有自己的生命週期,自己的輸入事件的模塊區,並且你可以增加到activity或者從activity中移出(可以說像是一個“子activity”,你可以在不同的activity中來使用它). 這節課我們可以學會怎樣用Support Library繼承Fragment類,這樣你的app就能在最低android1.6版本的系統上運行.

注意: 如果你決定你的app的最低SDK版本爲11或者以上,你不需要使用Support Library,而直接使用對應的API中框架裏的的Fragment類,但是要注意這裏用的是Support Library(用的是指定的包簽名的版本)中的Fragment,所以可能會與SDK中的版本API名稱稍微有點不同.

在開始之前,你必須在你的android項目中使用Support Library,如果你以前沒用過Support Library, 根據Support Library的安裝文檔來給你的項目添加V4 library支持. 然而 ,你也能使用v7 appcompat library來在你的activity中包含actionbar,它與Android2.1(API版本爲7)是適配的,它也包含Fragment的API.

創建一個Fragment類

創建一個fragment,繼承Fragment類,重寫生命週期方法來插入你的app邏輯,與你所認識的activity十分類似.
當創建fragment時你必須使用onCreateView回調來定義你的佈局(Activity中是onCreate()).事實上,這是唯一的一個你需要重寫使得fragment可以運行的回調. 例如,下面是一個簡單的制定了自己的佈局的fragment:

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

就像Activity一樣,一個fragment應該重寫其他的生命週期回調使得允許你在把它放入activity或者從activity移除時或者在activity在它的生命週期過渡時能管理它的狀態,例如:當activity的onPause()方法被調用,activity中的任何fragment也會調用它們的onPause().

更多關於fragment生命週期和回調方法在fragment開發指導上是可用的.

使用xml把Fragment加入到Activity

由於fragment是可以複用的,模塊化的UI組件,每一個fragment的實例必須與父FragmentActivity相關聯,你能通過在你的activity佈局xml文件中定義每一個fragment來把它們關聯起來.

注意:FragmentActivity是一個在Support Library中提供的一個特殊的Activity,專門用來處理系統API低於11的系統中的fragment. 如果系統最低的版本爲11或者更高,那麼你可以使用正常的Activity.

這裏是一個添加了兩個fragment到一個activity中的佈局文件(這裏考慮的是大屏幕“large”,具體資源文件夾爲res/layout-large/.).

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

然後把佈局文件應用到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,你的activity應該繼承ActionBarActivity,它是FragmentActivity的一個子類(更多信息查看ActionBar).

注意:當你通過在佈局xml文件中添加一個fragment到activity,你不能在運行時移除該fragment.如果你想在與用戶交互的時候把你的fragment拿出或者放入,你必須在activity首次啓動的時候把fragment添加到activity.下面我們會講到.

構造一個靈活的UI

當設計你的app支持大量的屏幕尺寸的時候,你應該在不同的佈局配置中複用你的fragment來使的根據可用的屏幕空間最優化用戶體驗.

例如,在手機設備對一個用戶界面一次只展示一個fragment會比較合適.相反的,你也許想要在更寬的設備上使用多個fragment來展示更多的信息給用戶.

FragmentManager類提供了方法允許你在爲了創造動態體驗在運行時增加fragment,移除fragment或者替代fragment到activity.

在運行時增加一個fragment到activity

不在activity佈局文件重定義fragment,直接在運行是添加一個fragment到activity,這在當你計劃在activity生命週期中改變fragment時是很有必要的

爲了增加或者移除fragment,你必須使用FragmentManager 類來獲得一個FragmentTransaction,FragmentTransaction有現成的API增加,移除,替換或者執行其他的fragment事務.

如果你的activity允許fragment被移除或者替換,你應該在activity的onCreate方法中增加一些最初的fragment.

處理fragment的時候要記住一條準則.當在運行是添加一個fragment,你的activity必須包含一個vie容器,在這個容器裏面你能插入fragment.

以下的layout是和先前的layout一樣只在同一時刻顯示一個fragment,爲了用另一個fragment替換掉這一個,這個activity必須佈局必須包含一個空的FrameLayout作爲Fragment容器.

注意到這裏文件名和上面的一樣,但是資源文件夾沒有large屬性,所以這個佈局文件是爲在同一時刻只能顯示一個fragment的小屏幕設備準備的.

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

        // Check that the activity is using the layout version with
        // the fragment_container FrameLayout
        if (findViewById(R.id.fragment_container) != null) {

            // However, if we're being restored from a previous state,
            // then we don't need to do anything and should return or else
            // we could end up with overlapping fragments.
            if (savedInstanceState != null) {
                return;
            }

            // Create a new Fragment to be placed in the activity layout
            HeadlinesFragment firstFragment = new HeadlinesFragment();

            // In case this activity was started with special instructions from an
            // Intent, pass the Intent's extras to the fragment as arguments
            firstFragment.setArguments(getIntent().getExtras());

            // Add the fragment to the 'fragment_container' FrameLayout
            getSupportFragmentManager().beginTransaction()
                    .add(R.id.fragment_container, firstFragment).commit();
        }
    }
}

因爲fragment已經在運行時添加到了FramLayout容器中,而不是在activity的佈局文件定義,所以這個activity可以移除fragment或者用另外一個來替換它.

用另一個Fragment來替換這個Fragment

替換一個fragment和添加一個是很相似的,但是需要調用replace()而不是add()方法.

我們要記住當你執行fragment事務時,比如替換或者移除fragment,通常允許用戶撤銷操作都是很好的用戶體驗. 爲了允許用戶回退,你必須在提交事務之前調用addToBackStack()方法.

注意:當你移除或者替代fragmtn並把這些事務添加到返回棧,被移除的fragment將會處於stopped狀態(沒有被銷燬)。如果用戶回退來複原fragment,fragmnent會調用自身的restart方法,如果你沒有把這些事務添加到返回棧,則fragment在被移除或者替換時會被直接銷燬.

Fragment替換的例子

// Create fragment and give it an argument specifying the article it should show
ArticleFragment newFragment = new ArticleFragment();
Bundle args = new Bundle();
args.putInt(ArticleFragment.ARG_POSITION, position);
newFragment.setArguments(args);

FragmentTransaction transaction = getSupportFragmentManager().beginTransaction();

// Replace whatever is in the fragment_container view with this fragment,
// and add the transaction to the back stack so the user can navigate back
transaction.replace(R.id.fragment_container, newFragment);
transaction.addToBackStack(null);

// Commit the transaction
transaction.commit();

addToBackStack() 方法有一個可選的string參數指定了事務的唯一的名稱. 這個名稱除非你計劃使用 FragmentManager.BackStackEntry API執行更進一步的fragment運算否則是不需要的.

與其他的Fragment進行通信

爲了複用Fragment UI組件,你應該做好每一個fragment使得其是自包含的模塊組件,使得它有自己的佈局和行爲,一旦你已經定義了這些可以複用的Fragments,你可以把它們同時放在一個Activity並用應用邏輯來連接他們來理解並控制整個混合的UI.

你將會常常想要一Fragment和另一個Fragment交互,例如,爲了改變根據用戶事件變化的內容,所有的Fragment到Fragment交互都是通過聯合的Activity來完成,兩個Fragment絕對不可能直接互相交互

定義一個接口

爲了允許一個Fragment與它所在的Activity交互,你可以在Fragment中定義一個接口,並且在activity中實現它,這個Fragment通過生命週期方法onAttach()獲取到接口方法,然後能調用接口方法完成與Activity的交互.

下面是一個Fragment與Activity交互的例子.

public class HeadlinesFragment extends ListFragment {
    OnHeadlineSelectedListener mCallback;

    // Container Activity must implement this interface
    public interface OnHeadlineSelectedListener {
        public void onArticleSelected(int position);
    }

    @Override
    public void onAttach(Activity activity) {
        super.onAttach(activity);

        // This makes sure that the container activity has implemented
        // the callback interface. If not, it throws an exception
        try {
            mCallback = (OnHeadlineSelectedListener) activity;
        } catch (ClassCastException e) {
            throw new ClassCastException(activity.toString()
                    + " must implement OnHeadlineSelectedListener");
        }
    }

    ...
}

現在fragment可以傳遞信息到activity通過使用OnHeadlineSelectedListener 接口中的mCallback實例來調用onArticleSelected()方法(或者接口中的其他方法).

例如,fragment中以下的方法在用戶點擊list的子item時會被調用,fragment使用接口回調來把時間傳遞到所在的Activity.

    @Override
    public void onListItemClick(ListView l, View v, int position, long id) {
        // Send the event to the host activity
        mCallback.onArticleSelected(position);
    }

重寫接口

爲了收到fragment事件回調,fragment所在的activity必須實現fragment中定義的接口

例如:下面的activity實現了上面例子中的接口

public static class MainActivity extends Activity
        implements HeadlinesFragment.OnHeadlineSelectedListener{
    ...

    public void onArticleSelected(int position) {
        // The user selected the headline of an article from the HeadlinesFragment
        // Do something here to display that article
    }
}

傳遞信息到Fragment

fragment所在的activity可以通過findFragmentById()方法獲取fragment實例,然後調用fragment中的方法來傳遞消息到fragment.

例如, 想像上面的activity也許包含另一個fragment,這個fragment用來顯示由上面的回調方法返回的數據對應的item.在這種情況下,activity能傳遞從回調方法收到的信息到另外一個將要顯示item的fragment.

public static class MainActivity extends Activity
        implements HeadlinesFragment.OnHeadlineSelectedListener{
    ...

    public void onArticleSelected(int position) {
        // The user selected the headline of an article from the HeadlinesFragment
        // Do something here to display that article

        ArticleFragment articleFrag = (ArticleFragment)
                getSupportFragmentManager().findFragmentById(R.id.article_fragment);

        if (articleFrag != null) {
            // If article frag is available, we're in two-pane layout...

            // Call a method in the ArticleFragment to update its content
            articleFrag.updateArticleView(position);
        } else {
            // Otherwise, we're in the one-pane layout and must swap frags...

            // Create fragment and give it an argument for the selected article
            ArticleFragment newFragment = new ArticleFragment();
            Bundle args = new Bundle();
            args.putInt(ArticleFragment.ARG_POSITION, position);
            newFragment.setArguments(args);

            FragmentTransaction transaction = getSupportFragmentManager().beginTransaction();

            // Replace whatever is in the fragment_container view with this fragment,
            // and add the transaction to the back stack so the user can navigate back
            transaction.replace(R.id.fragment_container, newFragment);
            transaction.addToBackStack(null);

            // Commit the transaction
            transaction.commit();
        }
    }
}
發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章