创建Fragment
只有通过创建Fragment的子类来获取Fragment实例. Fragment的代码和Activity十分相似. 他也拥有和Activity类似的生命周期方法. 比如
onCreate()
,onStart()
,onPause()
和onStop()
等方法. 事实上, 如果将将一个使用Activity的Application转换成使用Fragment只需要Activity中的回调方法中的代码拷贝到Fragment对应的方法中即可.
通常情况下, 以下三个生命周期方法是需要重写的 :
onCreate()
系统会在创建Fragment的时候调用这个方法, 在这个方法中可以初始化Fragment需要的组件.
onCreateView()
当系统第一次准备绘制Fragment界面时会调用这个方法, 为了绘制我们需要的UI我们需要在这个方法中返回布局的根布局. 如果Fragment没有UI则可以返回null.
onPause()
当用户离开Fragment的时候, 系统首先会调用这个方法. 通常情况下并不是Fragment被销毁了. 通常需要在这个方法中保存所有需要保存的数据和状态, 用户有可能不会再返回来.
大部分的Application都需要为每一个Fragment至少重写上面的三个方法, Fragment也有一些其他的方法.
还有一些系统提供的Fragment的子类可以使用.
- DialogFragment
- ListViewFragment
- PreferenceFragment
添加UI
Fragment通常用作Activity UI的一部分. 为了为Fragment提供一个布局文件, 需要重写
onCreateView()
回调方法. 在系统绘制Fragment UI 的时候会调用这个方法. 在这个方法中需要返回Fragment布局的根布局.注意 : 如果使用
ListViewFragment
默认情况下会返回一个ListView, 因此不需要重写这个方法.
为了方便加载布局资源中的布局文件, onCreateVIew()
方法提供了一个LayoutInflate
. 比如下面是一个Fragment的子类加载一个xml布局文件(example_fragment.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);
}
}
其中container
参数是Fragment要插入的父布局. 当Fragment resume
后savedInstanceState
是一个Bundle
存储是一个实例的数据.
注意 : inflate()
方法的最后一个参数必须是false
, 因为系统已经将当前的布局加载到父布局中, 如果这里使用true
就会出现重复加的问题.
将Fragment添加到Activity
有两种方式将Fragment添加到Activity的布局当中.
方式一 : 在Activity的布局文件中声明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>
android:name
属性指定了实例化Fragment的类 . 需要使用Fragment的子类的全类名.当系统创建这个Activity的时候会自动创建布局中指定的所有的Fragment类. 并且自动调用他们的额onCreateView()
方法.
注意 : 为了在Activity重启后回复Fragment, 因此每一Fragment都有一个唯一的ID. 有三种方式提供ID.
- 使用 android:id
属性设置一个唯一的ID.
- 使用 android:tag
属性设置一个唯一的string.
- 如果上面两个都没有设置则系统会默认使用 container
的ID.
方式二 : 使用代码动态将Fragment添加到一个ViewGroup上.
在Activity运行期间(Resumed State)都可以将一个Fragment添加到指定的ViewGroup上.
使用FragmentTransaction
相关的API来处理事务. 在Activity中获取方法如下.
FragmentManager fragmentManager = getFragmentManager()
FragmentTransaction fragmentTransaction = fragmentManager.beginTransaction();
然后通过 add()
方法将一个Fragment添加到制定的ViewGroup
上
ExampleFragment fragment = new ExampleFragment();
fragmentTransaction.add(R.id.fragment_container, fragment);
fragmentTransaction.commit();
第一个参数 : 要插入的ViewGroup
的资源ID.
第二个参数 : 带插入的Fragment 对象.
只要进行了修改都需要调用commit()
方法进行事务的提交.
管理Fragment
为了管理Fragment, 需要在Activity中调用getFragmentManager()
方法获取到FragmentManager
对象. 使用FragmentManager
可以做如下操作 :
- 使用
findFragmentById()
或者findFragmentByTag()
获取Activity中已经存在的Fragment. - 从返回栈中弹出Fragment, 使用
popBackStack()
方法. - 使用
addOnBackStackChangedListener()
为返回栈注册一个变化监听者.
详细用法参考 FragmentManager
的相关文档.
使用Transactions
在Activity中使用Fragment的最大的好处就是在响应用户操作的时候他可以进行添加、删除、替换以及其他操作. 每一系列的改变都可以都是放在一个Transaction中的. 同时也可以进Translation保存到返回栈中.
可以下面的方式那样开启Translation
FragmentManager fragmentManager = getFragmentManager();
FragmentTransaction fragmentTransaction = fragmentManager.beginTransaction();
每一个Translation都是一系列我们想要同时进行的操作, 我们可以使用Translation提供的方法比如add()
、remove()
和replace()
等方法在Translation中建立我们想要进行的所有的操作, 最后通过调用commit()
方法使设置生效.
在调用commit()
之前可以通过调用addToBackStack()
方法将Translation添加到返回栈中.
下面的例子中实现了替换Fragment, 并且将Translation保存到返回栈中.
// 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();
添加操作的顺序是没有关系的, 但是以下两点需要特别注意 :
- 最后必须调用 commit()
方法.
- 如果你添加了多个Fragment到同一个ViewGroup. 此时添加的顺序会对他们在视图层次结构中的位置产生影响.
当你执行一个删除Fragment的Translation的时候如果你没有调用addToBackStack()
, 那么当Translation提交后Fragment就会被销毁无法找回. 但是如果你在提交前调用了addToBackStack()
方法那么Fragment是不会被销毁的, 只是出于Stop状态, 可以找回.
Tip : 在你提交Translation之前可以使用
setTransition()
方法设置一个动画.
调用了commit()
方法后并不一定立即执行Translation.而是当线程有机会就会将这个Translation调度到UI 线程. 如果真的想立即执行可以调用executePendingTransactions()
, 一般情况下没有必要这么做, 除非他是其他线程的一个依赖.
注意 :
commit()
方法只能在Activity执行saving its state(用户离开Activity的时候)
之前执行. 否则就会抛出异常, 因为在Activity恢复的过程中commit的Translation会丢失. 在这种情况下可以使用commitAllowingStateLoss()
提交Translation.
Fragment和Activity通信
尽管一个Fragment是一个独立的对象, 可以应用到多个Activity中. 但是一个Fragment对象和他嵌入的Activity是有直接联系的. Fragment可以通过
getActivity()
方法获取到Activity并且可以很方便的执行操作, 比如获取Activity 布局中的元素等.
View listView = getActivity().findViewById(R.id.list);
同样的Activity也可以通过findFragmentById()
和 findFragmentByTag()
方法获取到指定的Fragment.
ExampleFragment fragment = (ExampleFragment) getFragmentManager().findFragmentById(R.id.example_fragment);
通信方式 : 创建可以发送给Activity的回调事件
在某些情况下,你需要将fragment发送的事件通知给Activity. 一种不错的做法是在fragment内部定义一个回调接口, 然后让宿主Activity去实现这个接口. 这样当Activity接收到回调的信息后如果有需要可以传递给其他的fragment.
举例 : 比如有一个新闻类的APP有两个Fragment, 一个用来展示新闻列表(FragmentA), 另一个用来展示文章的详细信息(FragmentB). 此时FragmentA就需要通知Activity用户点击了哪一个Item以便Activity告诉FragmentB应该显示哪一个文章. 在这种情况下 回调接口OnArticleSelectedListener
的写法如下 :
public static class FragmentA extends ListFragment {
// ...
// Container Activity must implement this interface
public interface OnArticleSelectedListener {
public void onArticleSelected(Uri articleUri);
}
// ...
}
然后宿主Activity必须实现OnArticleSelectedListener
接口, 并且在onArticleSelected()
方法中将FragmentA传递过来的URL发送给FragmentB.为了确保宿主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 new ClassCastException(activity.toString() + " must implement OnArticleSelectedListener");
}
}
...
}
如果强制类型转换失败则会抛出 ClassCastException
异常. 成功了Fragment就可以持有一个OnArticleSelectedListener
实例对象的引用. 以便FragmentA可以通过调用OnArticleSelectedListener
中的方法通知宿主Activity触发的事件.
例子 : 一个ListFragment
的子类, 每次用户点击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 the content provider Uri
Uri noteUri = ContentUris.withAppendedId(ArticleColumns.CONTENT_URI, id);
// Send the event and Uri to the host activity
mListener.onArticleSelected(noteUri);
}
...
}
管理Fragment生命周期
管理Fragment的生命周期和管理Activity的声明周期非常相似. 像Activity的声明周期一样Fragment的声明周期也存在三个状态.
Resumed
在一个运行的Activity中可见的Fragment.
Paused
宿主Activity失去焦点, 但是还没有完全被隐藏.
Stop
fragment不可见了, 有可能是宿主Activity处于Stop状态, 也有可能是Fragment被从宿主Activity中移除了, 但是有一个前提是fragment被添加到了返回栈中. 处于stop状态的fragment是没有被销毁的所有的状态数据都保存完好. 但是用户不可见, 并且当Activity被销毁时他也就被销毁了.
和Activity一样, 你同样可以使用一个Bundle
对象来保存状态数据, 以便在Activity重新创建后恢复Fragment. 可以在fragment的onSaveInstanceState()
回调方法中保存数据.然后在回调方法onCreate()
, onCreateView()
, onActivityCreated()
中恢复数据.
Fragment和Activity的生命周期的最大的不同之处是对于返回栈的保存处理
默认情况下Activity 被stop后由系统自动将Activity添加到返回栈中.但是Fragment需要手动调用addToBackStack()
方法才可以将remove的Fragment添加到返回栈中.
注意 : 如果你在Fragment的内部需要一个Context对象, 可以通过
getActivity()
方法获取到. 但是需要注意的是只有在Fragment 添加到Activity中后才会有效, 如果Fragment还没有添加到Activity或者已经从Activity中解除移除则getActivity()
方法会返回null.
Fragment和Activity生命周期的协调
Fragment的生命周期中和Activity生命周期中一样的回调方法都是直接联系的比如, 当Activity的onPause()
方法被调用, fragment的onPause()
方法也会被调用. Fragment也有一些自己独有的方法. 这些方法如下 :
onAttach()
当Fragment和Activity进行绑定时调用. Activity在此时传入Fragment.
onCreateView()
创建和Fragment关联的视图结构时调用.
onActivityCreated()
当Activity的onCreate()
方法返回时调用.
onDestroyView()
当和Fragment关联的视图结构销毁时调用.
onDetach()
当fragment和Activity解除关系时调用.
Fragment的一系列的生命周期回调方法都被他的宿主Activity的生命周期回调方法影响着.
一旦Activity处于Resumed状态, 你可以进行自由的添加, 删除, 替换等操作.但是只有Activity处于Resumed的状态下Fragment的生命周期才是自由的. 否则都是受Activity影响的.