1. 继承关系
java.lang.Object
|__android.app.Fragment
实现接口:ComponentCallbacks2View.OnCreateContextMenuListener
引入版本:APILevel 11
已知的子类:
DialogFragment、ListFragment、PreferenceFragment、WebViewFragment
2. 类概要
一个Fragment是应用程序的用户界面或行为的一个片段,它能够被放置在一个Activity中。通过FragmentManager对象来实现与Fragment对象的交 互,能够通过Activity.getFragmentManager()方法和Fragment.getFragmentManager()方法来获取 FragmentManager对象。
Fragment类有着 广泛的应用,它的核心是代表了一个正在较大的Activity中运行的特俗的操作或界面。Fragment对象跟它所依附的Activity对象是紧密相 关的,并且不能被分开使用。尽管Fragment对象定义了它们自己的生命周期,但是这个生命周期要依赖与它所在的Activity:如果该 Activity被终止,那么它内部的Fragment是不能被启动的;当Activity被销毁时,它内部的所有Fragment对象都会被销毁。
所有的Fragment 子类都必须包含一个公共的空的构造器。在需要的时候,Framework会经常重新实例化Fragment类,在特殊的状态恢复期间,需要能够找到这个构造器来实例化Fragment类。如果空的构造器无效,那么在状态恢复期间会导致运行时异常发生。
较旧的平台
尽管FragmentAPI是在HONEYCOMB版本中被引入的,但是通过FragmentActivity也能够在较旧的平台上使用该API。
声明周期
尽管Fragment对象的生命周期要依附于它所在的Activity对象,但是它也有自己标准的活动周期,它包含了基本的活动周期方法,如onResume(),但是同时也包含了与Activity和UI交互相关的重要方法。
显示Fragment时(跟用户交互)要调用的核心的生命周期方法如下:
1. 把Fragment对象跟Activity关联时,调用onAttach(Activity)方法;
2. Fragment对象的初始创建时,调用onCreate(Bundle)方法;
3. onCreateView(LayoutInflater,ViewGroup, Bundle)方法用于创建和返回跟Fragment关联的View对象;
4. onActivityCreate(Bundle)方法会告诉Fragment对象,它所依附的Activity对象已经完成了Activity.onCreate()方法的执行;
5. onStart()方法会让Fragment对象显示给用户(在包含该Fragment对象的Activity被启动后);
6. onResume()会让Fragment对象跟用户交互(在包含该Fragment对象的Activity被启恢复后)。
Fragment对象不再使用时,要反向回调的方法:
1. 因为Fragment对象所依附的Activity对象被挂起,或者在Activity中正在执行一个修改Fragment对象的操作,而导致Fragment对象不再跟用户交互时,系统会调用Fragment对象的onPause()方法;
2. 因为Fragment对象所依附的Activity对象被终止,或者再Activity中正在执行一个修改Fragment对象的操作,而导致Fragment对象不再显示给用户时,系统会调用Fragment对象的onStop()方法。
3. onDestroyView()方法用于清除跟Fragment中的View对象关联的资源;
4. Fragment对象的状态被最终清理完成之后,要调用onDestroy()方法;
5. 在Fragment对象不再跟它依附的Activity关联的时候,onDetach()方法会立即被调用。
布局
Fragment对象能够被用于应用程序的布局,它会让代码的模块化更好,并且针对Fragment所运行的屏幕,让用户界面的调整更加容易。例如,一个简单的由项目列表和项目明细表示所组成的程序。
一个Activity的布局XML能够包含要嵌入到布局内部的Fragment实例的<fragment>标签。例如,下例中在布局中嵌入了一个Fragment对象:
<FrameLayout xmlns:android="http://schemas.android.com/apk/res/android"
android:layout_width="match_parent"android:layout_height="match_parent">
<fragmentclass="com.example.android.apis.app.FragmentLayout$TitlesFragment"
android:id="@+id/titles"
android:layout_width="match_parent"android:layout_height="match_parent"/>
</FrameLayout>
以下是布局被安装到Activity中的通常方法:
@Override
protected voidonCreate(BundlesavedInstanceState){
super.onCreate(savedInstanceState);
setContentView(R.layout.fragment_layout);
}
依赖ListFragment对象,要显示列表的标题是相当简单的。要注意的是,点击一个列表项的实现,会依赖当前Activity的布局,它既可以创建一个新的Fragment用于显示该项目的明细,也可以启动一个新的Activity用于显示项目的明细。
public staticclassTitlesFragmentextendsListFragment{
booleanmDualPane;
intmCurCheckPosition=0;
@Override
publicvoid onActivityCreated(BundlesavedInstanceState){
super.onActivityCreated(savedInstanceState);
// Populate list with our static array of titles.
setListAdapter(newArrayAdapter<String>(getActivity(),
android.R.layout.simple_list_item_activated_1,Shakespeare.TITLES));
// Check to see if we have a frame in which to embed the details
// fragment directly in the containing UI.
ViewdetailsFrame= getActivity().findViewById(R.id.details);
mDualPane =detailsFrame!=null&& detailsFrame.getVisibility()==View.VISIBLE;
if(savedInstanceState!=null){
// Restore last state for checked position.
mCurCheckPosition = savedInstanceState.getInt("curChoice",0);
}
if(mDualPane){
// In dual-pane mode, the list viewhighlights the selected item.
getListView().setChoiceMode(ListView.CHOICE_MODE_SINGLE);
// Make sure our UI is in the correct state.
showDetails(mCurCheckPosition);
}
}
@Override
publicvoid onSaveInstanceState(BundleoutState){
super.onSaveInstanceState(outState);
outState.putInt("curChoice", mCurCheckPosition);
}
@Override
publicvoid onListItemClick(ListView l,View v, int position,long id) {
showDetails(position);
}
/**
* Helper function to show the details of a selected item, eitherby
* displaying a fragment in-place in the current UI, or starting a
* whole new activity in which it is displayed.
*/
voidshowDetails(intindex){
mCurCheckPosition = index;
if(mDualPane){
// We can display everything in-place withfragments, so update
// the list to highlight the selected itemand show the data.
getListView().setItemChecked(index,true);
// Check what fragment is currently shown,replace if needed.
DetailsFragment details=(DetailsFragment)
getFragmentManager().findFragmentById(R.id.details);
if(details==null || details.getShownIndex()!= index){
// Make new fragment toshow this selection.
details =DetailsFragment.newInstance(index);
// Execute a transaction,replacing any existing fragment
// with this one insidethe frame.
FragmentTransaction ft=getFragmentManager().beginTransaction();
ft.replace(R.id.details, details);
ft.setTransition(FragmentTransaction.TRANSIT_FRAGMENT_FADE);
ft.commit();
}
}else{
// Otherwise we need to launch a newactivity to display
// the dialog fragment with selected text.
Intent intent=newIntent();
intent.setClass(getActivity(),DetailsActivity.class);
intent.putExtra("index",index);
startActivity(intent);
}
}
}
明细Fragment对象只会显示所选项目的详细文本字符串,它是基于内置在应用中的一个字符数组的索引来获取的:
public staticclassDetailsFragmentextendsFragment{
/**
* Create a new instance of DetailsFragment, initialized to
* show the text at 'index'.
*/
publicstaticDetailsFragmentnewInstance(intindex){
DetailsFragmentf=new DetailsFragment();
// Supply index input as an argument.
Bundleargs=newBundle();
args.putInt("index", index);
f.setArguments(args);
returnf;
}
publicint getShownIndex(){
returngetArguments().getInt("index",0);
}
@Override
publicView onCreateView(LayoutInflater inflater,ViewGroup container,
Bundle savedInstanceState){
if(container==null){
// We have different layouts, and in one ofthem this
// fragment's containing frame doesn'texist. The fragment
// may still be created from its savedstate, but there is
// no reason to try to create its viewhierarchy because it
// won't be displayed. Note this isnot needed -- we could
// just run the code below, where we wouldcreate and return
// the view hierarchy; it would just neverbe used.
returnnull;
}
ScrollViewscroller=newScrollView(getActivity());
TextViewtext=newTextView(getActivity());
intpadding=(int)TypedValue.applyDimension(TypedValue.COMPLEX_UNIT_DIP,
4, getActivity().getResources().getDisplayMetrics());
text.setPadding(padding,padding, padding,padding);
scroller.addView(text);
text.setText(Shakespeare.DIALOGUE[getShownIndex()]);
returnscroller;
}
}
在用户点击标题的情况下,在当前的Activity中没有明细容器,因此标题Fragment的点击事件代码会启动一个新的显示明细Fragment的Activity:
public staticclassDetailsActivityextendsActivity{
@Override
protectedvoid onCreate(Bundle savedInstanceState){
super.onCreate(savedInstanceState);
if(getResources().getConfiguration().orientation
==Configuration.ORIENTATION_LANDSCAPE){
// If the screen is now in landscape mode,we can show the
// dialog in-line with the list so we don'tneed this activity.
finish();
return;
}
if(savedInstanceState==null){
// During initial setup, plug in the detailsfragment.
DetailsFragment details=newDetailsFragment();
details.setArguments(getIntent().getExtras());
getFragmentManager().beginTransaction().add(android.R.id.content,details).commit();
}
}
}
但是,屏幕可能足够显示标题列表和当前所选标题相关的明细。对于在横向屏幕上这样的布局,可以被放置在layout-land下面:
<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
android:orientation="horizontal"
android:layout_width="match_parent"android:layout_height="match_parent">
<fragmentclass="com.example.android.apis.app.FragmentLayout$TitlesFragment"
android:id="@+id/titles"android:layout_weight="1"
android:layout_width="0px"android:layout_height="match_parent"/>
<FrameLayoutandroid:id="@+id/details"android:layout_weight="1"
android:layout_width="0px"android:layout_height="match_parent"
android:background="?android:attr/detailsElementBackground"/>
</LinearLayout>
要注意的是,以上代码是如何调整这种可选的UI流的:标题Fragment对象被嵌入到该Activity内部的明细Fragment对象中,并且如果Fragment对象运行在一个有显示明细空间的配置环境中,那么明细Activity会由它自己来完成。
当由于配置的改变而导致Activity所持有的这些Fragment对象重启的时候,它们新的Fragment实例可以使用与之前所使用的布局不同的布局。在这种情况中,之前所有的Fragment对象依然会被实例化,并运行在新的实例中。但是任何不在跟<fragment>关联的View对象将不会再被创建,并且重isInLayout()方法中返回false。
在把Fragment的View对象绑定到父容器的时候,<fragment>标签的属性被用于控制提供给LayoutParams对象的信息,它们能够作为Fragment对象中的onInflate(Activity, AttributeSet, Bundle)方法的参数来解析。
正在实例化的Fragment对象必须要有某些类型唯一标识,以便在它的父Activity在销毁并重建的时候,能够被重新关联到之前的实例。可以使用以下方法来实现这种关联:
1. 如果没有明确的指定,则使用容器的ViewID来标识;
2. 使用<fragment>元素的android:tag属性,给Fragment对象元素提供一个特定的标签名称;
3. 使用<fragment>元素的android:id属性,给Fragment对象的元素提供一个特定的标识。
回退堆栈
在Fragment中被编辑的事务能够放在它自己的Activity中回退堆栈内。当用户在该Activity中按下返回按钮时,在回退堆栈中的任何事务在Activity自己被结束之前会被弹出堆栈。
例如,实例化一个带有整数参数的简单的Fragment对象,并且把这个整数显示在它的UI的一个TextView中:
publicstaticclassCountingFragmentextendsFragment{
int mNum;
/**
* Create a new instance of CountingFragment, providing"num"
* as an argument.
*/
staticCountingFragment newInstance(int num){
CountingFragment f=newCountingFragment();
// Supply num input as an argument.
Bundle args=newBundle();
args.putInt("num", num);
f.setArguments(args);
return f;
}
/**
* When creating, retrieve this instance's number from itsarguments.
*/
@Override
publicvoid onCreate(Bundle savedInstanceState){
super.onCreate(savedInstanceState);
mNum = getArguments()!=null? getArguments().getInt("num"):1;
}
/**
* The Fragment's UI is just a simple text view showing its
* instance number.
*/
@Override
publicView onCreateView(LayoutInflater inflater,ViewGroup container,
Bundle savedInstanceState){
View v= inflater.inflate(R.layout.hello_world, container,false);
View tv= v.findViewById(R.id.text);
((TextView)tv).setText("Fragment #"+ mNum);
tv.setBackgroundDrawable(getResources().getDrawable(android.R.drawable.gallery_thumb));
return v;
}
}
用下面的方法创建一个新的Fragment实例,用它来替换当前被显示的Fragment实例,并把这种改变发布到回退堆栈上:
voidaddFragmentToStack(){
mStackLevel++;
// Instantiate a new fragment.
Fragment newFragment=CountingFragment.newInstance(mStackLevel);
// Add the fragment to the activity, pushing this transaction
// on to the back stack.
FragmentTransaction ft= getFragmentManager().beginTransaction();
ft.replace(R.id.simple_fragment, newFragment);
ft.setTransition(FragmentTransaction.TRANSIT_FRAGMENT_OPEN);
ft.addToBackStack(null);
ft.commit();
}
每次调用上面这个方法之后,就会在堆栈上增加一个新的实体,并且按下回退键时,会把它从堆栈中弹出,并给用户返回之前的Activity状态。
类说明
1. 嵌套类
classFragment.InstantiationException
当有一个实例化错误时,会通过instantiate(Context, String, Bundle)方法抛出这个异常类。
classFragment.SavedState
通过FragmentManager.saveFragmentInstanceState(Fragment)方法从一个Fragment对象实例中获取要保存的状态信息。
2. 继承的常量
来自接口:android.content.ComponentCallbacks2
3. Public构造器和方法
public Fragment()
默认构造器。每个Fragment类都必须有一个空的构造器,以便在恢复Activity状态时能够用它能够来实例化Fragment对象。强烈的推荐Fragment的 任何子类不要有带有参数的其他构造器,因为这些构造器在Fragment被重新实例化时不会被调用,相反,能够通过调用setArguments(Bundle)方法把参数提供给调用者,并且随后可以通过Fragment的getArguments()方法来获取。
通常,应用程序不应该实 现这个构造器。在该Fragment对象首次准备运行的地方,使用onAttach(Activity)方法,将Fragment对象跟Activity 对象关联到一起。某些应用程序还可能想要实现onInflate(Activity, AttributeSet, Bundle)方法,来从布局资源中获取属性,但是因为Fragment对象被绑定到Activity上,就应该小心的使用这种方法。
public void dump(String prefix, FileDescriptor fd, PrintWriter writer,String[] args)
该方法把Fragment对象的状态打印到给定的二进制流中。
参数:
prefix:在每行前面要打印的文本。
fd:转存信息要被发送给的原始文件描述符。
writer:指定接收转存状态的PrintWriter对象,该方法返回后,这个PrintWriter对象会被关闭。
args:指定转存请求的附加参数。
public final boolean equals(Object o)
子类不同覆写这个方法。
参数:
o:指定要跟本实例比较的对象。
返回值:
true:指定的对象跟本对象相等,false:不相等。
public final Activity getActivity()
返回跟该Fragment对象关联的Activity对象。
public final Bundle getArguments()
返回该Fragment对象被实例化时所提供的参数。
public final FragmentManager getFragmentManager()
返回跟该 Fragment的Activity关联的所有的Fragment对象的管理器---FragmentManager对象。要注意的是在Fragment 对象被放置到FragmentTransaction对象中,直到被提交给与它绑定的Activity期间,该方法一直返回null值。
public final int getId()
该方法返回该Fragment对象的标识,这个标识既可以是在布局中提供的android:id属性值,也可以是在添加Fragment对象时提供的容器View ID。
public LoaderManager getLoaderManager()
返回针对该Fragment对象的LoaderManager对象,如果需要就创建它。
public final Resources getResources()
该方法返回跟Fragment对象关联的资源。
public final boolean getRetainInstance()
public final String getString(int resId)
从应用程序包的默认字符串表中返回一个本地化的字符串。
参数:
resId 要获取的字符串的资源ID。
public final String getString(int resId, Object… formatArgs)
从应用程序包的默认字符串表中返回一个被本地化的格式化字符串,用Formatter对象中format(String, Object…)方法来替换格式化的参数。
参数:
resId 指定格式化字符串的资源id
formatArgs 指定要替换的格式化参数
public final String getTag()
如果Fragment对象被指定了名称,那么使用该方法来获取Fragment对象的名称。
public final Fragment getTargetFragment()
返回由setTargetFragment(Fragment,int)方法所设置的目标Fragment对象。
public final int getTargetRequestCode()
返回由setTargetFragment(Fragment,int)方法所设置的目标请求编码。
public final CharSequence getText(int resId)
从应用程序包的默认字符串表中返回指定的本地化的、样式化的CharSequence对象。
参数:
resId 指定要获取的CharSequence对象文本的资源id。
public final getUserVisibleHint()
返回要该Fragment对象上显示给用户的提示信息的值。
Public View getView()
获取该Fragment对象布局的根View对象,如果没有布局,则返回null。
public final int hashCode()
子类不能覆盖重写该方法。返回对象的hash code。
public static Fragment instantiate(Context context, String fname)
除了没有Bundle参数以外,其他的跟instantiate(Context, String, Bundle)方法一样。
public static Fragment instantiate(Context context, String fname, Bundleargs)
用给定的类名创建一个新的Fragment对象实例。它跟调用空的构造器一样。
参数:
context 实例化该Fragment对象时要使用的上下文环境。当前只用于获取它的类装载器---ClassLoader对象。
fname 要实例化的Fragment类的名称。
args 指定要提供给Fragment对象的Bundle参数,可以使用getArguments()方法获取其中的参数。也可以是 null。
返回一个新的Fragment对象实例。
异常(Throws)
InstantiationException 如果在实例化给定的Fragment类时发生错误,就会抛出这个运行时异常,它通常是不被期望发生的。