稍稍摘錄一段Fragment.java中的說明文檔。html
/** * A Fragment is a piece of an application's user interface or behavior * that can be placed in an {@link Activity}. Interaction with fragments * is done through {@link FragmentManager}, which can be obtained via * {@link Activity#getFragmentManager() Activity.getFragmentManager()} and * {@link Fragment#getFragmentManager() Fragment.getFragmentManager()}. * * <p>The Fragment class can be used many ways to achieve a wide variety of * results. In its core, it represents a particular operation or interface * that is running within a larger {@link Activity}. A Fragment is closely * tied to the Activity it is in, and can not be used apart from one. Though * Fragment defines its own lifecycle, that lifecycle is dependent on its * activity: if the activity is stopped, no fragments inside of it can be * started; when the activity is destroyed, all fragments will be destroyed. * * <p>All subclasses of Fragment must include a public empty constructor. * The framework will often re-instantiate a fragment class when needed, * in particular during state restore, and needs to be able to find this * constructor to instantiate it. If the empty constructor is not available, * a runtime exception will occur in some cases during state restore. */
其中有一點之前不知道,就是Fragment的子類必須包括一個public的空構造方法,不然可能會發生運行時異常。由於framework常常會在須要的時候(好比狀態恢復時),從新實例化Fragment類,這個時候它就會查找這個構造方法來進行實例化。java
本文主要是參考官方文檔(https://developer.android.com/guide/components/fragments.html),記錄下Fragment的使用用法。android
爲了在大小屏間更好的利用空間,更好的動態地設計UIapp
Figure 1. An example of how two UI modules defined by fragments can be combined into one activity for a tablet design, but separated for a handset design.ide
方法:建立子類繼承自Fragment(或者已存在的子類)佈局
那接下來不得不討論的就是Fragment的生命週期了。ui
方法 | 說明 |
onAttach() | called once the fragment is associated with its activity. |
onCreate() | called to do initial creation of the fragment. |
onCreateView() | creates and returns the view hierarchy associated with the fragment.this |
onActivityCreated() | tells the fragment that its activity has completed its own {@link Activity#onCreate Activity.onCreate()}.spa |
onStart() | makes the fragment visible to the user (based on its containing activity being started).設計 |
onResume() | makes the fragment interacting with the user (based on its containing activity being resumed). |
onPause() | fragment is no longer interacting with the user either because its activity is being paused or a fragment operation is modifying it in the activity. |
onStop() | fragment is no longer visible to the user either because its activity is being stopped or a fragment operation is modifying it in the activity. |
onDestroyView() | allows the fragment to clean up resources associated with its View. |
onDestroy() | called to do final cleanup of the fragment's state. |
onDetach() | called immediately prior to the fragment no longer being associated with its activity. |
Fragment通常都是將本身做爲Activity的一部分,並在Activity上顯示本身的界面。而Fragment中實例化UI界面的操做是在onCreateView方法中。下面這個例子,在onCreateView方法中加載了xml佈局文件:
public static class ExampleFragment extends Fragment { @Override public View onCreateView(LayoutInflater inflater, ViewGroup container, Bundle savedInstanceState) { // Inflate the layout for this fragment return inflater.inflate(R.layout.example_fragment, container, false); } }
onCreateView方法參數解析:
1.inflater
調用其inflate方法,最終會返回一個View對象。inflate方法參數說明:
(1)想要擴展的佈局文件的資源id
(2)ViewGroup對象,加載參數1的佈局
(3)boolean變量,代表是否將參數1的佈局添加到參數2的ViewGroup中(這裏爲false,是由於系統已經將該佈局添加到container了)
2.container
container是存放fragment的layout的ViewGroup對象。抽象麼?換種說法:Activity的佈局,暫稱爲A;Fragment的佈局,暫稱爲F。由於Fragment會嵌套到Activity中,佈局F天然須要加入到佈局A裏面。因此這裏的container就是指佈局A中用來加載佈局F的ViewGroup(好比LinearLayout)。
3.savedInstanceState
Bundle對象,保存先前的fragment的實例等數據,用來恢復。
OK,爲Fragment添加界面就結束了,總結:在onCreateView方法中,加入layout佈局文件。接下來就是添加Fragment到Activity了。
two methods:
* Declare the fragment inside the activity's layout file. (在Activity的佈局文件中添加Fragment,經過<fragment ...>標籤)
<?xml version="1.0" encoding="utf-8"?> <LinearLayout xmlns:android="http://schemas.android.com/apk/res/android" android:orientation="horizontal" android:layout_width="match_parent" android:layout_height="match_parent"> <fragment android:name="com.example.news.ArticleListFragment" android:id="@+id/list" android:layout_weight="1" android:layout_width="0dp" android:layout_height="match_parent" /> <fragment android:name="com.example.news.ArticleReaderFragment" android:id="@+id/viewer" android:layout_weight="2" android:layout_width="0dp" android:layout_height="match_parent" /> </LinearLayout>
說明:
1.<fragment>中的android:name指明瞭具體的Fragment類。當系統建立Activity的佈局時,它會檢查每一個<fragment>,並調用指明的Fragment類的onCreateView方法。當onCreateView返回一個View對象後,系統會用該View替換<fragment ...>標籤指代的內容。
Note:每一個Fragment都須要一個惟一標識符ID,用來在Activity restart 的時候恢復Fragment。有三種方法能夠提供ID:
(1)android:id a unique ID
(2)android:tag a unique string
(3)container view的ID 當(1)(2)都沒有設置的話
* Or, programmatically add the fragment to an existing ViewGroup
.(在代碼中,動態添加fragment到一個已存在的ViewGroup中)
想要在Activity中動態添加(替換/刪除)Fragment,須要FragmentTransaction
類的實例。該類的實例可經過下面方法得到:
FragmentManager fragmentManager = getFragmentManager()
FragmentTransaction fragmentTransaction = fragmentManager.beginTransaction();
添加Fragment:經過add()方法 (固然還有replace/remove等方法,具體參考官方文檔)
ExampleFragment fragment = new ExampleFragment(); fragmentTransaction.add(R.id.fragment_container, fragment); fragmentTransaction.commit();
Note:add方法的第一個參數是ViewGroup對象的ID,用來添加fragment。 當操做完成後,還須要調用FragmentTransaction::commit方法,才能奏效。
經過 add(Fragment, String)方法添加此Fragment對象。第二個參數是一個字符串,做爲tag,惟一識別該Fragment。由於沒有UI,就沒法設置android:id這類的屬性了,就只能經過這裏的tag來獲取該Fragment了(使用方法
findFragmentByTag (String tag) 來獲取Fragment)。
上面講完了」建立Fragment「 / 」加載Fragment到Activity「後,接下來講說如何管理Fragment
最開始須要先獲取 FragmentManager
類對象,經過getFragmentManager()
方法。FragmentManager對象能夠作如下幾件事情:
findFragmentById()
(for fragments that provide a UI in the activity layout) or findFragmentByTag()
(for fragments that do or don't provide a UI).popBackStack()
(simulating a Back command by the user).addOnBackStackChangedListener()
.在Activity中使用Fragment很大的特徵就是添加/刪除/替換。每次把對Fragment所做的操做被稱爲一個transaction。當commit 這個 transaction 到Activity裏時,須要另外一個類 FragmentTransaction。該類對象的獲取方法以下:
FragmentManager fragmentManager = getFragmentManager();
FragmentTransaction fragmentTransaction = fragmentManager.beginTransaction();
而後進行add()
, remove()
, and replace() 等方法。而後經過commit() 提交transaction到Activity中。
Note:當調用commit()方法前,能夠調用addToBackStack()方法將transaction添加到 back stack 中(這個back stack是由Activity管理的)。當用戶按返回鍵後,就能夠返回到Fragment執行transaction以前的狀態了。
// Create new fragment and transaction Fragment newFragment = new ExampleFragment(); FragmentTransaction transaction = getFragmentManager().beginTransaction(); // Replace whatever is in the fragment_container view with this fragment, // and add the transaction to the back stack transaction.replace(R.id.fragment_container, newFragment); transaction.addToBackStack(null); // Commit the transaction transaction.commit();
當提交」刪除Fragment「的transaction時,若是不調用addToBackStack方法,那麼這個Fragment就destroyed了,用戶就回不到以前的Fragment了。但反之,調用了addToBackStack方法後再調用commit方法,那麼 這個Fragment只是處於stopped狀態,按返回鍵後,就能夠resume該Fragment。
固然,若是你一次性對Fragment進行不少操做,而後調用一次commit()方法,系統仍是會將這一系列操做做爲一次transaction的。
Activity中:經過調用getFragmentManager方法,得到FragmentManager實例。而後經過 findFragmentById()
or findFragmentByTag()方法得到Fragment。
Fragment中:經過getActivity方法得到Activity的Context,而後經過findViewById方法得到Activity佈局中的控件。
一個很好的方法就是:
1.在Fragment中實現一個接口,包含一些回調方法。
2.讓Activity實現這個接口。
public static class FragmentA extends ListFragment { ... // Container Activity must implement this interface public interface OnArticleSelectedListener { public void onArticleSelected(Uri articleUri); } ... }
這樣,當Activity接受到回調方法時,就可以得到Fragment的信息,而且能將信息傳遞給其餘Fragment了。
那怎麼在Fragment中怎麼調用Activity的實例,爲它設置回調方法呢?能夠經過getActivity方法,可是更好的是在onAttach方法中,由於onAttach方法的參數就是Fragment所在的Activity的實例。那麼,就能夠在onAttach方法中將Activity實例強制轉換成接口對象了。
public static class FragmentA extends ListFragment { OnArticleSelectedListener mListener; ... @Override public void onAttach(Activity activity) { super.onAttach(activity); try { mListener = (OnArticleSelectedListener) activity; } catch (ClassCastException e) { throw new ClassCastException(activity.toString() + " must implement OnArticleSelectedListener"); } } ... }
--------------------------------------------------------------------------------------------------
OK,就講到這裏了,下面給出一個小例子,先上圖:
代碼結構截圖:
代碼以下:
1 package com.cb.fragmenttest; 2 3 import com.cb.listener.OnTitleItemClickListener; 4 5 import android.os.Bundle; 6 import android.app.Activity; 7 import android.app.Fragment; 8 import android.app.FragmentManager; 9 import android.util.Log; 10 11 public class MainActivity extends Activity implements OnTitleItemClickListener { 12 private static final String TAG = "MainActivity"; 13 14 @Override 15 protected void onCreate(Bundle savedInstanceState) { 16 super.onCreate(savedInstanceState); 17 setContentView(R.layout.activity_main); 18 Log.d(TAG, "onCreate"); 19 } 20 21 @Override 22 public void onItemClick(int position) { 23 Log.d(TAG, "onItemClick: position is "+position); 24 ContentFragment contentFragment = (ContentFragment) getFragmentManager().findFragmentById(R.id.content_fragment); 25 contentFragment.updateContent(position); 26 } 27 }
1 <LinearLayout xmlns:android="http://schemas.android.com/apk/res/android" 2 android:layout_width="match_parent" 3 android:layout_height="match_parent" 4 android:orientation="horizontal" > 5 6 <fragment 7 android:id="@+id/title_fragment" 8 android:name="com.cb.fragmenttest.TitleFragment" 9 android:layout_width="0dp" 10 android:layout_height="match_parent" 11 android:layout_weight="1" /> 12 13 <fragment 14 android:id="@+id/content_fragment" 15 android:name="com.cb.fragmenttest.ContentFragment" 16 android:layout_width="0dp" 17 android:layout_height="match_parent" 18 android:layout_weight="2" /> 19 20 </LinearLayout>
兩個Fragment:TitleFragment、ContentFragment
1 package com.cb.fragmenttest; 2 3 import com.cb.listener.OnTitleItemClickListener; 4 import com.cb.utils.Constants; 5 6 import android.app.Activity; 7 import android.app.Fragment; 8 import android.os.Bundle; 9 import android.util.Log; 10 import android.view.LayoutInflater; 11 import android.view.View; 12 import android.view.ViewGroup; 13 import android.widget.AdapterView; 14 import android.widget.AdapterView.OnItemClickListener; 15 import android.widget.ArrayAdapter; 16 import android.widget.ListView; 17 18 public class TitleFragment extends Fragment implements OnItemClickListener { 19 private static final String TAG = "TitleFragment"; 20 private ListView mListView; 21 private OnTitleItemClickListener mCallback; 22 23 @Override 24 public void onActivityCreated(Bundle savedInstanceState) { 25 super.onActivityCreated(savedInstanceState); 26 Log.d(TAG, "onActivityCreated"); 27 } 28 29 @Override 30 public void onAttach(Activity activity) { 31 super.onAttach(activity); 32 Log.d(TAG, "onAttach: " + activity); 33 try { 34 mCallback = (OnTitleItemClickListener) activity; 35 } catch (ClassCastException e) { 36 e.printStackTrace(); 37 } 38 } 39 40 @Override 41 public void onCreate(Bundle savedInstanceState) { 42 super.onCreate(savedInstanceState); 43 Log.d(TAG, "onCreate"); 44 } 45 46 @Override 47 public View onCreateView(LayoutInflater inflater, ViewGroup container, Bundle savedInstanceState) { 48 Log.d(TAG, "onCreateView"); 49 View view = inflater.inflate(R.layout.title_fragment, container, false); 50 mListView = (ListView) view.findViewById(R.id.list); 51 ArrayAdapter<String> adapter = new ArrayAdapter<String>(getActivity(), android.R.layout.simple_list_item_1, Constants.mTitle); 52 mListView.setAdapter(adapter); 53 mListView.setOnItemClickListener(this); 54 return view; 55 } 56 57 @Override 58 public void onItemClick(AdapterView<?> arg0, View arg1, int arg2, long arg3) { 59 Log.d(TAG, "onItemClick: arg2 is "+arg2); 60 mCallback.onItemClick(arg2); 61 } 62 63 }
1 <LinearLayout xmlns:android="http://schemas.android.com/apk/res/android" 2 android:id="@+id/linear2" 3 android:layout_width="match_parent" 4 android:layout_height="match_parent" 5 android:orientation="vertical" > 6 7 <TextView 8 android:layout_width="wrap_content" 9 android:layout_height="wrap_content" 10 android:layout_gravity="center_horizontal" 11 android:text="Titles" 12 android:textColor="#0000ff" /> 13 14 <ListView 15 android:id="@+id/list" 16 android:layout_width="match_parent" 17 android:layout_height="wrap_content" /> 18 19 </LinearLayout>
1 package com.cb.fragmenttest; 2 3 import com.cb.utils.Constants; 4 5 import android.app.Activity; 6 import android.app.Fragment; 7 import android.os.Bundle; 8 import android.util.Log; 9 import android.view.LayoutInflater; 10 import android.view.View; 11 import android.view.ViewGroup; 12 import android.widget.TextView; 13 14 public class ContentFragment extends Fragment{ 15 private static final String TAG = "ContentFragment"; 16 17 private TextView mTextView; 18 19 @Override 20 public View onCreateView(LayoutInflater inflater, ViewGroup container, Bundle savedInstanceState) { 21 Log.d(TAG, "onCreateView"); 22 View view = inflater.inflate(R.layout.content_fragment, container, false); 23 mTextView = (TextView)view.findViewById(R.id.text); 24 return view; 25 } 26 27 public void updateContent(int position) { 28 Log.d(TAG, "updateContent, mContent["+position+"] is "+Constants.mContent[position]); 29 mTextView.setText(Constants.mContent[position]); 30 } 31 32 }
1 <LinearLayout xmlns:android="http://schemas.android.com/apk/res/android" 2 android:id="@+id/linear2" 3 android:layout_width="match_parent" 4 android:layout_height="match_parent" 5 android:orientation="vertical" > 6 7 <TextView 8 android:layout_width="wrap_content" 9 android:layout_height="wrap_content" 10 android:layout_gravity="center_horizontal" 11 android:text="Contents" 12 android:textColor="#0000ff" 13 android:textIsSelectable="true" /> 14 15 <ScrollView 16 android:layout_width="wrap_content" 17 android:layout_height="wrap_content"> 18 <TextView 19 android:id="@+id/text" 20 android:layout_width="wrap_content" 21 android:layout_height="wrap_content" 22 android:textSize="20sp" /> 23 </ScrollView> 24 25 </LinearLayout>
接口文件
1 package com.cb.listener; 2 3 public interface OnTitleItemClickListener { 4 void onItemClick(int position); 5 }
常量類
1 package com.cb.utils; 2 3 public class Constants { 4 5 public static final String[] mTitle = { "Monday", "Tuesday", "Wednesday" }; 6 7 public static final String[] mContent = { 8 "Monday (Listeni/ˈmʌndeɪ/ or /ˈmʌndi/) is the day of the week between Sunday and Tuesday. According to the traditional Christian, Islamic and Hebrew calendars, it is the second day of the week. But according to international standard ISO 8601 it is the first day of the week. The name of Monday is derived from Old English Mōnandæg and Middle English Monenday, which means \"moon day\".", 9 "Tuesday (Listeni/ˈtjuːzdeɪ/, /ˈtjuːzdi/, /ˈtuːzdeɪ/ or /ˈtuːzdi/) is a day of the week occurring after Monday and before Wednesday. According to some commonly used calendars (esp. in the US), it is the third day of the week, but according to international standard ISO 8601, it is the second day of the week. The English name is derived from Old English Tiwesdæg and Middle English Tewesday, meaning \"Tīw's Day\", the day of Tiw or Týr, the god of single combat, victory and heroic glory in Norse mythology. Tiw was equated with Mars in the interpretatio romana, and the name of the day is a translation of Latin dies Martis.", 10 "Wednesday (Listeni/ˈwɛdənzdeɪ/, Listeni/ˈwɛnzdeɪ/[1] or /ˈwɛnzdi/[2]) is the day of the week between Tuesday and Thursday. According to international standard ISO 8601 adopted in most western countries it is the third day of the week. In countries that use the Sunday-first convention Wednesday is defined as the fourth day of the week. It is the fourth day of the week in the Judeo-Christian calendar as well, and was defined so in the ancient Mesopotamian and biblical calendars. The name is derived from Old English Wōdnesdæg and Middle English Wednesdei, \"day of Wodanaz\", ultimately a calque of dies Mercurii \"day of Mercury\"." + 11 "Wednesday is in the middle of the common Western five-day workweek that starts on Monday and finishes on Friday." }; 12 }