使用Fragment建立動態UI

使用Fragment建立動態UI

         爲了在Android上爲用戶提供動態的、多窗口的交互體驗,我們需要將UI組件和Activity操作封裝成模塊進行使用,使得我們可以在activity中對這些模塊進行切入切出操作。可以用Fragment來創建這些模塊,Fragment就像一個嵌套的activity,擁有自己的佈局(layout)並管理自己的生命週期。接收自己的輸入事件,可以在acvitity運行過程中添加或者移除(有點像"子activity",可以在不同的activity裏面重複使用)。一個fragment定義了自己的佈局後,它可以在activity中與其他的fragment生成不同的組合,從而爲不同的屏幕尺寸生成不同的佈局(一個小的屏幕一次也許只能一個fragment,大的屏幕則可以顯示更多)。

         一 . 創建一個Fragment類

         創建一個fragment,首先需要繼承Fragment類,然後在關鍵的生命週期方法中插入APP的邏輯,就像Activity一樣。其中一個區別是當創建Fragment的時,必須重寫onCreateView()回調方法來定義佈局。事實上,這是使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(LayoutInflaterinflater, ViewGroup container,
       Bundle savedInstanceState){
       // Inflate the layout for this fragment
       return inflater.inflate(R.layout.article_view,container,false);
    }
}


就像activity一樣,當fragment從activity添加或者移除、當activity生命週期發生變化時,fragment通過生命週期回調函數管理其狀態。例如,當activity的onPause()被調用時,它裏面的所有fragment的onPause()方法也會被觸發。

    .XMLFragment添加到Activity

fragments是可重用的,模塊化的UI組件,每個Fragment的實例都必須與一個FragmentActivity關聯。我們可以在activity的XML佈局文件中定義每一個fragment來實現這種關聯。FragmentActivity是Support Library提供的一個特殊activity ,用於處理API11版本以下的fragment。如果我們APP中的最低版本大於等於11,則可以使用普通的Activity

<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:id="@+id/headlines_fragment"
             android:layout_weight="1"
             android:layout_width="0dp"
             android:layout_height="match_parent" />
 
   <fragment
             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(BundlesavedInstanceState){
       super.onCreate(savedInstanceState);
       setContentView(R.layout.news_articles);
    }
}


如果用的是 v7 appcompat library,activity應該改爲繼承ActionBarActivity,ActionBarActivity是FragmentActivity的一個子類。當通過XML佈局文件的方式將Fragment添加進activity時,Fragment是不能被動態移除的。如果想要在用戶交互的時候把fragment切入與切出,必須在activity啓動後,再將fragment添加進activity

       .建立靈活動態的UI

比如,一個手持設備可能適合一次只有一個fragment的單面板用戶交互。而在更大屏幕尺寸的平板電腦上,我們可能更想要兩個fragment並排在一起,用來向用戶展示更多信息。兩個fragments,在同一個activity不同屏幕尺寸中用不同的配置來展示。在大屏幕上,兩個fragment被並排放置,在手持設備上,一次只放置一個fragment,所以在用戶導航中,兩個fragment必須進行替換。FragmentManager類爲在activity運行時對fragment進行添加,移除,替換等操作提供了方法,來實現動態的用戶體驗

 

         .Activity運行時動態添加Fragment

比起用<fragment>標籤在activity的佈局文件中定義fragment,我們還可以在activity運行時動態添加fragment,如果打算在activity的生命週期內替換fragment,這是必須的。爲了執行fragment的增加或者移除操作,必須通過FragmentManager創建一個FragmentTransaction對象,FragmentTransaction提供了用來增加、移除、替換以及其它一些操作的APIS。如果我們的activity允許fragment移除或者替換,我們應該在activity的onCreate()方法中添加初始化fragment(s)

運用fragment(尤其是那些在運行時添加的)的一個很重要的規則就是在佈局中必須有一個容器View,fragment的layout將會放在這個view裏面。activity中,用Support Library APIs調用 getSupportFragmentManager()方法獲取FragmentManager對象,然後調用 beginTransaction() 方法創建一個FragmentTransaction對象,然後調用add()方法添加一個fragment.可以使用同一個 FragmentTransaction進行多次fragment事務。完成這些變化操作,準備開始執行改變時,必須調用commit()方法。

下例顯示瞭如何添加一個fragment到之前的layout中

importandroid.os.Bundle;
importandroid.support.v4.app.FragmentActivity;
 
publicclassMainActivityextendsFragmentActivity {
    @Override
   publicvoidonCreate(BundlesavedInstanceState){
       super.onCreate(savedInstanceState);
        setContentView(R.layout.news_articles);
       if (findViewById(R.id.fragment_container) !=null) {
           if (savedInstanceState !=null) {
               return;
           }
           HeadlinesFragment firstFragment =newHeadlinesFragment();
           firstFragment.setArguments(getIntent().getExtras());
           getSupportFragmentManager().beginTransaction()
                   .add(R.id.fragment_container, firstFragment).commit();
       }
    }
}   


    五 .替換Fragment

替換fragment的過程類似於添加過程,只需要將add()方法替換爲 replace()方法。記住在執行fragment事務時,如移除或者替換,我們經常要適當地讓用戶可以向後導航與"撤銷"這次改變。爲了讓用戶向後導航fragment事務,我們必須在FragmentTransaction提交前調用addToBackStack()方法。

Note當移除或者替換一個fragment並把它放入返回棧中時,被移除的fragment的生命週期是stopped(不是destoryed).當用戶返回重新恢復這個fragment,它的生命週期是restarts。如果沒有把fragment放入返回棧中,那麼當它被移除或者替換時,其生命週期是destoryed。

 

下面是一個fragment替換的例子

 

ArticleFragment newFragment = new ArticleFragment();
Bundle args = new Bundle();
args.putInt(ArticleFragment.ARG_POSITION, position);
newFragment.setArguments(args);
 
FragmentTransaction transaction = getSupportFragmentManager().beginTransaction();
 
transaction.replace(R.id.fragment_container, newFragment);
transaction.addToBackStack(null);
 
transaction.commit();


addToBackStack()方法提供了一個可選的String參數爲事務指定了一個唯一的名字。除非打算用FragmentManager.BackStackEntry APIs來進行一些高級的fragments操作,這個名字不是必須的。
     

六 . Frgament 之間的交互

通常fragment之間可能會需要交互,比如基於用戶事件改變fragment的內容。所有fragment之間的交互需要通過他們關聯的activity,兩個fragment之間不應該直接交互。
1 . 定義一個接口
  爲了讓Fragmentactivity交互可以在Fragment中定義一個接口並在Activity中實現,Fragment在它們的生命週期的onAttach()方法中獲取接口的實現然後調用接口的方法與Activity交互。
public class TestFragment extends ListFragment {
OnHeadlineSelected mCallBack;
    Int position;
    public interface OnHeadlineSelected{
        public void onArticleSelected(int position);
    }
 
    public void onAttach(Activity activity){
        super.onAttach(activity);
        mCallBack = (OnHeadlineSelected)activity;
    }
 
    public void getPosition(){
        this.position = position;
    }
}


現在Fragment就可以通過OnHeadlineSelectedListener 接口實例mCallBack中的OnArticleSelected()方法與Activity傳遞消息。
比如在Fragment中點擊ListView的一個條目時調用OnArticleSelected()方法Fragment通過回調接口來傳遞事件給父Activity
2 . Activity中實現接口
public class TestActivity extends Activity implements TestFragment.OnHeadlineSelected{
    @Override
    protected void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
    }
 
    @Override
    public void onArticleSelected(int position) {
        /**
         * doing something
         */
    }
}


3 . 傳消息給Fragment
Activity中獲取Fragment實例然後直接調用getPosition()方法向Fragment傳遞消息。
 
假定上面的Activity中包含另外一個Fragment這個Fragment用來展示從上面的回調方法中返回的指定的數據,在這種情況下Activity可以把回調方法中接收到的消息傳遞給這個展示數據的Fragment
public class TestActivity extends FragmentActivity implements TestFragment.OnHeadlineSelected{
    @Override
    protected void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        setContentView(R.layout.fragment);
        TestFragment fragment = (TestFragment)getSupportFragmentManager().findFragmentById(R.id.fragment);
        if(fragment != null) {
            fragment.setPosition(0);
        } else {
            TestFragment testFragment = new TestFragment();
            Bundle bundle = new Bundle();
            bundle.putInt("position", 0);
            testFragment.setArguments(bundle);
            android.support.v4.app.FragmentTransaction transaction =
                    getSupportFragmentManager().beginTransaction();
            transaction.replace(R.id.fragment, testFragment);
            transaction.commit();
        }
 
    }
 
    @Override
    public void onArticleSelected(int position) {
        /**
         * doing something
         */
    }
}


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