Android Fragment---概要介绍
一个Fragment代表一个行为或Activity中用户界面的一部分。你能够在一个Activity中组合使用多个Fragment来创建一个多面板的用户界面,并且可以在多个Activity中重用同个一个Fragment。你可以把一个Frament想象成一个Activity的模块,它有自己的生命周期,接受它们自己的输入事件,并且能够在Activity运行时进行添加或删除(这有些像“子Activity”,你能够在不同的Activity中重用)。
Frament必须嵌入到一个Activity中,并且Fragment的生命周期直接受到这个Activity生命周期的影响。例如,当Activity被终止时,这个Activity中的所有的Fragment也会被终止,当Activity被销毁时,这个Activity中的所有的Fragment也会被销毁。但是,当一个Activity正在运行时(也就是在Activity的恢复态时),你可以独立的维护每个Fragment,如添加或删除它们。在执行这样一个Fragment事务时,你也能够把它添加到被Activity管理的回退堆栈中---在Activity中,每个回退堆栈的入口是一个发生Fragment事务的记录。通过按回退按钮,回退堆栈允许用户返回一个Fragment事务(向后导航)。
当你添加一个Fragment作为Activity布局的一部分时,它生活在内部的Activity的ViewGroup的层次树中,并且Fragment定义了自己的View布局。你能够在通过在Activity布局文件中声明Fragment的方法把一个Fragment插入到你的Activity中,或者通过代码把Fragment添加到一个既存的ViewGroup中。但是Fragment作为Activity布局的一部分不是必须的,你也可以使用没有自己界面的Fragment,让它作为Activity的不可见的工作器。
Android Fragment---设计理念
Android在Android3.0中引入了Fragment,主要用于支持在大屏幕上进行更多的动态和灵活的UI设计,如平板电脑。因为平板电脑的屏幕比手持设备要大,因此有更多的空间用于UI组件的组合和互换。Fragment允许不需要你来管理对View层树的复杂管理的设计方式。通过把一个Activity的布局分解成不同的Fragment,你就能够在运行时编辑Activity的外观,并且在被Activity管理的回退堆栈中保留这些改变。
例如,一个新闻相关的应用在左侧能够使用一个Fragment来显示文章列表,在右侧使用另一个Fragment来显示一篇文章---这两个Fragment并列的显示在同一个Activity中,并且每个Fragment有它自己的生命周期回调方法和处理它们自己的用户输入事件的集合。这样,就不会使用一个Activity来选择文章,而在另一个Activity中阅读文章,用户可以像下图1介绍的那样(平板电脑布局),能够在同一个Activity中选择并预读文章。
图1 在平板电脑中怎样把Fragment定义的两个UI模块组合到一个Activity,而在手持设备中又是如何分开例子。
你应该把每个Fragment设计成一个模块和可复用的Activity组件。这是因为每个Fragment定义了它们自己的布局和拥有它们自己生命周期的行为,你能够在多个Activity中包含一个Fragment,因此你应该把它设计成可复用的,并且要避免一个Fragment到另一个Fragment直接操作。这尤其重要,因为一个模块化的Fragment允许你针对不同的屏幕尺寸来改变你的Fragment组合。当正在设计的应用程序要同时支持平板电脑和手持设备时,你能够在不同的布局配置中重用Fragment,借此来优化基于有效的屏幕空间的用户体验。例如,在手持设备上,当在同一个Activity中不能同时填充多个Fragment时,就需要分别给这些Fragment提供单独的UI面板。
例如,继续使用关于新闻的应用作为例子,在平板电脑上运行时,Activity A中能够被嵌入两个Fragment,而在手持设备的屏幕上,没有足够的空间来包含这两个Fragment,因此Activity A仅包含了针对文章列表的Fragment,并且当用户选择一篇文章时,它启动Activity B,它包含了第二个用于阅读文章的Fragment。这样,就像上图1说明的那样,应用程序通过使用不同的组合重用Fragment来支持平板电脑和手持设备。
关于用不同的Fragment组合来设计程序以适应不同的屏幕配置,请看支持平板电脑和手持设备(Supporting Tablets and Handsets)。
Android Fragment---创建Fragment
要创建一个Fragment,你必须创建一个Fragment的子类(或一个既存的Fragment的子类)。Fragment类的代码看上去有点象Activity,它包含了类似Activity的的回调方法,如onCreate()、onStart()、onPause()和onStop()方法。实际上,如果你正在把一个既存的Android应用程序转换成使用Fragment的应用程序,你只需简单的把Activity的回调方法的代码移到各自的Fragment的回调方法中。
通常,至少应该实现下列生命周期方法:
onCreate()
当创建Fragment时系统调用这个方法。在你的实现中,你应该初始化哪些在Fragment暂停态、终止态、和恢复态时想要保持状态的的Fragment组件。
onCreateView()
当第一次用Fragment来描画用户界面时,系统调用这个方法。要用你的Fragment来描画一个UI界面,你必须从这个方法中返回一个View,这个View是Fragment布局的根。如果Fragment没有提供UI界面,那么它返回一个null。
onPause()
在用户要离开Fragment的第一时刻系统会调用这个方法(但这并意味着Fragment要被销毁)。通常应该在这儿提交本次用户会话之外的要持久化的改变(因为用户可能不在回来)。
对于每个Fragment,大多数应用程序应该至少实现这三个方法,但是你也应该使用其它的回调方法来处理Fragment生命周期的各种状态。全部的生命周期回调方法将在随后的处理Fragment生命周期(Handing the Fragment Lifecycle)章节中进行更多的讨论。
你可能想要扩展一些子类,而不是基于Fragment类:
DialogFragment
显示一个浮动的对话框,使用这个类创建一个在Activity类中使用对话帮助器的对话框是一个好的选择,因为你能够把一个Fragment对话框纳入到Activity管理的Fragment的回退堆栈中,允许用户返回到被废止的Fragment。
ListFragment
显示一个被类似ListActivity的适配器管理的项目列表(如SimpleCursorAdapter)。它为管理一个列表View提供几个方法,如用来处理Click事件的onListItemClick()回调方法。
PreferenceFragment
显示一个引用对象的层次树列表,类似于PreferenceActivity。当给应用程序创建设置Activity时,这是有用的。
图2 Fragment的生命周期(Activity正在运行时)
Andriod Fragment---添加用户界面
通常,Fragment是作为Activity用户界面的一部分来使用的,并且它会给Activity提供自己的布局。
要给Fragment提供一个布局,你必须实现onCreateView()回调方法,系统在给Fragment描画布局的时候会调用这个方法。这个方法的实现必须返回一个View,它是Fragment布局的根。
注:如果你的的Fragment是ListFragment的子类,默认的实现是从onCreateView()方法中返回一个ListView(),因此你不需要实现它。
为了从onCreateView()方法中返回一个布局,你能够通过XML文件中的一个布局资源的定义来填充它。为了帮助你做这件事,onCreateView()方法提供了一个LayoutInflater对象。
例如,Fragment的一个子类通过example_fragment.xml文件加载一个布局:
public static class ExampleFragment extends Fragment { @Override public View onCreateView(LayoutInflater inflater, ViewGroupcontainer, Bundle savedInstanceState) { // Inflate the layout for this fragment return inflater.inflate(R.layout.example_fragment,container, false); } }
传递给onCreateView()方法的container参数是ViewGroup(来自Activity的布局)的父类,你的Fragment参数将被插入到这个容器中。如果Fragment正在被恢复,那么savedInstanceState参数就提供一个有关前一个Fragment实例数据的Bundle对象(在有关处理Fragment生命周期(Handling the Fragment Lifecycle)一节中会更多的讨论恢复状态)。
Inflate()方法需要三个参数:
1. 第一个参数是你想要填充的布局的资源ID;
2. 第二个参数ViewGroup是被填充的布局的父容器,传递container参数是至关重要的,因为在这个方法执行过程,系统要把这个父视图指定的布局参数用作被填充的布局的根视图。
3. 一个布尔值参数指示在填充期间是否要把布局绑定到ViewGroup(第二个参数)上。在这个例子中,指定false是因为系统已经把被填充的布局插入到container参数中了---如果传递true,在最终的布局中会创建一个多余ViewGroup。
你已经看到了如何创建一个提供布局的Fragment,接下来就需要把Fragment添加到Activity中。
Andriod Fragment---给Activity添加一个Fragment
通常,Fragment作为Activity整体视图层次树的一部分,被嵌入到宿主Activity的界面的一部分。有两种方法能够把Fragment添加到Activity布局中:
1. 在Activity布局文件的内部声明Fragment。
你能够像一个视图那样给Fragment指定布局属性。下例说明了给Activity指定两个Fragment的布局文件。
<?xml version="1.0"encoding="utf-8"?> <LinearLayoutxmlns:android="http://schemas.android.com/apk/res/android" android:orientation="horizontal" android:layout_width="match_parent" android:layout_height="match_parent"> <fragmentandroid:name="com.example.news.ArticleListFragment" android:id="@+id/list" android:layout_weight="1" android:layout_width="0dp" android:layout_height="match_parent"/> <fragmentandroid:name="com.example.news.ArticleReaderFragment" android:id="@+id/viewer" android:layout_weight="2" android:layout_width="0dp" android:layout_height="match_parent" /> </LinearLayout>
在<fragment>元素中的android:name属性指定了在布局中要实例化的Fragment。
当系统创建这个Activity布局时,它实例化在布局中指定的每一个Fragment,并且分别调用onCreateView(),来获取每个Fragment的布局。然后系统会在Activity布局中插入通过<fragment>元素中声明直接返回的视图。
注:每个Fragment需要一个唯一的标识,这样能够在Activity被重启时系统使用这个ID来恢复Fragment(并且你能够使用这个ID获取执行事务的Fragment,如删除)。有三种给Fragment提供ID的方法:
A. 使用android:id属性来设置唯一ID;
B. 使用android:tag属性来设置唯一的字符串;
C. 如果没有设置前面两个属性,系统会使用容器视图的ID。
2. 编程给一个既存的ViewGroup添加Fragment。
在Activity运行的任何时候,都可以把Fragment添加到Activity布局中。你只需要指定一个放置Fragment的ViewGroup。要在Activity中使用Fragment事务(如添加、删除、或替换Fragment),必须使用来自FragmentTransaction的APIs。你能够向下面例子那样从Activity中获取一个FragmentTransaction实例:
FragmentManager fragmentManager = getFragmentManager();
FragmentTransaction fragmentTransaction = fragmentManager.beginTransaction();
然后,你能够使用add()方法把Fragment添加到指定的视图中,如:
ExampleFragment fragment = new ExampleFragment(); fragmentTransaction.add(R.id.fragment_container, fragment); fragmentTransaction.commit();
传递给add()方法的第一个参数是Fragment应该被放入的ViewGroup,通过资源ID来指定这个ViewGroup,第二个参数是要添加的Fragment。
一旦FragmentTransaction对象发生了改变,就必须调用commit方法来提交改变的影响。
Android Fragment---添加一个没有UI的Fragment
上面的例子显示了怎样把Fragment作为UI的一部分添加到Activity上,但是,你也能够使用Fragment只提供一个后台行为,而没有额外的UI展现。
要添加一个没有UI的Fragment,需要在Activity中使用add(Fragment,String)(给Fragment提供一个唯一的字符串“tag”,而不是视图ID)方法来添加Fragment。但是,因为这样添加的Fragment没有跟Activity布局中的视图关联,它不接受对onCreateView()方法的调用,因此你不需要实现这个方法。
不能说提供了字符串“tag”的Fragment就是非UIFragment,因为你也可以给有UI的Fragment提供字符串“tag”,但是如果Fragment没有UI,那么只能使用字符串的方法来标识它。如果你想要从Activity获取这个Fragment,需要使用findFragmentByTag()方法。
FragmentRetainInstance.java演示了一个没有UI的使用Fragment作为后台工作器的Activity。
/* * Copyright (C) 2010 The Android Open Source Project * * Licensed under the Apache License, Version 2.0 (the"License"); * you may not use this file except in compliance with the License. * You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS"BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express orimplied. * See the License for the specific language governing permissions and * limitations under the License. */ package com.example.android.apis.app; import com.example.android.apis.R; import android.app.Activity; import android.app.Fragment; import android.app.FragmentManager; import android.os.Bundle; import android.view.LayoutInflater; import android.view.View; import android.view.ViewGroup; import android.view.View.OnClickListener; import android.widget.Button; import android.widget.ProgressBar; /** * This example shows how you can use a Fragment to easily propagate state * (such as threads) across activity instances when an activity needs tobe * restarted due to, for example, a configuration change. This is alot * easier than using the raw Activity.onRetainNonConfiguratinInstance()API. */ public class FragmentRetainInstance extends Activity { @Override protected void onCreate(Bundle savedInstanceState) { super.onCreate(savedInstanceState); // First time init, create the UI. if (savedInstanceState == null) { getFragmentManager().beginTransaction().add(android.R.id.content, newUiFragment()).commit(); } } /** * This is a fragment showing UI that will be updated fromwork done * in the retained fragment. */ public static class UiFragment extends Fragment { RetainedFragment mWorkFragment; @Override public View onCreateView(LayoutInflater inflater,ViewGroup container, BundlesavedInstanceState) { View v =inflater.inflate(R.layout.fragment_retain_instance, container, false); // Watch for button clicks. Button button =(Button)v.findViewById(R.id.restart); button.setOnClickListener(newOnClickListener() { public voidonClick(View v) { mWorkFragment.restart(); } }); return v; } @Override public void onActivityCreated(BundlesavedInstanceState) { super.onActivityCreated(savedInstanceState); FragmentManager fm =getFragmentManager(); // Check to see if we have retainedthe worker fragment. mWorkFragment =(RetainedFragment)fm.findFragmentByTag("work"); // If not retained (or first timerunning), we need to create it. if (mWorkFragment == null) { mWorkFragment = newRetainedFragment(); // Tell it who it isworking with. mWorkFragment.setTargetFragment(this, 0); fm.beginTransaction().add(mWorkFragment, "work").commit(); } } } /** * This is the Fragment implementation that will be retainedacross * activity instances. It represents some ongoingwork, here a thread * we have that sits around incrementing a progressindicator. */ public static class RetainedFragment extends Fragment { ProgressBar mProgressBar; int mPosition; boolean mReady = false; boolean mQuiting = false; /** * This is the thread that will do our work. It sits in a loop running * the progress up until it has reached thetop, then stops and waits. */ final Thread mThread = new Thread() { @Override public void run() { // We'll figure thereal value out later. int max = 10000; // This thread runsalmost forever. while (true) { // Updateour shared state with the UI. synchronized (this) { // Our thread is stopped if the UI is not ready // or it has completed its work. while (!mReady || mPosition >= max) { if (mQuiting) { return; } try { wait(); } catch (InterruptedException e) { } } // Now update the progress. Note it is important that // we touch the progress bar with the lock held, so it // doesn't disappear on us. mPosition++; max = mProgressBar.getMax(); mProgressBar.setProgress(mPosition); } //Normally we would be doing some work, but put a kludge // hereto pretend like we are. synchronized (this) { try { wait(50); } catch (InterruptedException e) { } } } } }; /** * Fragment initialization. We way wewant to be retained and * start our thread. */ @Override public void onCreate(Bundle savedInstanceState) { super.onCreate(savedInstanceState); // Tell the framework to try to keepthis fragment around // during a configuration change. setRetainInstance(true); // Start up the worker thread. mThread.start(); } /** * This is called when the Fragment's Activityis ready to go, after * its content view has been installed; it iscalled both after * the initial fragment creation and after thefragment is re-attached * to a new activity. */ @Override public void onActivityCreated(Bundle savedInstanceState){ super.onActivityCreated(savedInstanceState); // Retrieve the progress bar from thetarget's view hierarchy. mProgressBar =(ProgressBar)getTargetFragment().getView().findViewById( R.id.progress_horizontal); // We are ready for our thread to go. synchronized (mThread) { mReady = true; mThread.notify(); } } /** * This is called when the fragment is goingaway. It is NOT called * when the fragment is being propagatedbetween activity instances. */ @Override public void onDestroy() { // Make the thread go away. synchronized (mThread) { mReady = false; mQuiting = true; mThread.notify(); } super.onDestroy(); } /** * This is called right before the fragment isdetached from its * current activity instance. */ @Override public void onDetach() { // This fragment is being detachedfrom its activity. We need // to make sure its thread is notgoing to touch any activity // state after returning from thisfunction. synchronized (mThread) { mProgressBar = null; mReady = false; mThread.notify(); } super.onDetach(); } /** * API for our UI to restart the progressthread. */ public void restart() { synchronized (mThread) { mPosition = 0; mThread.notify(); } } } }
Android Fragment---管理Fragment
要管理Activity中Fragment,需要使用FragmentManager对象,在Activity中调用getFragmentManager()方法能够获得这个对象。
FragmentManager对象能够做以下事情:
关于这些方法的更多信息,可以参照FragmentManager类文档。
像前面章节中介绍的那样,你也能使用FragmentManager来打开一个FragmentTransaction对象,以便执行诸如添加和删除Fragment等事务。
Android Fragment---执行Fragment事务
在Activity中使用有关Fragment的添加、删除、替换以及用它们执行其他响应用户交互行为的能力是一项伟大的功能。你提交给Activity的每组改变集合被叫做一个事务,并且你能使用FragmentTransaction中APIs来执行它。也能够把每个事务保存到被Activity管理的回退堆栈中,并允许用户通过Fragment改变来向后导航(类似同Activity的向后导航)。
你能够从FragmentManager对象中获取一个FragmentTransaction对象的实例,例如:
FragmentManager fragmentManager = getFragmentManager();
FragmentTransaction fragmentTransaction = fragmentManager.beginTransaction();
每个事务是一组想要同时执行的改变,你能够使用诸如add()、remove()和replace()方法把想要在一个事务中执行的所有改变组合到一起,然后,调用commit()方法,把事务的执行结果反映到Activity中。
但是,在调用commit()方法之前,为了把事务添加到Fragment事务的回退堆栈,你可能要调用addToBackStack()方法。这个回退堆栈被Activity管理,并且允许用户通过按返回按钮返回到先前的Fragment状态。
下例说明了怎样用另一个Fragment替换当前的Fragment,并且在回退堆栈中保留这个Fragment的当前状态。
// 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();
在这个例子中,newFragment替换了在布局容器中被R.id.fragment_container ID标识的任何当前Fragment。通过调用addToBackStack()方法,替换事务被保存到回退堆栈中,以便用户能够反转事务,并且能够通过按回退按钮返回到前一个Fragment。
如果给事务添加多个改变(如用add()或remove()方法),并且调用了addToBackStack()方法,那么所有这些改变在调用commit()方法之前,都会作为一个单一的事务被添加到回退堆栈中,并且案返回按钮时,所有这些改变将会被恢复。
除了以下两种情况之外,添加到FragmentTransaction的改变顺序无关紧要:
1. 最后必须调用commit()方法;
2. 如果给同一个容器添加了多个Fragment,那么添加的顺序决定了它们在View层次树中显示顺序。
在你执行删除Fragment的事务时,如果没有调用addToBackStack()方法,那么Fragment将会在事务被提交时销毁,并且用户不能再向后导航。因此,在删除Fragment时,如果调用了addToBackStack()方法,那么这个Fragment就会被终止,并且用户向后导航时将会被恢复。
提示:对于每个Fragment事务,你能够在提交之前通过调用setTransition()方法,申请一个过渡动画。
调用commit()方法并不立即执行这个事务,而是在Activity的UI线程之上(”main”线程)调度运行,以便这个线程能够尽快执行这个事务。但是,如果需要,可以调用来自UI线程的executePendingTransactions()方法,直接执行被commit()方法提交的事务。通常直到事务依赖其他线程的工作时才需要这样做。
警告:你能够使用commit()方法提交一个只保存之前Activity状态的事务(在用户离开Activity时)。如果试图在用户离开Activity之后提交,将会发生一个异常。这是因为如果Activity需要被恢复,而提交之后的状态却丢失了。这种情况下,使用commitAllowingStateLoss()方法,你丢失的提交就没问题了。
Android Fragment---跟Activity通信
尽管Fragment是作为一个独立于Activity来实现的一个对象,并且能够在多个Activity内部使用,但是一个给定的Fragment实例直接被捆绑包含它的Activity中。
特别是Fragment能够使用getActivity()方法访问Activity的实例,并且很容易执行如在Activity布局中查找视图的任务:
View listView = getActivity().findViewById(R.id.list);
同样Activity通过从FragmentManager中获得的Fragment引用也能够调用Fragment中的方法,使用findFragmentById()或findFragmentByTag()方法获取Fragment引用,例如:
ExampleFragment fragment = (ExampleFragment)getFragmentManager().findFragmentById(R.id.example_fragment);
为了重用Fragment的UI组件,你创建的每个Fragment都应该是自包含的、有它自己的布局和行为的模块化组件。一旦你定义了这些可重用的Fragment,你就可以把它们跟一个Activity关联,并把它们跟应用程序的逻辑相连来实现全部的组合式UI。
你会经常想要一个Fragment跟另一个Fragment进行通信,例如,要基于一个用户事件来改变内容。所有的Fragment间的通信都是通过跟关联的Activity来完成的。另外Fragment不应该直接通信。
定义接口
为了让Fragment跟它的Activity通信,你可以在Fragment类中定义一个接口,并在它所属的Activity中实现该接口。Fragment在它的onAttach()方法执行期间捕获该接口的实现,然后就可以调用接口方法,以便跟Activity通信。
以下是Fragment跟Activity通信的示例:
public class HeadlinesFragment extends ListFragment { OnHeadlineSelectedListener mCallback; // Container Activitymust implement this interface public interface OnHeadlineSelectedListener { public void onArticleSelected(int position); } @Override public void onAttach(Activity activity) { super.onAttach(activity); // This makessure that the container activity has implemented // the callbackinterface. If not, it throws an exception try { mCallback =(OnHeadlineSelectedListener) activity; } catch(ClassCastException e) { throw newClassCastException(activity.toString() + " mustimplement OnHeadlineSelectedListener"); } } ... }
现在,这个Fragment就可以通过调用OnHealdlineSelectedListener接口示例mCallback的onArticleSelected()方法(或其他的接口中的方法)给Activity发送消息。
例如,在Fragment中的下列方法会用户点击列表项时被调用。该Fragment使用回调接口把该事件发送给它的父Activity。
@Override public void onListItemClick(ListView l, View v, int position, long id) { // Send theevent to the host activity mCallback.onArticleSelected(position); }
实现接口
为了从Fragment中接收事件回调,它的父Activity必须实现Fragment类中定义的接口。
例如,下面Activity实现了上面示例中定义的接口:
public static class MainActivity extends Activity implements HeadlinesFragment.OnHeadlineSelectedListener{ ... public void onArticleSelected(int position) { // The user selected the headline of an article from the HeadlinesFragment // Do somethinghere to display that article } }
把消息发送给一个Fragment
通过使用findFragmentById()方法捕获Fragment实例,父Activity可以把消息发送给该Fragment,然后直接调用该Fragment的公共方法。
例如,假设上面显示的那个Activity包含了另外的用于显示上面回调方法中返回的特定项目数据的Fragment。这种情况下,该Activity可以把回调方法中接收到信息传递给显示该项目的Fragment:
public static class MainActivity extends Activity implements HeadlinesFragment.OnHeadlineSelectedListener{ ... public void onArticleSelected(int position){ // The user selected the headline of an article from the HeadlinesFragment // Do something here to display that article ArticleFragment articleFrag =(ArticleFragment) getSupportFragmentManager().findFragmentById(R.id.article_fragment); if(articleFrag !=null){ // If article frag is available, we're in two-pane layout... // Call a method in the ArticleFragment to update its content articleFrag.updateArticleView(position); }else{ // Otherwise, we're in the one-pane layout and must swap frags... // Create fragment and give it an argument for the selected article ArticleFragment newFragment =new ArticleFragment(); Bundle args =newBundle(); args.putInt(ArticleFragment.ARG_POSITION, position); newFragment.setArguments(args); FragmentTransactiontransaction = getSupportFragmentManager().beginTransaction(); // Replace whatever is in the fragment_container view with this fragment, // and add the transaction to the back stack so the user can navigate back transaction.replace(R.id.fragment_container, newFragment); transaction.addToBackStack(null); // Commit the transaction transaction.commit(); } } }
Android Fragment---给Activity创建事件回调
在某些案例中,可能需要Fragment与Activity共享事件。在Fragment内部定义一个回调接口是一个好方法,并且规定由持有它的Activity实现这个回调方法。当Activity通过接口接受回调时,它能在必要时与布局中的其他Fragment共享信息。
例如,如果一个新闻类的应用程序在一个Activity中有两个Fragment---一个用来显示文章列表(Fragment A),另一个用来显示文章内容(Fragment B)---然后再列表项目被选中时Fragment A必须告诉Activity,以便它能告诉Fragment B显示对应的文章。在下面的例子中在Fragment A的内部声明了OnArticleSelectedListener接口。
public static class FragmentA extends ListFragment {
...
// Container Activity must implement this interface
public interface OnArticleSelectedListener {
public void onArticleSelected(Uri articleUri);
}
...
}
然后,持有这个Fragment的Activity要实现OnArticleSelectedListener接口,并且要重写onArticleSelected()方法把来自Fragment A的事件通知给Fragment B。要确保持有Fragment的Activity实现这个接口, Fragment A 的onAttach()回调方法(当Fragment被添加到Activity时系统调用这个方法)通过类型转换onAttach()传入的Activity来实例化一个OnArticleSelectedListener的实例。
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 newClassCastException(activity.toString() + " must implementOnArticleSelectedListener");
}
}
...
}
如果这个Activity没有实现这个接口,那么Fragment会抛出ClassCastException异常。如果成功,那么mListener成员就会拥有Activity实现的OnArticleSelectedListener对象的引用,以便Fragment A能够通过OnArticleSelectedListener接口定义的回调方法和Activity共享事件。例如,如果ListFragment继承了Fragment A,那么用户每次点击列表项时,系统都会调用Fragment中的onListItemClick()方法,然后调用onArticleSelected()方法和Activity共享事件:
public static class FragmentA extends ListFragment {
OnArticleSelectedListener mListener;
...
@Override
public void onListItemClick(ListView l, View v, int position,long id) {
// Append the clicked item's row ID with thecontent provider Uri
Uri noteUri = ContentUris.withAppendedId(ArticleColumns.CONTENT_URI, id);
// Send the event and Uri to the host activity
mListener.onArticleSelected(noteUri);
}
...
}
传递给onListItemClick()的id参数是被点击项目的行ID,Activity(或其他的Fragment)使用这个ID从应用程序的ContentProvider对象中获取对应的文章。
关于使用有效内容提供器的更多内容,请看内容提供器(Content Providers)文档。
Fragment通过实现onCreateOptionsMenu()方法给Activity的可选菜单(包括动作栏)提供菜单项,但是为了这个方法能够接受调用,必须在onCreate()方法中调用setHasOptionsMenu()方法来指示这个Fragment应该作为可选菜单的添加项(否则,这个Fragment不接受对onCreateOptionsMenu()方法的调用)。
然后,你把来自Fragment的要添加到可选菜单中项目追加到既存的菜单中。当菜单项被选择时,这个Fragment也接受onOptionsItemSelected()的回调。
你也能够通过调用registerForContextMenu()方法在Fragment布局中注册一个视图来提供一个上下文菜单。当用户打开上下文菜单时,Fragment会接受对onCreateContextMenu()方法的调用。当用户选择一个菜单项时,Fragment会接受对onContextItemSelected()方法的调用。
注意:尽管Fragment添加的每个菜单项都接受一个on-item-selected回调,但是当用户选择一个菜单项时,对应的Activity会首先受到相应的回调。如果Activity的on-item-selected回调的实现不处理被选择的项目,那么事件会被传递给Fragment的回调。这是真正的可选菜单和上下文菜单。
关于菜单的更多信息,请看菜单(Menus)和动作栏(Action Bar)开发指南。
Android Fragment---处理Fragment生命周期
管理Fragment的生命周期有点像管理Activity的生命周期,跟Activity一样,Fragment也存在三种状态:
恢复态:
这种状态下,Fragment显示在正在运行的Activity中。
暂停态:
这种状态下,另一个Activity在前台,并且有焦点,但这个Fragment所在的Activity依然是可见的(它前面的Activity是部分透明或没有完全覆盖它)。
终止态:
这种状态下,Fragment是不可见的,既可以是持有它的Activity已经被终止,也可以是Fragment从Activity中被删除,但被添加到了回退堆栈中。被终止的Fragment依然存活着(所有的状态和成员信息被系统保留着)。但是,对用户它不再可见,并且如果Activity被杀死,它也会被杀死。
跟Activity一样,你也能使用Bundle对象保留Fragment的状态,这样,在Activity的进程被杀死时,并且在Activity被重建时,你需要使用这个对象来恢复Fragment的状态。你能够在Fragment的onSaveInstanceState()回调执行期间保存状态,并且在onCreate(),onCreateView()回调或onActivityCreated()回调期间恢复状态。关于保存状态的更多信息,请看Activity文档。
Activity和Fragment之间在生命周期中最显著的不同是在各自的回退堆栈中它们是如何存储的。在Activity被终止时,默认情况下,Activity被放到了通过系统来管理的Activity的回退堆栈(因此用户能够使用回退按钮向后导航)。但是,在删除Fragment的事务期间,只有通过调用addToBackStack()方法明确的请求要保存Fragment实例时,它才被放到由持有Fragment的Activity管理的回退堆栈中。
否则,管理Fragment的生命周期与管理Activity的生命周期非常类似。因此,尽管你也需要理解Activity的生命是如何影响Fragment的生命的,但是在管理Activity生命周期(managing the activity lifecycle)文章中介绍的内容同样适用Fragment。
Android Fragment---与Activity生命周期的协调
拥有Fragment的Activity的生命周期直接影响了其中的Fragment的生命周期,这样,针对Activity的每一个生命周期的回调都会有一个类似的针对Fragment的回调。例如,当Activity收到onPause()回调时,在Activity中每个Fragment都会收到onPause()回调。
但是,Fragment有几个额外的生命周期回调方法,用来处理跟Activity的交互,以便执行诸如创建和销毁Fragment的UI的动作。这些额外的回调方法如下:
onAttach()
当Fragment已经跟Activity关联上的时候,这个回调被调用。Activity会作为onAttach()回调方法的参数来传递。
onCreateView()
创建跟Fragment关联的视图层时,调用这个回调方法。
onActivityCreated()
当Activity的onCreate()方法执行完之后,调用这个回调方法。
onDestroyView()
当跟Fragment关联的视图层正在被删除时,调用这个回调方法。
onDetach()
当从Activity中解除Fragment的关联时,调用这个回调方法。
像图3中说明的那样,Fragment的生命周期流收到持有这些Fragment的Activity的影响,在这个图中,你能看到每个连续的Activity状态决定了Fragment的那个回调方法可以被调用。例如,当Activity已经收到了onCreate()的回调之后,在Activity中的Fragment就不会再接收onActivityCreated()以上的回调了。
图3. 在Activity生命周期影响之下的Fragment生命周期
一旦Activity到达了被恢复的状态,你就可以自由的给这个Activity添加和删除Fragment了,只有Activity在恢复态时,Fragment的生命周期才能独立的改变。
但是,当Activity离开恢复态时,Fragment会再次被推进Activity的生命周期中。
1. 继承关系
java.lang.Object
|__android.app.Fragment
实现接口:ComponentCallbacks2View.OnCreateContextMenuListener
引入版本:API Level 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类。如果空的构造器无效,那么在状态恢复期间会导致运行时异常发生。
较旧的平台
尽管Fragment API是在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">
<fragment class="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 void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.fragment_layout);
}
依赖ListFragment对象,要显示列表的标题是相当简单的。要注意的是,点击一个列表项的实现,会依赖当前Activity的布局,它既可以创建一个新的Fragment用于显示该项目的明细,也可以启动一个新的Activity用于显示项目的明细。
public static class TitlesFragment extends ListFragment {
boolean mDualPane;
int mCurCheckPosition = 0;
@Override
public void onActivityCreated(Bundle savedInstanceState) {
super.onActivityCreated(savedInstanceState);
// Populate list with our staticarray of titles.
setListAdapter(new ArrayAdapter<String>(getActivity(),
android.R.layout.simple_list_item_activated_1, Shakespeare.TITLES));
// Check to see if we have a frame inwhich to embed the details
// fragment directly in thecontaining UI.
View detailsFrame = getActivity().findViewById(R.id.details);
mDualPane = detailsFrame != null && detailsFrame.getVisibility() == View.VISIBLE;
if (savedInstanceState != null) {
// Restore last state for checkedposition.
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 correctstate.
showDetails(mCurCheckPosition);
}
}
@Override
public void onSaveInstanceState(Bundle outState) {
super.onSaveInstanceState(outState);
outState.putInt("curChoice", mCurCheckPosition);
}
@Override
public void onListItemClick(ListView l, View v, int position, long id) {
showDetails(position);
}
/**
* Helper function to show the detailsof a selected item, either by
* displaying a fragment in-place inthe current UI, or starting a
* whole new activity in which it is displayed.
*/
void showDetails(int index) {
mCurCheckPosition = index;
if (mDualPane) {
// We can display everything in-placewith fragments, so update
// the list to highlight the selecteditem and show the data.
getListView().setItemChecked(index, true);
// Check what fragment is currentlyshown, replace if needed.
DetailsFragment details = (DetailsFragment)
getFragmentManager().findFragmentById(R.id.details);
if (details == null || details.getShownIndex() != index) {
// Make new fragment to show this selection.
details = DetailsFragment.newInstance(index);
// Execute a transaction, replacing any existing fragment
// with this one inside the 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 selectedtext.
Intent intent = new Intent();
intent.setClass(getActivity(), DetailsActivity.class);
intent.putExtra("index", index);
startActivity(intent);
}
}
}
明细Fragment对象只会显示所选项目的详细文本字符串,它是基于内置在应用中的一个字符数组的索引来获取的:
public static class DetailsFragment extends Fragment {
/**
* Create a new instance ofDetailsFragment, initialized to
* show the text at 'index'.
*/
public static DetailsFragment newInstance(int index) {
DetailsFragment f = new DetailsFragment();
// Supply index input as an argument.
Bundle args = new Bundle();
args.putInt("index", index);
f.setArguments(args);
return f;
}
public int getShownIndex() {
return getArguments().getInt("index", 0);
}
@Override
public View onCreateView(LayoutInflater inflater, ViewGroup container,
Bundle savedInstanceState) {
if (container == null) {
// We have different layouts, and inone of them this
// fragment's containing framedoesn't exist. The fragment
// may still be created from itssaved state, but there is
// no reason to try to create itsview hierarchy because it
// won't be displayed. Notethis is not needed -- we could
// just run the code below, where wewould create and return
// the view hierarchy; it would justnever be used.
return null;
}
ScrollView scroller = new ScrollView(getActivity());
TextView text = new TextView(getActivity());
int padding = (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()]);
return scroller;
}
}
在用户点击标题的情况下,在当前的Activity中没有明细容器,因此标题Fragment的点击事件代码会启动一个新的显示明细Fragment的Activity:
public static class DetailsActivity extends Activity {
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
if (getResources().getConfiguration().orientation
== Configuration.ORIENTATION_LANDSCAPE) {
// If the screen is now in landscapemode, we can show the
// dialog in-line with the list so wedon't need this activity.
finish();
return;
}
if (savedInstanceState == null) {
// During initial setup, plug in thedetails fragment.
DetailsFragment details = new DetailsFragment();
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">
<fragment class="com.example.android.apis.app.FragmentLayout$TitlesFragment"
android:id="@+id/titles" android:layout_weight="1"
android:layout_width="0px" android:layout_height="match_parent" />
<FrameLayout android: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实例,并把这种改变发布到回退堆栈上:
void addFragmentToStack(){
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状态。
Android类参考---Fragment(三)InstantiationException
类说明
1. 嵌套类
class Fragment.InstantiationException
当有一个实例化错误时,会通过instantiate(Context, String, Bundle)方法抛出这个异常类。
class Fragment.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 FragmentManagergetFragmentManager()
返回跟该Fragment的Activity关联的所有的Fragment对象的管理器---FragmentManager对象。要注意的是在Fragment对象被放置到FragmentTransaction对象中,直到被提交给与它绑定的Activity期间,该方法一直返回null值。
public final int getId()
该方法返回该Fragment对象的标识,这个标识既可以是在布局中提供的android:id属性值,也可以是在添加Fragment对象时提供的容器View ID。
public LoaderManagergetLoaderManager()
返回针对该Fragment对象的LoaderManager对象,如果需要就创建它。
public final Resources getResources()
该方法返回跟Fragment对象关联的资源。
public final booleangetRetainInstance()
public final String getString(intresId)
从应用程序包的默认字符串表中返回一个本地化的字符串。
参数:
resId 要获取的字符串的资源ID。
public final String getString(intresId, Object… formatArgs)
从应用程序包的默认字符串表中返回一个被本地化的格式化字符串,用Formatter对象中format(String, Object…)方法来替换格式化的参数。
参数:
resId 指定格式化字符串的资源id
formatArgs 指定要替换的格式化参数
public final String getTag()
如果Fragment对象被指定了名称,那么使用该方法来获取Fragment对象的名称。
public final FragmentgetTargetFragment()
返回由setTargetFragment(Fragment, int)方法所设置的目标Fragment对象。
public final int getTargetRequestCode()
返回由setTargetFragment(Fragment, int)方法所设置的目标请求编码。
public final CharSequence getText(intresId)
从应用程序包的默认字符串表中返回指定的本地化的、样式化的CharSequence对象。
参数:
resId 指定要获取的CharSequence对象文本的资源id。
public final getUserVisibleHint()
返回要该Fragment对象上显示给用户的提示信息的值。
Public View getView()
获取该Fragment对象布局的根View对象,如果没有布局,则返回null。
public final int hashCode()
子类不能覆盖重写该方法。返回对象的hash code。
public static Fragmentinstantiate(Context context, String fname)
除了没有Bundle参数以外,其他的跟instantiate(Context, String, Bundle)方法一样。
public static Fragmentinstantiate(Context context, String fname, Bundle args)
用给定的类名创建一个新的Fragment对象实例。它跟调用空的构造器一样。
参数:
context 实例化该Fragment对象时要使用的上下文环境。当前只用于获取它的类装载器---ClassLoader对象。
fname 要实例化的Fragment类的名称。
args 指定要提供给Fragment对象的Bundle参数,可以使用getArguments()方法获取其中的参数。也可以是 null。
返回一个新的Fragment对象实例。
异常(Throws)
InstantiationException 如果在实例化给定的Fragment类时发生错误,就会抛出这个运行时异常,它通常是不被期望发生的。
public final boolean isAdded()
如果该Fragment对象被添加到了它的Activity中,那么它返回true,否则返回false。
public final boolean isDetached()
如果该Fragment已经明确的从UI中分离,那么它返回true。也就是说,在该Fragment对象上使用FragmentTransaction.detach(Fragment)方法。
该方法在API Level 13中被引入。
public final boolean isHidden()
如果该Fragment对象已经被隐藏,那么它返回true。默认情况下,Fragment是被显示的。能够用onHiddenChanged(boolean)回调方法获取该Fragment对象状态的改变,要注意的是隐藏状态与其他状态是正交的---也就是说,要把该Fragment对象显示给用户,Fragment对象必须是被启动并不被隐藏。
Public final boolean isInLayout()
如果布局通过<fragment>标签被包含在Activity层次树中,那么它就返回true。当Fragment是通过<fragment>标签来创建的时候,这个方法始终会返回true。从之前的状态恢复旧的Fragment对象,并且该对象没有显示在当前状态的布局中的情况除外。
Public final boolean isRemoving()
如果当前的Fragment对象正在从它的Activity中被删除,那么就返回true。这删除过程不是该Fragment对象的Activity的结束过程,而是把Fragment对象从它所在的Activity中删除的过程。
public final boolean isResumed()
如果Fragment对象是在恢复状态中,该方法会返回true。在onResume()和onPause()回调期间,这个方法都返回true。
Public final boolean isVisible()
如果该Fragment对象对用户可见,那么就返回true。这就意味着它:1.已经被添加到Activity中;2.它的View对象已经被绑定到窗口中;3.没有被隐藏。
Public void onActivityCreated(BundlesavedInstanceState)
当Fragment对象的Activity被创建,并且Fragment对象的View层次树被实例化的时候,系统会调用这个方法。能够利用这个方法来做一些最后的初始化处理,如获取或恢复状态,还可用于那些使用setRetainInstance(boolean)方法来保留它们的实例的Fragment对象,因为这个回调方法会告诉Fragment对象,它是在什么时候跟这个新的Activity实例关联的。该回调方法在onCreateView(LayoutInflater,ViewGroup, Bundle)之后和onStart()之前被调用。
参数
savedInstanceState 如果该Fragment对象正在被重建,那么该参数指定之前被保存的状态。
public void onActivityResult(intrequestCode, int resultCode, Intent data)
接收来自前面调用startActivityForResult(Intent, int)方法的结果。在Activity API的相关介绍中有关于onActivityResult(int, int, Intent)方法的介绍。
参数
requestCode 这个整数是由startActivityForResult()提供的初始请求Code,允许用来标识该结果的来源。
resultCode 这个整数是由子Activity通过它的setResult()方法设置并返回的结果Code。
Data 一个Intent类型的对象,它把结果数据返回给调用者(能包各种数据绑定给Intent对象的extras属性字段)。
public void onAttach(Activityactivity)
当该Fragment对象被第一次绑定到它的Activity时,系统会调用这个方法。这个方法被调用之后,系统会调用该Fragment对象的onCcreate(Bundle)方法。
public voidonConfigurationChanged(Configuration newConfig)
当设备配置改变且组件正在运行时,系统会调用这个方法。要注意的是,跟Activity不一样,在配置改变时,其他的组件不会被重启:它们始终通过重新获取资源来处理这种改变。
在该功能被调用后,被更新的资源对象会返回与新的配置相匹配的资源值。
参数
newConfing 指定新的设备配置。
public boolean onContextItemSelected(MenuItemitem)
在上下文菜单中的一个菜单项被选择时,系统会调用这个回调方法。对于所放生的普通的处理过程,该方法的默认实现只是简单的返回false(调用该项目的Runnable对象或把一个消息发送给相应的Handler)。可以使用这个方法针对菜单项做一些其他的处理。
使用getMenuInfo()方法来获取由添加给菜单项的View对象所设置的附加信息。
其子类应用通过调用基类的该方法实现来执行默认的菜单处理。
参数
item 该参数指定了被选择的上下文菜单项。
返回值
如果允许正常的上下文菜单处理,就返回false,否则返回true。
public void onCreate(BundlesavedInstanceState)
Fragment对象被初始创建时,系统会调用该方法。调用时机是在onAttach(Activity)之后,onCreateView(LayoutInflater, ViewGroup, Bundle)之前。
要注意的是,该方法在其Fragment的Activity依然在被创建的过程中,也能够被调用。因此,不能在这个时点依赖正在被初始化的Activity的上下文View层次树。
参数
savedInstanceState 如果该Fragment正在被重建,那么这个参数会指定Fragment之前的状态。
public Animator onCreateAnimator(inttransit, boolean enter, int nextAnim)
在Fragment对象加载一个动画时,系统会调用这个方法。
public voidonCreateContextMenu(ContextMenu menu, View v, ContextMenu.ContextMenuInfomenuInfo)
当View对象相关的上下文菜单显示的时候,系统会调用该方法。跟onCreateOptionMenu(Menu,MenuInflater)不同,这个方法在上下文菜单每次显示的时候,都会被调用,并且应该被填入对应的View对象。
使用onContextIntemSelected(android.view.MenuItem)方法来获取被选择的菜单项。
这个方法的默认实现是调用Activity.onCreateContextMenu方法,不过如果不想要这个默认行为,也能够不调用这个实现。
在这个方法返回之后,持有该上下文菜单是不安全的。当该上下文菜单所对应View对象被创建时,这个方法会被调用。
参数
menu 该参数指定要创建的上下文菜单。
v 该参数指定要创建的上下文菜单所对应的View对象。
menuInfo 该参数指定要显示的上下文菜单的菜单项的附加信息。这个信息会根据v参数的类型而有所不同。
public void onCreateOptionsMenu(Menumenu, MenuInflater inflater)
该方法初始化Activity的标准的选项菜单的内容。应该把菜单项放到menu参数中。针对该方法的调用,必须要首先调用setHasOptionsMenu(boolean)方法。更多信息请看Activity.onCreateOptionsMenu。
参数
menu 该参数指定要放置菜单项的那个选项菜单。
public View onCreateView(LayoutInflater, inflater, ViewGroup container, Bundle savedInstanceState)
调用该方法,初始化Fragment的用户界面。这个方法是可选的,并且对于非图形化的Fragment对象,该方法会返回null(这是默认的实现)。该方法在onCreate(Bundle)和onActivityCreated(Bundle)方法之间被调用。
如果从该方法中返回一个View对象,那么在该View对象被释放时,会调用onDestroyView()方法。
参数
inflager 该LayoutInflater对象能够被用于填充Fragment对象中任何View对象。
Container 如果该参数是非空(non-null),那么它指定了Fragment对象的UI应该被绑定到这个参数所指向的容器上,它是Fragment对象的父容器。Fragment对象不应该把这个View对象添加到自己的布局中,但是能够使用它来生成View对象的LayoutParams对象。
savedInstanceState 如果该参数是非空(non-null),那么就会使用该参数中所保持的状态值来重建Fragment对象。
返回值
该方法返回对应的Fragment UI的View对象,或者是null。
public void onDestroy()
当Fragment不再被使用时,系统会调用该方法。在onStop()方法之后、onDetach()方法之前被调用。
Public void onDestroyOptionsMenu()
当该Fragment的选项菜单项目不再被包含在整体的选项菜单中时,系统会调用该方法。收到这个调用,意味着该菜单需要被重建,但是这个Fragment的项目没有被包含在最新创建的菜单中(它的onCreateOptionsMenu(Menu,MenuInflater)方法不会被调用)。
public void onDestroyView()
当先前用onCreateView(LayoutInflater, ViewGroup, Bundle)方法创建的View对象从Fragment对象中解除绑定的时候,系统会调用这个方法。在下次需要显示这个Fragment对象时,要创建一个的View对象。这个方法在onStop()方法之后、onDestroy()方法前被调用。调用这个方法与onCreateView(LayoutInflater, ViewGroup, Bundle)方法是否返回了非空的View对象无关。在这个方法调用的内部,要先保存该View对象的状态,然后才能把它从器父对象中删除。
public void onDetach()
当该Fragment对象不在跟它的Activity绑定时,系统会调用这个方法。它是在onDestroy()方法之后被调用。
public void onHiddenChanged(booleanhidden)
当该Fragment对象改变了隐藏状态(由isHidden()方法返回)时,系统会调用这个方法。Fragment初始是不隐藏的,只要Fragment对象改变了它的显示状态,就会调用该方法。
参数
hidden 如果该Fragment对象现在是隐藏的,则该参数是true,否则是false。
public void onInflate(AttributeSetattrs, Bundle savedInstanceState)
该方法在API Level 12以后已经被废弃了,请使用onInflate(Activity, AttributeSet, Bundle)方法来代替。
public void onInflate(Activityactivity, AttributeSet attrs, Bundle savedInstanceState)
当一个Fragment对象被作为一个View对象布局的一部分来填充时,就会调用该方法,通常用于设置一个Activity的内容视窗。在从布局文件的标签中创建该Fragment对象之后,可以立即调用该对象。注意:这时的调用是在该Fragment对象的onAttach(Activity)方法被调用之前,因此在这时所能做的所有的事情就是解析并保存它的属性设置。
每次调用该方法时,该Fragment对象都被填充,即使是把它填充到一个新的用于保存状态的新的实例。通常每次都要重新解析参数,从而允许它们根据不同的配置来改变。
该方法在API Level 12中被引入。
以下是一个Fragment的典型实现,它能够同时通过属性和getArguments()方法来获取参数:
publicstaticclassMyFragmentextendsFragment{
CharSequence mLabel;
/**
* Create a new instance of MyFragment that will beinitialized
* with the given arguments.
*/
staticMyFragment newInstance(CharSequence label){
MyFragment f =newMyFragment();
Bundle b =newBundle();
b.putCharSequence("label", label);
f.setArguments(b);
return f;
}
/**
* Parse attributes during inflation from a view hierarchyinto the
* arguments we handle.
*/
@Overridepublicvoid onInflate(Activity activity,AttributeSet attrs,
Bundle savedInstanceState){
super.onInflate(activity, attrs, savedInstanceState);
TypedArray a = activity.obtainStyledAttributes(attrs,
R.styleable.FragmentArguments);
mLabel = a.getText(R.styleable.FragmentArguments_android_label);
a.recycle();
}
/**
* During creation, if arguments have been supplied to thefragment
* then parse those out.
*/
@Overridepublicvoid onCreate(Bundle savedInstanceState){
super.onCreate(savedInstanceState);
Bundle args = getArguments();
if(args !=null){
mLabel = args.getCharSequence("label", mLabel);
}
}
/**
* Create the view for this fragment, using the argumentsgiven to it.
*/
@OverridepublicView 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(mLabel !=null? mLabel :"(no label)");
tv.setBackgroundDrawable(getResources().getDrawable(android.R.drawable.gallery_thumb));
return v;
}
}
要注使用意的是,使用styleable资源来解析XML属性。以下是styleable所使用的XML声明:
<declare-styleablename="FragmentArguments">
<attrname="android:label"/>
</declare-styleable>
然后,在Activity的内容布局内部,能够像下面这样声明一个Fragment标签:
<fragmentclass="com.example.android.apis.app.FragmentArguments$MyFragment"
android:id="@+id/embedded"
android:layout_width="0px"android:layout_height="wrap_content"
android:layout_weight="1"
android:label="@string/fragment_arguments_embedded"/>
Fragment对象也能够在运行时,通过在Bundle对象中参数来动态的创建,以下是一个动态创建Fragment对象的例子:
@Overrideprotectedvoid onCreate(Bundle savedInstanceState){
super.onCreate(savedInstanceState);
setContentView(R.layout.fragment_arguments);
if(savedInstanceState ==null){
// First-time init; create fragmentto embed in activity.
FragmentTransaction ft = getFragmentManager().beginTransaction();
Fragment newFragment =MyFragment.newInstance("From Arguments");
ft.add(R.id.created, newFragment);
ft.commit();
}
}
参数
activity 指定要该Fragment对象来填充的Activity对象;
attrs 指定了正在创建的Fragment对象标签中的属性;
savedInstanceState 如果该Fragment对要重之前保存的状态中重建,那么就要使用该参数,它保存了该Fragment对象之前的状态。
public void onLowMemory()
当整个系统运行在低内存的状态,并当前活跃的运行进程视图回收内存的时候,会调用该方法。这个方法被调用的精确的时间点没有被定义,通常它会发生在所有的后台进程都被杀死的前后,这是在到达杀死进程托管服务的时点之前,并且会尽量避免杀死前台UI。
应用程序能够实现该方法,用于释放缓存或其他的不需要的资源。在从这个方法返回之后,系统会执行gc(垃圾回收)操作。
public booleanonOptionsItemSelected(MenuItem item)
选项菜单被选择的时候,系统会调用该方法。该方法的默认实现只是简单的返回false,默认实现只是执行一些正常的处理(如调用菜单项目的Runnable对象,或把一个消息发送给合适的Handler对象)。可以使用菜单项的这个方法,做那些没有其他措施处理的工作。
该类的任何子类都应该调用基类的实现,以便执行默认的菜单处理。
参数
item 用户选择的菜单项。
返回值
布尔值,返回false,则运行正常的菜单处理继续执行,否则会终止执行。
public void onOptionsMenuClosed(Menumenu)
在选项菜单被关闭的时候(既可以是用户按下了回退或菜单按钮取消了菜单,也可以是用户选择了一个菜单项),系统会调用这个方法。
参数
menu 最后显示的或由onCreateOptionsMenu()方法第一次初始化的选项菜单。
Public void onPause()
当该Fragment对象不再是恢复状态的时候,系统会调用该方法。这个方法通常会跟它的Activity的生命周期的Activity.onPause()方法捆绑。
public void onPrepareOptionsMenu(Menumenu)
该方法用于准备显示屏幕的标准选项菜单。它是选项菜单显示之前被调用的。能够使用这个方法来启用或禁用某些菜单项,或者动态的修改菜单项的内容。
参数
menu最后显示的或由onCreateOptionsMenu()方法第一次初始化的选项菜单。
Public void onResume()
当Fragment对象显示给用户并处于活跃的运行状态时,系统会调用这个方法。它通常会跟它的Activity生命周期的Activity.onResume()方法绑定。
public voidonSaveInstanceState(Bundle outstate)
当要求Fragment对象保存当前的动态的状态时,系统会调用该方法,以便能够在以后的新实例重建时,使用这些被保存的状态。如果以后需要创建一个新的该Fragment对象实例,那么放置该方法Bundle参数中的数据,就会传递给onCreate(Bundle)、onCreateView(LayoutInflater, ViewGroup, Bundle)、onActivityCreated(Bundle)方法的Bundle参数)。
对应Activity.onSaveInstanceState(Bundle)方法的大多数讨论,也适用于本方法。但是要注意的是:这个方法可以在onDestroy()方法之前的任意时点调用。有一些情况是该Fragment对象已经被关闭了(如当它放置在没有UI显示的回退堆栈中时),但是直到它的Activity需要保存它的状态时,该Fragment的状态才会保存。
参数
outstate 该参数用于放置要保存的状态,它是一个Bundle类型的对象。
public void onStart()
当该Fragment对象对用户可见时,该方法会被调用。该方法通常会跟它的Activity的生命周期的Activity.onStart()方法绑定。
public void onStop()
当Fragment对象被终止的时候,该方法会被调用,它通常会跟它的Activity的生命周期的Activity.onStop()方法绑定。
public void onTrimMemory(int level)
当操作系统判断某个时机是从进程中消除不需要的内存的好时机时,系统会调用该方法。例如,该方法会在该Fragment对象进入后台,并且没有足够的内存用于保证后台进程的运行时,系统会调用该方法。
参数
level 该参数指定消除内存的上下文环境,给出可能要执行的消减应用程序的数量的提示。可以指定以下值:
TRIM_MEMORY_COMPLETE、TRIM_MEMORY_MODERATE、TRIM_MEMORY_BACKGROUND或TRIM_MEMORY_UI_HIDDEN。
public void onViewCreated(View view,Bundle savedInstanceState)
onCreateView(LayoutInflater,ViewGroup, Bundle)方法返回之后、之前被保存的View对象的状态被恢复之前,系统会立即调用该方法。这样就给子类在了解自己所在的View层次树被完全被创建的情况,提供初始化自己的机会。在这个时点,Fragment对象的View层次树还没有跟它的父对象绑定。
参数
view 通过onCreateView(LayoutInflater, ViewGroup, Bundle)方法返回的View对象。
savedInstanceState 如果该参数是非空的(non-null),那么该Fragment对象要使用这个参数中的状态来进行重建。
public voidregisterForContextMenu(View view)
该方法为给定的View对象注册一个上下文菜单(多个View对象能够使用同一个上下文菜单)。这个方法会给该Fragment对象的View对象设置一个View.OnCreateContextMenuListener事件监听器,因此在上下文菜单显示时,onCreateContextMenu(ContextMenu,View, ContextMenuInfo)方法会被系统调用。
参数
view 该参数指定应该显示内容菜单的那个View对象。
public void setArguments(Bundle args)
该方法给该Fragment对象提供构建参数。它只。在Fragment对象被绑定到它Activity对象之前被调用,也就是说在构建该Fragment对象之后,应该立即调用。该方法提供的参数会在Fragment对象销毁和创建期间被保留。
public void setHasOptionsMenu(booleanhasMenu)
通过该方法设置Fragment对象是否想要通过接受onCreateOptionsMenu(Menu,MenuInflater)方法的调用来加入选项菜单。
参数
hasMenu 如果是true,那么该Fragment对象就会有一个选项菜单。
public voidsetInitialSavedState(Fragment.SavedState state)
该方法使用由FragmentManager.saveFragmentInstanceState()方法返回的状态,作为该Fragment对象初次创建时应该恢复的状态。
参数
state 该参数指定应该恢复的状态。
public void setMenuVisibility(booleanmenuVisible)
该方法用于设置Fragment对象的菜单是否应该显示。如果该Fragment对象已经被放到了View的层次树中,而用户当前还看不到它,所以该Fragment对象的任何菜单也是不可见,这时调用这个方法就可以帮助显示和隐藏菜单。
参数
menuVisible 默认值是true,意味着该Fragment对象的菜单像通常一样显示,如果是false,用户就不能够看到这个菜单。
public void setRetainInstance(booleanretain)
该方法用于设置在Activity对象被重建(如配置的变化)时,是否应该保留该Fragment对象的实例。它仅适用于没有在回退堆栈中Fragment对象。如果设置为true,那么该Fragment对象的生命周期与创建Activity时有些不同:
1. onDestory()方法不会被调用(但是onDetach()方法会依然被调用,因为该Fragment对象会从当前的Activity中被解除绑定)。
2. onCreate(Bundle)方法不会被调用,因为该Fragment对象没有被重建;
3. onAttach(Activity)和onActivityCreated(Bundle)方法会依然被调用。
public voidsetTargetFragment(Fragment fragment, int requestCode)
该方法给该Fragment对象设置一个可选的目标。例如,如果该Fragment对象使用由另一个Fragment对象启动的,并且在想要把一个给定的结果返回给第一个Fragment对象时,就可以使用这个方法。这里被设置的目标是通过FragmentManager.putFragment()方法保留的实例。
参数
fragment 该参数给Fragment对象指定一个接受结果的目标Fragment对象。
requestCode 可选的请求编码,为返回到onActivityResult(int, int, Intent)方法提供便利。
public void setUserVisibleHint(booleanisVisibleToUser)
该方法用于告诉系统该Fragment对象的UI是否是对用户可见的。这个设置默认是true,并且会作为该Fragment对象被保存和恢复的状态。
任何设置为false的应用程序,指明该Fragment对象的UI对用户是不可见的,或者不直接对用户可见。这个方法可被系统用于优先级的操作,如Fragment对象生命周期的更新或启动顺序等行为。
参数
isVisibleToUser 如果设置为true,那么该Fragment对象对用户是可见的(默认),否则不可见。
该方法在API Level 15中被引入。
public void startActivity(Intentintent)
调用包含该Fragment对象的Activity的startActivity(Intent)方法。
public voidstartActivityForResult(Intent intent, int requestCode)
调用包含该Fragment对象的Activity的startActivityForResult(Intent, int)方法。
public String toString()
该方法返回一个简洁的,外行人能够读的懂的对象的描述。强烈推荐子类重写该方法,并提供考虑了对象类型和数据的实现。默认实现使用以下表达式:
getClass().getName()+'@'+Integer.toHexString(hashCode())
返回值
一个可打印的该对象的说明。
public voidunregisterForContextMenu(View view)
防止一个上下文菜单显示给给定的View对象。这个方法会删除指定View对象上View.OnCreateContextMenuListener事件监听器。
参数
view 该参数指定不应该显示上下文菜单的那个View对象。