Fragment(二)使用Fragment
转载请注明:http://blog.csdn.net/liaoqianchuan00/article/details/24270771
翻译自http://developer.android.com/guide/components/fragments.html
创建Fragment
要创建一个Fragment,你需要创建一个Fragment的子类。Fragment的代码很像Activity。他有一些和Activity很像的回调函数,比如onCreate,onStart,onPause和onStop。事实上,如果你要想现有的应用程序使用Fragment,你只需要把你的Activity回调函数方法的代码移动到Fragment相关的回调中去就可以了。
通常,你需要至少实现下面的方法:
1. onCreate
当创建一个Fragment的时候,系统会调用这个方法。如果你想要Fragment从paused/stopped到resumed的时候保持一些组件,你需要在这里初始化这些组件。
2. onCreateView
当第一次画UI的时候系统会调用这个方法。你需要在这个方法中返回你Fragment的根视图。如果你的Fragment没有UI的话,你可以直接返回null。
3. onPause
在用户离开这个Fragment的时候系统会调用这个方法(虽然这并不意味着Fragment会被destroyed)。你通常需要在这里保存一些持久化的数据(因为用户可能不会再回来到这里了)。
很多程序都需要至少完成这3个基本的回调方法,但是为了管理各种不同的生命周期时候的状态还有一些其他的回调方法你可能会用到。更详细的关于Fragment生命周期的回调方法会在后面的章节讨论。
还有一些继承自Fragment的子类你可以继承:
1. DialogFragment
显示一个对话框,使用这个可以很好的让用户回退到之前的Fragment。
2. ListFragment
和ListActivity很像,显示adapter管理的item列表。他提供了一些管理List View的方法,比如在点击其中一项的时候,onListItemClick回调方法。
3. PreferenceFragment
和PreferenceActivity很像,可以很好的为你的应用程序创建一个“settings”界面。
添加用户界面
Fragment通常被用来当做Activity的一部分UI,为Activity提供自己的layout。
为一个Fragment提供layout,你必须实现onCreateView回调方法,这个会在系统第一次画Fragment得界面的时候调用。你必须在这个回调方法中返回你的Fragment的layout的根视图。
注意:如果你是ListFragment的一个子类,那么你不需要实现这个回调方法,因为默认已经为你返回了一个ListView。
你可以从XML文件来创建一个View。onCreateView方法已经为你提供了LayoutInflater对象,可以直接使用。
比如,我们有个Fragment的子类,根据example_fragment.xml来生成layout视图:
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参数是你的Fragmentlayout将会被插入的Activity中的一个ViewGroup视图。savedInstanceState参数是你Fragment被重新创建的时候从上次的Fragment实例中保存的东西。
Inflate方法需要三个参数:
1. Layout的资源ID。
2. 将被插入到哪个ViewGroup中。用来接收一些layout的参数。
3. Boolean值指出是否需要在inflation的时候将这个view插入到ViewGroup中,这里我们传入false,因为系统已经帮我们插入这个View了-如果传入true的话,会在ViewGroup中创建一个冗余的layout。
现在,你已经知道了怎么为Fragment创建layout了,下面我们将看怎么把Fragment添加到Activity中。
添加Fragment到Activity中
通常,Fragment会为它的宿主Activity提供一部分UI嵌入到Activity的视图结构中。有两种方式来添加一个Fragment到Activity的Layout中去:
1. 在Activity的layout文件中声明Fragment
在这种情况下,如果你的Fragment有界面的话,你需要为它设置一些layout的属性。比如,这里我们有一个Layout文件,里面嵌入了两个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">
<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属性指出了系统创建Activity layout的时候,哪个Fragment类会被实例化,他会实例化Layout文件中声明的每个Fragment,并且调用每个Fragment的onCreateView函数来获取每个Fragment的视图。系统直接将Fragment返回的视图插入到<fragment>元素的地方。
注意:每个Fragment都需要一个单独的ID,这样系统在重新创建Fragment的时候来还原这个Fragment(你也可以使用这个ID来做一些transaction,比如移除这个Fragment)。有三种方式来为Fragment提供ID:
2. android:id
2. android:tag
2. 如果两个都没有提供,系统会使用Container View的ID。
2. 在代码中添加Fragment到一个已经存在的ViewGroup中
在Activity运行的任何时候,你都可以添加Fragment到Activity的layout中。你只需要简单的指出Fragment需要被放到哪个ViewGroup中就行了。
要在Activity中创建一个Fragment transaction(比如添加,移除或者是替换一个Fragment),你必须使用FragmentTransaction中得API。你可以像下面这样从Activity中取得一个FragmentTransaction的实例:
FragmentManager fragmentManager = getFragmentManager()
FragmentTransaction fragmentTransaction = fragmentManager.beginTransaction();
然后你就可以使用add方法来添加一个Fragment了,同时指出你的Fragment将会被添加到哪个ViewGroup中,比如:
ExampleFragment fragment = new ExampleFragment();
fragmentTransaction.add(R.id.fragment_container, fragment);
fragmentTransaction.commit();
Add方法的第一个参数通过资源ID来指出Fragment将会被放置在哪个ViewGroup中,第二个参数是要被添加的Fragment。
一旦你对FragmentTransaction做了修改,你必须调用commit方法来使你的修改生效。
3. 添加一个没有UI的Fragment
上面的例子讲述了怎样将一个带UI的Fragment添加到Activity中。但是,有的时候你仍然可以为Activity提供一个没有UI的的Fragment。
要添加一个没有UI的Fragment,在Activity中使用add(Fragment,String)来添加这个Fragment(使用唯一的TAG String)。这样就添加了这个Fragment,但是因为没有指出将会被插入到Activity中得哪个ViewGroup中,所以onCreateView事件不会被调用,所以你不需要实现这个回调方法。
用Tag来指出Fragment不只是在没有UI的Fragment中使用,你也可以为有UI的Fragment来指明一个Tag,但是,如果一个Fragment没有UI,那么Tag是唯一能找到这个Fragment的方法。如果你后面想在Activity中找到这个Fragment,你可以调用findFragmentByTag函数。
管理Fragment
要在Activity中管理Fragment,你需要使用FragmentManager。可以在Activity中调用getFragmentManager来得到这个FragmentManager。
你可以用FragmentManager来作下面的事情:
2. 使用findFragmentById(有UI)或者findFragmentBytTag(没有UI)从Activity中得到Fragment。
2. 使用popBackStack从back stack中弹出Fragment(模拟用户按下Back)。
2. 通过addOnBackStackChangedListener在back stack上注册一个listener。
像前面演示的那样,你可以使用FragmentManager来打开一个FragmentTransaction,他允许你进行一些transaction,比如添加,删除Fragment。
Perform Fragment Transactions
当用户与界面交互的时候,在Activity添加,删除,替换Fragment来响应用户。你提交给Activity的每一组变化叫做一个transaction,你可以使用FragmentTransaction来操纵。你也可以保存每个transaction到Activity的back stack中去,这样就可以让用户回。退到之前的Fragment了(类似于Activity的回退)。
你可以通过FragmentManager来取得一个FragmentTransaction的实例:
FragmentManager fragmentManager = getFragmentManager();
FragmentTransaction fragmentTransaction = fragmentManager.beginTransaction();
每个transaction都是一组你想在同一时间做的一些变化。你可以通过add,remove,replace来创建一组你想要的transaction。然后通过commit将他们提交给Activity。
在你调用commit之前,为了将这个transaction提交到一个back stack,你可以调用addToBackStack。Back stack是由Activity管理的,他允许用户通过按Back来回退到之前的Fragment状态。
比如,下面我们将一个Fragment替换成另外一个,并且把之前的状态保存在back stack中:
// 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();
在这个例子中,new Fragment 替换了当先id为fragment_container的Fragment。通过调用addToBackStack,replace transaction被保存到back stack中去了,所以用户按back的时候可以回退到之前的Fragment。
如果你在一个transaction中做了很多个改变(比如其他的add,remove操作),然后调用addToBackStack,所有这些改变被当做一个单独的transaction保存在backstack中,当用户点击Back的时候,将会回退整个所有的这些变化。
除了以下情况,你对transaction的设置的顺序是没有影响的:
1. 最后调用commit
2. 如果你向同一个container添加多个Fragment,那么他们被添加的顺序决定了他们在视图中显示的顺序。
如果你在移除一个Fragment的时候没有调用addToBackStack,那么这个Fragment将会被销毁,并且用户不能再回退到这个Fragment。反之,如果你在移除一个Fragment的时候调用了addToBackStack,那么这个Fragment is stopped,当用户按Back的时候他会 be resumed。
注意:对于每个Fragment transaction,你都可以在commit之前通过setTransition来设置一个Transition animation。
调用commit不会立即使transaction生效。他被放在UI线程的队列中,直到UI线程可以处理他得时候。如果非得这样不可,你可以通过调用executePendingTransactions来让你的transactions在commit之后立即执行。一帮来说这样做没有必要,除非其他线程依赖于这个transaction。
注意:你只能在Activity保存他的状态之前通过调用commit来提交一个transaction。如果你尝试在这之后commit,会抛出exception。这是因为当Activity需要被restore的时候,这次commit的状态没有被保留。但是你可以通过commitAllowingStateLoss来解决这个问题。
和Activity通讯
虽然我们在实现一个Fragment的时候是把它当做一个对象并且是独(敏感词汇我X)立于Activity,也可以被用在多个Activity中,但是一个Fragment是和他得宿主Activity绑定了的。
特别是我们在Fragment中可以通过getActivity来得到他的宿主Activity,能很轻松的做一些类似于查找Activity layout中的空间的工作。
View listView = getActivity().findViewById(R.id.list);
同时,你的Activity也可以通过findFragmentById或者findFragmentBytag得到Fragment的实例,从而调用Fragment中的方法。比如:
ExampleFragment fragment = (ExampleFragment)getFragmentManager().findFragmentById(R.id.example_fragment);
在Activity中创建回调函数
有的时候,你可能需要一个Fragment和Activity共享一些事件。很好的一个方式就是在Fragment中定义一个回调接口,然后让它的宿主Activity来实现他。当一个Activity接收到这个回调的时候,他可以再共享给这个Activity中的其他的一些Fragment。
比如,前面我们介绍的新闻应用程序,在Activity中有两个Fragment-一个现实标题列表(Fragment A)另外一个现实详细文章(Fragment B)-Fragment A需要告诉Activity它的一个标题被选中了,这样Activity才能告诉Fragment B去显示这篇文章,在这个例子中,OnArticleSelectedListener接口就被定义在Fragment A中:
public static class FragmentA extends ListFragment {
...
// Container Activity must implement this interface
public interface OnArticleSelectedListener {
public void onArticleSelected(Uri articleUri);
}
...
}
然后Fragment的宿主Activity就需要实现这个onArticleSelectedListener接口并且通知Fragment B。为了保证宿主Activity实现了这个接口,在FragmentA中得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 new ClassCastException(activity.toString()+ " must implement OnArticleSelectedListener");
}
}
...
}
如果Activity没有实现这个接口,Fragment会抛出一个ClassCastException。如果转换成功,Fragment就持有了Activity中对这个接口的实现的引用,这样Fragment A就可以喝Activity共享事件了。比如Fragment A是ListFragment的一个子类,当每次用户点击其中一项的时候,系统都会调用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);
}
...
}
通过id参数,Activity就可以找到对应的文章,从而通知Fragment B去显示。
向Action Bar中添加项
你的Fragment可以通过实现onCreateOptionsMenu向Activity的OptionsMenu添加菜单选项。你必须在onCreate中调用setHasOptionsMenu,这样就是说这个Fragment想要想菜单项中添加一些新的项(否则,Fragment的onCreateOptionsMenu不会被调用)。
你从Fragment中向Options Menu中添加的菜单项都被追加到现有的菜单项后面。当某项被选择的时候,Fragment的onOptionsItemSelected会被调用。
你也可以通过registerForContextMenu向你的Fragment layout中得一个View注册一个context menu。当用户打开context menu的时候,Fragment的onCreateContextMenu会被调用。当用户选择其中一项的时候,Fragment的onContextItemSelected会被调用。
注意:虽然对于这些你的Fragment添加的菜单项, Fragment的on-item-selected回调函数会被调用,但是,Activity会在第一时间接收到这些菜单选项的回调函数。如果Activity没有实现怎么处理这些项,这些事件才会被传送到Fragment中来处理。Options Menu和context Menu都是一样的。
处理Fragment的生命周期
管理Fragment的生命周期很像管理Activity的生命周期。像Activity一样,一个Fragment有三个状态:
1. Resumed
Fragment在运行中的Activity中可见,也可获取焦点。
2. Paused
另外一个Activity挡在前面,但是这个Activity当前是可见的(前面的Activity可能是透明的活着没有覆盖整个屏幕)
3. Stopped
Fragment不可见。可能是宿主Activity been stopped或者这个Fragment被移除同时被加入到back stack中了。一个stopped Fragment仍然存活着(他的状态和成员信息都被系统保存着)。但是,他即将不被用户看见,将可能随着Activity被杀掉而杀掉。
和Activity一样,当Activity被杀掉,当Activity被重新创建的时候你需要还原Fragment之前的状态,你可以通过Bundle来保存一个Fragment的状态。你可以在Fragment的onSaveInstanceState回调中保存这些状态,在onCreate,onCreateView或者onActivityCreated的时候还原这些状态。对于更多的信息,可以参见Activity的文档。
Activity和Fragment生命周期最大的不同是他们怎么被保存在back stack中得。Activity默认是被保存在系统管理的back stack中的。更多的信息可以参考Tasks and Back Stack。但是,Fragment只有当你显示的通过调用addToBackStack才能将这个transaction保存在Activity管理的back stack中。
其他方面,管理Fragment的生命周期和管理Activity的生命周期是差不多的。所以managing the activity lifecycle这篇文章也同样适用于Fragment。你同事需要知道的是Activity生命周期对Fragment生命周期的影响。
注意:如果你要再Fragment中使用context,你可以调用getActivity。但是,只有在Fragment被attached到一个Activity的时候再调用getActivity。如果Fragment还没有被attach到Activity,或者已经detach from Activity,getActivity会返回null。
Coordinating with the activity lifecycle
Fragment的宿主Activity的生命周期直接影响到Fragment的生命周期,Activity生命周期的回调也会致使Fragment中发生同样的回调。比如,当Activity的onPause被调用的时候,每个Fragment的onPause也会同样被调用。
Fragment只有很少的额外生命周期的回调,但是他们处理创建个销毁Fragment UI等的一些事情:
onAttach
当Fragment被分配到这个Activity的时候调用。
onCreateView
当创建Fragment的视图的时候调用
onActivityCreated
当Activity的onCreate方法返回的时候调用
onDestroyView
当这个Fragment的视图被移除的时候调用
onDetach
当Fragment从这个Activity移除的时候调用
如上图所示,Fragment被Activity影响的生命周期。比如,当Activity接受到onCreate回调的时候,Fragment不会接收到比onActivityCreated更多的回调了。
一旦Activity进入resumed状态,你就可以很自由的添加删除Fragment了。因此,只有在Activity进入resumed状态后,Fragment才又自己独(敏感词汇我X)立的生命周期。
但是,当Activity离开resumed状态后,Fragment的生命周期又开始被Activity的生命周期影响。