Android教程網
  1. 首頁
  2. Android 技術
  3. Android 手機
  4. Android 系統教程
  5. Android 游戲

Fragment

編輯:關於Android編程

為了讓界面可以在平板上更好地展示,Android在3.0版本引入了Fragment(碎片)功能。

首先需要注意,Fragment是在3.0版本引入的,如果你使用的是3.0之前的系統,需要先導入android-support-v4的jar包才能使用Fragment功能。

①靜態創建Fragment

這是使用Fragment最簡單的一種方式,把Fragment當成普通的控件,直接寫在Activity的布局文件中。步驟:

1、繼承Fragment,重寫onCreateView決定Fragemnt的布局

2、在Activity中聲明此Fragment,就當和普通的View一樣

就是把Fragment當成普通的View一樣聲明在Activity的布局文件中,然後所有控件的事件處理等代碼都由各自的Fragment去處理,瞬間覺得Activity好干淨有木有~~代碼的可讀性、復用性以及可維護性是不是瞬間提升了~~~

新建一個項目叫做Fragments,然後在layout文件夾下新建一個名為fragment1.xml的布局文件:

可以看到,這個布局文件非常簡單,只有一個LinearLayout,裡面加入了一個TextView。我們如法炮制再新建一個fragment2.xml :

 

然後新建一個類Fragment1,這個類是繼承自Fragment的:

public class Fragment1 extends Fragment {  
  
    @Override  
    public View onCreateView(LayoutInflater inflater, ViewGroup container, Bundle savedInstanceState) {  
        return inflater.inflate(R.layout.fragment1, container, false);  
    }  
  
}  

我們可以看到,這個類也非常簡單,主要就是加載了我們剛剛寫好的fragment1.xml布局文件並返回。同樣的方法,我們再寫好Fragment2 :

public class Fragment2 extends Fragment {  
  
    @Override  
    public View onCreateView(LayoutInflater inflater, ViewGroup container, Bundle savedInstanceState) {  
        return inflater.inflate(R.layout.fragment2, container, false);  
    }  
 
} 

然後打開或新建activity_main.xml作為主Activity的布局文件,在裡面加入兩個Fragment的引用,使用android:name前綴來引用具體的Fragment:

最後打開或新建MainActivity作為程序的主Activity,裡面的代碼非常簡單,都是自動生成的:

public class MainActivity extends Activity {  
  
    @Override  
    protected void onCreate(Bundle savedInstanceState) {  
        super.onCreate(savedInstanceState);  
        setContentView(R.layout.activity_main);  
    }  
  
}  

②動態創建Fragment

動態添加Fragment主要分為4步:

1.獲取到FragmentManager,在Activity中可以直接通過getFragmentManager得到。

2.開啟一個事務,通過調用beginTransaction方法開啟。

3.向容器內加入Fragment,一般使用replace方法實現,需要傳入容器的id和Fragment的實例。

4.提交事務,調用commit方法提交

 

public class MainActivity extends Activity {  
  
    @Override  
    protected void onCreate(Bundle savedInstanceState) {  
        super.onCreate(savedInstanceState);  
        setContentView(R.layout.activity_main);  
        Display display = getWindowManager().getDefaultDisplay();  
        if (display.getWidth() > display.getHeight()) {  
            Fragment1 fragment1 = new Fragment1();  
            getFragmentManager().beginTransaction().replace(R.id.main_layout, fragment1).commit();  
        } else {  
            Fragment2 fragment2 = new Fragment2();  
            getFragmentManager().beginTransaction().replace(R.id.main_layout, fragment2).commit();  
        }  
    }  
  
} 

 

③Fragment家族常用的API

 

Fragment常用的三個類:

android.app.Fragment主要用於定義Fragment

android.app.FragmentManager主要用於在Activity中操作Fragment

android.app.FragmentTransaction 保證一些列Fragment操作的原子性,熟悉事務這個詞,一定能明白~

a、獲取FragmentManage的方式:

getFragmentManager() // v4中,getSupportFragmentManager

b、主要的操作都是FragmentTransaction的方法

FragmentTransaction transaction = fm.benginTransatcion();//開啟一個事務

transaction.add()

往Activity中添加一個Fragment

transaction.remove()

從Activity中移除一個Fragment,如果被移除的Fragment沒有添加到回退棧(回退棧後面會詳細說),這個Fragment實例將會被銷毀。

transaction.replace()

使用另一個Fragment替換當前的,實際上就是remove()然後add()的合體~

transaction.hide()

隱藏當前的Fragment,僅僅是設為不可見,並不會銷毀

transaction.show()

顯示之前隱藏的Fragment

detach()

會將view從UI中移除,和remove()不同,此時fragment的狀態依然由FragmentManager維護。

attach()

重建view視圖,附加到UI上並顯示。

transatcion.commit()//提交一個事務

注意:常用Fragment的哥們,可能會經常遇到這樣Activity狀態不一致:State loss這樣的錯誤。主要是因為:commit方法一定要在Activity.onSaveInstance()之前調用。

上述,基本是操作Fragment的所有的方式了,在一個事務開啟到提交可以進行多個的添加、移除、替換等操作。

值得注意的是:如果你喜歡使用Fragment,一定要清楚這些方法,哪個會銷毀視圖,哪個會銷毀實例,哪個僅僅只是隱藏,這樣才能更好的使用它們。

a、比如:我在FragmentA中的EditText填了一些數據,當切換到FragmentB時,如果希望回到A還能看到數據,則適合你的就是hide和show;也就是說,希望保留用戶操作的面板,你可以使用hide和show,當然了不要使勁在那new實例,進行下非null判斷。

b、再比如:我不希望保留用戶操作,你可以使用remove(),然後add();或者使用replace()這個和remove,add是相同的效果。

c、remove和detach有一點細微的區別,在不考慮回退棧的情況下,remove會銷毀整個Fragment實例,而detach則只是銷毀其視圖結構,實例並不會被銷毀。那麼二者怎麼取捨使用呢?如果你的當前Activity一直存在,那麼在不希望保留用戶操作的時候,你可以優先使用detach。


④生命周期

\

·onAttach方法:Fragment和Activity建立關聯的時候調用。

·onCreateView方法:為Fragment加載布局時調用。

·onActivityCreated方法:當Activity中的onCreate方法執行完後調用。

·onDestroyView方法:Fragment中的布局被移除時調用。

·onDetach方法:Fragment和Activity解除關聯的時候調用。

啟動Activity

\

銷毀Activity

\

可以看出針對Activity狀態的改變Fragment狀態的改變就如果入棧出棧的操作,Activity啟動的時候相應的Fragment狀態總是後執行,當我們要銷毀Activity時,Fragment的狀態總是優先銷毀。就如同進棧的時候Activity先進入,出棧的時候Activity後出,先進後出,恰好符合棧的操作。

 

⑤Activity 和 Fragment之間傳值

 

可以使用bundle進行參數傳遞、這樣在兩個Fragment跳轉的時候就可以帶上參數了、同樣也可以傳遞一個復雜的對象

ft.hide(getActivity().getSupportFragmentManager().findFragmentByTag(""));
	DemoFragment demoFragment = new DemoFragment();  
	Bundle bundle = new Bundle();  
	bundle.putString("key", "這是方法二");  
	demoFragment.setArguments(bundle);  
	ft.add(R.id.fragmentRoot, demoFragment, SEARCHPROJECT);  
	ft.commit(); 

在另外一個Fragment獲取參數的方式只需要一個語句、key是自己定義的一個標識、參數的形式只要bundle能傳遞都可以實現

String string = getArguments().getString("key"); 

Activity主動傳值到Fragment

\

 

Fragment主動傳值到Activity

 

這種方式更簡單了就是通過intent傳值

\

Activity或Fragment獲取值

例如獲取Fragment中EditText中的值,或者Activity獲取Fragment中EditText值:

\

 

在這裡所講的獲取值指的是一個Fragment在某個Activity中的情況,也就是上面所講的生命周期部分,Fragment的生命周期受Activity控制的情況,這也是最常見的傳值方式。無論是Activity獲取Fragment中的值還是Fragment獲取Activity中的值,應該都不是太難,因為一個Fragment一定屬於這個Activity了,所以在Fragment中可以通過getActivity()就獲取到了Activity,再通過Activity中的UI控件或方法得到所要的值都是一件很簡單的事。

同樣的道理,Activity獲取某一個Fragment中值也同上面說的一樣,既然Activity已經有了這個Fragment的對象,想拿到你控件或方法值都輕而易舉了。

回調函數傳值

簡單的舉個例子,在Activity中獲取Fragment中某個控件的值:

\

回調函數解釋

回調函數透徹理解Java

Android學習筆記之java中的回調函數

程序員A寫了一段程序(程序a),其中預留有回調函數接口,並封裝好了該程序。程序員B要讓a調用自己的程序b中的一個方法,於是,他通過a中的接口回調自己b中的方法。目的達到。在C/C++中,要用回調函數,被調函數需要告訴調用者自己的指針地址,但在JAVA中沒有指針,怎麼辦?我們可以通過接口(interface)來實現定義回調函數。

⑥管理Fragment回退棧

類似與Android系統為Activity維護一個任務棧,我們也可以通過Activity維護一個回退棧來保存每次Fragment事務發生的變化。如果你將Fragment任務添加到回退棧,當用戶點擊後退按鈕時,將看到上一次的保存的Fragment。一旦Fragment完全從後退棧中彈出,用戶再次點擊後退鍵,則退出當前Activity。

如何添加一個Fragment事務到回退棧:

FragmentTransaction.addToBackStack(String)

 

如何處理運行時配置發生變化

 

當屏幕發生旋轉,Activity發生重新啟動,默認的Activity中的Fragment也會跟著Activity重新創建;這樣造成當旋轉的時候,本身存在的Fragment會重新啟動,然後當執行Activity的onCreate時,又會再次實例化一個新的Fragment,這就是出現的原因。

那麼如何解決呢:

其實通過檢查onCreate的參數Bundle savedInstanceState就可以判斷,當前是否發生Activity的重新創建:

默認的savedInstanceState會存儲一些數據,包括Fragment的實例:通過打印可以看出:

 

1.07-20 08:23:12.952: E/FragmentOne(1782): Bundle[{android:fragments=android.app.FragmentManagerState@40d0b7b8, 
android:viewHierarchyState=Bundle[{android:focusedViewId=2131230721, android:views=android.util.SparseArray@40d0af68}]}]  

 

所以,我們簡單改一下代碼,只有在savedInstanceState==null時,才進行創建Fragment實例:

 

 public class MainActivity extends Activity  {  
     private static final String TAG = "FragmentOne";  
     private FragmentOne mFOne;  
   
     @Override  
     protected void onCreate(Bundle savedInstanceState) {  
         super.onCreate(savedInstanceState);  
         requestWindowFeature(Window.FEATURE_NO_TITLE);  
         setContentView(R.layout.activity_main);  
   
         Log.e(TAG, savedInstanceState+"");  
           
         if(savedInstanceState == null)   {  
             mFOne = new FragmentOne();  
             FragmentManager fm = getFragmentManager();  
             FragmentTransaction tx = fm.beginTransaction();  
             tx.add(R.id.id_content, mFOne, "ONE");  
             tx.commit();  
         }  
     }  
 } 

 

現在無論進行多次旋轉都只會有一個Fragment實例在Activity中。

現在還存在一個問題,就是重新繪制時,Fragment發生重建,原本的數據如何保持?

其實和Activity類似,Fragment也有onSaveInstanceState的方法,在此方法中進行保存數據,然後在onCreate或者onCreateView或者onActivityCreated進行恢復都可以。

 

⑧沒有布局的Fragment的作用

 

沒有布局文件Fragment實際上是為了保存,當Activity重啟時,保存大量數據准備的

請參考博客:Android 屏幕旋轉 處理 AsyncTask 和 ProgressDialog 的最佳方案

如果重新啟動你的Activity需要恢復大量的數據,重新建立網絡連接,或者執行其他的密集型操作,這樣因為配置發生變化而完全重新啟動可能會是一個慢的用戶體驗。

並且,使用系統提供的onSaveIntanceState()的回調中,使用Bundle來完全恢復你Activity的狀態是可能是不現實的(Bundle不是設計用來攜帶大量數據的(例如bitmap),並且Bundle中的數據必須能夠被序列化和反序列化),這樣會消耗大量的內存和導致配置變化緩慢。

在這樣的情況下,當你的Activity因為配置發生改變而重啟,你可以通過保持一個Fragment來緩解重新啟動帶來的負擔。這個Fragment可以包含你想要保持的有狀態的對象的引用。

當Android系統因為配置變化關閉你的Activity的時候,你的Activity中被標識保持的fragments不會被銷毀。你可以在你的Activity中添加這樣的fragements來保存有狀態的對象。

在運行時配置發生變化時,在Fragment中保存有狀態的對象
a) 繼承Fragment,聲明引用指向你的有狀態的對象
b) 當Fragment創建時調用setRetainInstance(boolean)
c) 把Fragment實例添加到Activity中
d) 當Activity重新啟動後,使用FragmentManager對Fragment進行恢復

 

解釋:一旦我們設置 setRetainInstance(true),意味著在 Activity 重繪時,我們的 Fragment 不會被重復繪制,也就是它會被“保留”。為了驗證其作用,我們發現在設置為 true 狀態時,旋轉屏幕,Fragment 依然是之前的 Fragment。而如果將它設置為默認的 false,那麼旋轉屏幕時 Fragment 會被銷毀,然後重新創建出另外一個 fragment 實例。


  1. 上一頁:
  2. 下一頁:
熱門文章
閱讀排行版
Copyright © Android教程網 All Rights Reserved