一、Fragment的設計哲學
Android在3.0中引入了fragments的概念,主要目的是用在大屏幕設備上——例如平板電腦上,支持更加動態和靈活的UI設計。平板電腦的屏幕比手機的大得多,有更多的空間來放更多的UI組件,並且這些組件之間會產生更多的交互。
針對屏幕尺寸的差距,很多情況下,都是先針對手機開發一套App,然后拷貝一份,修改布局以適應平板神馬超級大屏的。難道無法做到一個App可以同時適應手機和平板么,當然了,必須有啊。Fragment的出現就是為了解決這樣的問題。你可以把Fragment當成Activity的一個界面的一個組成部分,甚至Activity的界面可以完全有不同的Fragment組成,更帥氣的是Fragment擁有自己的生命周期和接收、處理用戶的事件,這樣就不必在Activity寫一堆控件的事件處理的代碼了。更為重要的是,你可以動態的添加、替換和移除某個Fragment。
下圖是文檔中給出的一個Fragment分別對應手機與平板間不同情況的處理圖:
PS:簡單的新聞瀏覽頁面,使用兩個Fragment分別顯示新聞列表與新聞內容;
Fragment在應用中應當是一個模塊化和可重用的組件,因為Fragment定義了他自己的布局,以及通過使用他自己的生命周期回調方法定義了他自己的行為,可以將Fragment包含到多個Activity中。
二、靜態加載Fragment
Fragment知識概要:
-
- Fragment 可以作為Activity界面的一部分組成出現(不能單獨使用)
- 可以在一個Activity中同時出現多個fragment,並且一個fragment也可以在多個activity中使用
- 在activity運行過程中,可以添加、一處或替換fragment
- fragment可以響應自己的輸入事件,並且有自己的生命周期,他們的生命周期會受宿主activity的生命周期影響,比如Activity被destory銷毀了,他也會跟着銷毀
onCreateView()方法
Fragment第一次繪制它的用戶界面的時候,系統會調用此方法,未來繪制Fragment的UI,此方法必須返回一個View,如果不顯示UI,返回NULL即可。
在activity的layout文件中聲明Fragment,需要特別注意的是<fragment>中的android: name 屬性指定了在layout中實例化的Fragment類
標識Fragment的方法:
android:id 屬性提供一個唯一的ID
android:tag 屬性提供一個唯一字符串
1、fragment布局文件,顯示fragment顯示內容控件

1 <?xml version="1.0" encoding="utf-8"?> 2 <LinearLayout xmlns:android="http://schemas.android.com/apk/res/android" 3 android:layout_width="match_parent" 4 android:layout_height="match_parent" 5 android:orientation="vertical" > 6 7 <TextView 8 android:id="@+id/text" 9 android:layout_width="wrap_content" 10 android:layout_height="wrap_content" 11 ></TextView> 12 13 <Button 14 android:id="@+id/button" 15 android:layout_width="wrap_content" 16 android:layout_height="wrap_content" 17 18 /> 19 </LinearLayout>
2、自定義一個Fragment類,需要繼承Fragment或者他的子類,重寫onCreateView()方法 在該方法中調用:inflater.inflate()方法加載Fragment的布局文件,接着返回加載的view對象

1 package com.example.fragment; 2 3 import android.app.Fragment; 4 import android.os.Bundle; 5 import android.view.LayoutInflater; 6 import android.view.View; 7 import android.view.ViewGroup; 8 import android.widget.TextView; 9 10 public class MyFragment extends Fragment{ 11 @Override 12 public View onCreateView(LayoutInflater inflater, ViewGroup container, 13 Bundle savedInstanceState) { 14 15 //layout布局文件轉換成view對象 16 /** 17 * resource:Fragment需要加載的布局文件 18 * root:加載layout的父ViewGroup 19 * attachToRoot:flase,不反回父ViewGroup 20 */ 21 View view = inflater.inflate(R.layout.fragment, container, false); 22 TextView text = (TextView) view.findViewById(R.id.text); 23 text.setText("加載靜態Fragment"); 24 return view ; 25 26 } 27 }
3、在需要加載Fragment的Activity對應的布局文件中添加fragment的標簽, 記住,name屬性是全限定類名哦,就是要包含Fragment的包名

1 <?xml version="1.0" encoding="utf-8"?> 2 <LinearLayout xmlns:android="http://schemas.android.com/apk/res/android" 3 android:layout_width="match_parent" 4 android:layout_height="match_parent" 5 android:orientation="vertical" > 6 7 <fragment 8 android:id="@+id/fragment" 9 android:layout_width="wrap_content" 10 android:layout_height="wrap_content" 11 android:name="com.example.fragment.MyFragment" 12 /> 13 </LinearLayout>
4、 Activity在onCreate( )方法中調用setContentView()加載布局文件即可!(可添加事件)

1 import android.view.View.OnClickListener; 2 import android.widget.Button; 3 import android.widget.TextView; 4 5 public class MainActivity2 extends Activity{ 6 7 private TextView tv; 8 9 protected void onCreate(Bundle savedInstanceState) { 10 super.onCreate(savedInstanceState); 11 12 setContentView(R.layout.main2); 13 14 Button button = (Button) findViewById(R.id.button); 15 tv = (TextView) findViewById(R.id.text); 16 button.setText("改變"); 17 button.setOnClickListener(new OnClickListener() { 18 19 @Override 20 public void onClick(View v) { 21 tv.setText("TextView改變了"); 22 } 23 }); 24 } 25 }
三、動態加載
撰寫代碼將Fragmen添加到一個Activity layout 中
add():添加一個Fragment(指定要添加的fragment 和 要插入的View)
與此類似的還有remove() 、替換()
處理Frament事務
格局用戶的交互情況,對Fragment進行添加、移除、替換,以及執行其他動作,提交給Activity的每一套變化被稱作一個事務
FragmentManager fragmentManager = getFragmentManager();
FragmentTransaction beginTransaction = fragmentManager.beginTransaction();
每一個事務都是同時執行一套變化,可以在一個事務中設置所有想執行的變化,包括add()、remove()、replace()、然后提交給Activity,必須調用commit()方法
如果允許用戶通過按下back按鍵返回到前一個Fragment狀態,調用commit()之前可以加入addToBackStack()方法
MyFragment2.xml

1 package com.example.fragment; 2 3 import android.app.Fragment; 4 import android.os.Bundle; 5 import android.view.LayoutInflater; 6 import android.view.View; 7 import android.view.ViewGroup; 8 import android.widget.TextView; 9 10 public class MyFragment2 extends Fragment{ 11 @Override 12 public View onCreateView(LayoutInflater inflater, ViewGroup container, 13 Bundle savedInstanceState) { 14 15 //layout布局文件轉換成view對象 16 /** 17 * resource:Fragment需要加載的布局文件 18 * root:加載layout的父ViewGroup 19 * attachToRoot:flase,不反回父ViewGroup 20 */ 21 View view = inflater.inflate(R.layout.fragment, container, false); 22 TextView text = (TextView) view.findViewById(R.id.text); 23 text.setText("動態加載Fragment"); 24 return view ; 25 26 } 27 }
主要代碼
Fragment與activity通信
生命周期圖:
探究fragment生命周期案例:

1 package com.example.fragment; 2 3 import android.app.Activity; 4 import android.app.FragmentManager; 5 import android.app.FragmentTransaction; 6 import android.os.Bundle; 7 import android.view.View; 8 import android.view.View.OnClickListener; 9 import android.widget.Button; 10 11 public class MainActivity3 extends Activity { 12 13 private Button button; 14 private boolean flag = true; 15 16 17 @Override 18 protected void onCreate(Bundle savedInstanceState) { 19 super.onCreate(savedInstanceState); 20 setContentView(R.layout.main3); 21 init(); 22 button = (Button) findViewById(R.id.button); 23 button.setOnClickListener(new OnClickListener() { 24 25 @Override 26 public void onClick(View v) { 27 FragmentManager fragmentManager = getFragmentManager(); 28 FragmentTransaction beginTransaction = fragmentManager.beginTransaction(); 29 30 if (flag) { 31 MyFragment4 frag4=new MyFragment4(); 32 beginTransaction.replace(R.id.layout, frag4); 33 flag=false; 34 } else { 35 MyFragment3 frag3=new MyFragment3(); 36 beginTransaction.replace(R.id.layout, frag3); 37 flag=true; 38 39 } 40 41 beginTransaction.commit(); 42 } 43 }); 44 45 } 46 47 private void init() { 48 FragmentManager fragmentManager = getFragmentManager(); 49 FragmentTransaction beginTransaction = fragmentManager.beginTransaction(); 50 MyFragment3 frag3 = new MyFragment3(); 51 beginTransaction.add(R.id.layout,frag3); 52 beginTransaction.commit(); 53 } 54 55 }

1 <?xml version="1.0" encoding="utf-8"?> 2 <LinearLayout xmlns:android="http://schemas.android.com/apk/res/android" 3 android:id="@+id/layout" 4 android:layout_width="match_parent" 5 android:layout_height="match_parent" 6 android:orientation="vertical" > 7 8 <Button 9 android:text="切換Fragment" 10 android:id="@+id/button" 11 android:layout_width="wrap_content" 12 android:layout_height="wrap_content" > 13 </Button> 14 15 </LinearLayout>

1 package com.example.fragment; 2 3 import android.app.Activity; 4 import android.app.Fragment; 5 import android.os.Bundle; 6 import android.util.Log; 7 import android.view.LayoutInflater; 8 import android.view.View; 9 import android.view.ViewGroup; 10 import android.widget.TextView; 11 12 public class MyFragment3 extends Fragment { 13 14 /** 15 * 每次創建都會繪制Fragment的View組件 16 */ 17 @Override 18 public View onCreateView(LayoutInflater inflater, ViewGroup container, 19 Bundle savedInstanceState) { 20 View view = inflater.inflate(R.layout.fragment2, container, false); 21 TextView tv = (TextView) view.findViewById(R.id.text); 22 tv.setText("第一個ragment"); 23 Log.i("Main", "Fragment1---onCreateView()"); 24 return view; 25 } 26 27 /** 28 * 當fragment被添加Activity時候慧回調此方法,並且只調用一次 29 */ 30 @Override 31 public void onAttach(Activity activity) { 32 super.onAttach(activity); 33 Log.i("Main", "Fragment1---onAttach()"); 34 35 } 36 37 /** 38 * 創建Fragment時回調 39 */ 40 @Override 41 public void onCreate(Bundle savedInstanceState) { 42 super.onCreate(savedInstanceState); 43 Log.i("Main", "Fragment1---onCreate()"); 44 45 } 46 47 /** 48 * 當Fragment所長的activity啟動完成后調用 49 */ 50 @Override 51 public void onActivityCreated(Bundle savedInstanceState) { 52 super.onActivityCreated(savedInstanceState); 53 Log.i("Main", "Fragment1---onActivityCreated()"); 54 55 } 56 57 /** 58 * 啟動Fragment 59 * 60 */ 61 @Override 62 public void onStart() { 63 super.onStart(); 64 Log.i("Main", "Fragment1---onStart()"); 65 66 } 67 68 /** 69 * 恢復Fragment時會被回調,調用onStart()方法后 一定調用onResume()方法 70 */ 71 @Override 72 public void onResume() { 73 super.onResume(); 74 Log.i("Main", "Fragment1---onResume()"); 75 76 } 77 78 /** 79 * 暫停Fragment 80 */ 81 @Override 82 public void onPause() { 83 super.onPause(); 84 Log.i("Main", "Fragment1---onPause()"); 85 86 } 87 88 /** 89 * 停止Fragment 90 */ 91 @Override 92 public void onStop() { 93 super.onStop(); 94 Log.i("Main", "Fragment1---onStop()"); 95 96 } 97 98 /** 99 *銷毀 Fragment所包含的View組件 100 */ 101 @Override 102 public void onDestroyView() { 103 super.onDestroyView(); 104 Log.i("Main", "Fragment1---onDestroyView()"); 105 106 } 107 108 /** 109 * 銷毀Fragment是被回調 110 */ 111 @Override 112 public void onDestroy() { 113 super.onDestroy(); 114 Log.i("Main", "Fragment1---onDestroy()"); 115 116 } 117 118 /** 119 * Fragment從Activity中刪除時會回調該方法,只會調用一次 120 */ 121 @Override 122 public void onDetach() { 123 super.onDetach(); 124 Log.i("Main", "Fragment1---onDetach()"); 125 } 126 }

測試順序:啟動fragment—>回到桌面—>回到應用—>切換fragment—>退出fragment
日志打印:
總結:
1)啟動fragment
onAttach()—>onCreat()—>onCreateView()—>onActivityCreated()—>onStart()—>onResume()
2)鎖屏
onPause()—>onStop()
3)解鎖
onStart()—>onResume()
4)切換到其他的Fragment
第一個:onPause()—>onStop()—>onDestroyView()—>onDestroy()—>onDetach
第二個:onAttach()—>onCreat()—>onCreateView()—>onActivityCreated()—>onStart()—>onResume()
Fragment 與Activity通信
1)Fragment可調用getActivity()方法獲取它所在的Activity
2)Activity可調用FragmentManager的findFragmentById()或findFragmentByTag()方法獲取Fragment
Activity——>Fragment:在Activity中創建Bundle數據包,並調用Fragment的setArguments(Bundle bundle)方法
Fragment——>Activity:需要在Fragment中定義一個內部回調接口,再讓包含該Fragment的Activity實現該回調接口。這樣Fragment可調用該回調方法將數據傳遞給Activity