稍稍摘录一段Fragment.java中的说明文档。
/**
* 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类,这个时候它就会查找这个构造方法来进行实例化。
本文主要是参考官方文档(https://developer.android.com/guide/components/fragments.html),记录下Fragment的使用用法。
Design Philosophy(设计理念)
为了在大小屏间更好的利用空间,更好的动态地设计UI
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.
Creating a Fragment(创建Fragment对象)
方法:创建子类继承自Fragment(或者已存在的子类)
那接下来不得不讨论的就是Fragment的生命周期了。
| 方法 | 说明 |
| 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. |
| onActivityCreated() |
tells the fragment that its activity has completed its own {@link Activity#onCreate Activity.onCreate()}. |
| 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. |
Adding a user interface(为Fragment添加用户界面)
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了。
Adding a fragment to an activity(添加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方法,才能奏效。
Adding a fragment without a UI(添加一个无UI的fragment,这个情况我没用过,官方文档说 比如进行后台操作的时候,就不用界面了)
通过 add(Fragment, String)方法添加此Fragment对象。第二个参数是一个字符串,作为tag,唯一识别该Fragment。因为没有UI,就无法设置android:id这类的属性了,就只能通过这里的tag来获取该Fragment了(使用方法 findFragmentByTag (String tag) 来获取Fragment)。
上面讲完了”创建Fragment“ / ”加载Fragment到Activity“后,接下来说说如何管理Fragment
Managing Fragments(管理Fragment)
最开始需要先获取 FragmentManager类对象,通过getFragmentManager() 方法。FragmentManager对象可以做以下几件事情:
- Get fragments that exist in the activity, with
findFragmentById()(for fragments that provide a UI in the activity layout) orfindFragmentByTag()(for fragments that do or don't provide a UI). - Pop fragments off the back stack, with
popBackStack()(simulating a Back command by the user). - Register a listener for changes to the back stack, with
addOnBackStackChangedListener().
Performing Fragment Transactions(操作Fragment)
在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的。
Communicating with the Activity(与Activity交互)
Activity中:通过调用getFragmentManager方法,获得FragmentManager实例。然后通过 findFragmentById() or findFragmentByTag()方法获得Fragment。
Fragment中:通过getActivity方法获得Activity的Context,然后通过findViewById方法获得Activity布局中的控件。
Creating event callbacks to the activity(让Activity响应Fragment的事件)
一个很好的方法就是:
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 }