Android3.0(API11)开始引入Fragment,主要是为了在大屏幕上更好的支持动态和灵活的UI设计。以平板为例,有足够大的空间容纳更多的组件,管理起来无疑过于麻烦。Fragment能够分割Activity的layout,而且具备本身的生命周期,从而使得组件独立管理成为可能。正由于fragment具备独立的生命周期,因此能够定义本身的layout和行为,从而一个fragment能够在多个activity中重复调用。因此咱们在设计fragment的时候,要尽可能追求模块化和可重用性。通常在平板和TV上广泛使用。android
举例以下:在平板上,activity使用一个fragment展现文章列表,使用另外一个fragment展现文章内容;在手机上,activity使用一个fragment展现文章列表,点击跳转到另外一个activity展现。bash
fragment的生命周期和activity很是相似,都有onCreate()、onStart()、onStop()、onPause()回调方法。fragment通常至少要实现三个回调方法:app
常见的有三种fragment,分别是DialogFragment、ListFragment、PreferenceFragmentide
fragment通常具备本身的UI,并成为activity UI的一部分。为了给fragment建立UI,必须实现onCreateView()回调方法,并返回一个view(fragment的根layout)。模块化
若是fragment是ListFragment的子类,onCreateView()默认返回ListView,不须要重写。布局
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是activity的layout,fragment的layout将会被插入其中;savedInstanceState即保存的fragment被异常销毁前的状态;inflate()有三个参数:ui
通常来讲,fragment的layout会被嵌入成为activity view层级的一部分。有两种方式能够把fragment添加到activity的layout中:1.静态加载;2.动态加载。this
静态加载:在activity layout中声明fragment,代码以下:spa
<?xml version="1.0" encoding="utf-8"?>
<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:app="http://schemas.android.com/apk/res-auto"
xmlns:tools="http://schemas.android.com/tools"
android:layout_width="match_parent"
android:layout_height="match_parent"
app:layout_behavior="@string/appbar_scrolling_view_behavior"
tools:context="com.example.chaos.myapplication.MainActivity"
tools:showIn="@layout/activity_main">
<fragment
android:id="@+id/fragment_person_list"
android:name="com.example.chaos.myapplication.PersonListFragment"
android:layout_width="0dp"
android:layout_height="match_parent"
android:layout_weight="1" />
<fragment
android:id="@+id/fragment_person_detail"
android:name="com.example.chaos.myapplication.DetailFragment"
android:layout_width="0dp"
android:layout_height="match_parent"
android:layout_weight="1" />
</LinearLayout>复制代码
系统建立该activity的layout时,会将指定的fragment所有实例化并分别调用他们的onCreateView()回调。而后检索fragment的layout,并将onCreateView()返回的view取代<fragment>
元素直接插入activity的layout中。线程
activity重启时,须要恢复fragment的数据,这就要求fragment要有个惟一的标识符(不然没法管理fragment)。有三种方式能够为fragment赋予惟一的id
- 使用android:id属性赋予id
- 使用android:tag属性赋予惟一标签
- 若是id和tag都没提供的话,系统模式使用container view的id
2.. 动态加载:动态添加fragment到ViewGroup中
只要在activity在运行,均可以添加fragment到activity的layout中,只须要指定ViewGroup存放fragment就行。
为了在activity中操做fragment的事务(add、remove、replace),必须使用FragmentTransaction()。代码以下:
FragmentManager fragmentManager = getFragmentManager();
FragmentTransaction fragmentTransaction = fragmentManager.beginTransaction();复制代码
添加fragment使用add()方法,指定ViewGroup和要添加的fragment,对fragmentTransaction作的任何改动都要执行commit()方法才能生效。
ExampleFragment fragment = new ExampleFragment();
fragmentTransaction.add(R.id.fragment_container, fragment);
fragmentTransaction.commit();复制代码
并不是全部fragment都必须有UI,fragment也能够用来执行后台操做,此时,无需UI也能够。该如何添加一个没有UI的fragment呢?
首先,须要先经过findFragmentByTag()找到,由于tag是惟一能标识它的(只能经过add(fragment,string)主动设置tag)。
若是findFragmentByTag()返回null的话,须要经过add(fragment,string)为fragment主动设置tag并添加。
fragment的管理是由FragmentManager实现的,能够在activity中经过getFragmentManager()获取FragmentManager。FragmentManager的功能以下:
静态加载的fragment必定具备id或tag;动态加载的id必定具备container view的id或tag(fragment若是没有惟一标识符,则没法进行有效管理),动态加载一共有三个方法:
add(Fragment fragment, String tag)
:适用于有UI和无UI的fragmentadd(@IdRes int containerViewId, Fragment fragment)
:适用于有UI的fragmentadd(@IdRes int containerViewId, Fragment fragment,String tag);
:适用于有UI的fragment因此没有UI的fragment,只能经过findFragmentByTag()获取;有UI的fragment能够经过id或tag获取。
也可使用FragmentManager打开FragmentTransaction,从而操做fragment的事务,如添加或移除。
一个事务就是一系列变化,事务具备原子性、一致性、隔离性、持久性。可使用FragmentTransaction实现事务,也能够把事务放到由activity管理的返回栈中,便于用户返回以前的fragment的状态.
能够从FragmentManager获取FragmentTransaction实例
FragmentManager fragmentManager = getFragmentManager();
FragmentTransaction fragmentTransaction = fragmentManager.beginTransaction();复制代码
FragmentTransactioni能够借由add()、remove()、replace()实现对事务的操做,并且每次操做必须实现commit()才能生效。
若是想把fragment加入到返回栈,以便用户退回到fragment原先的状态的话,须要在commit()以前调用addBackStack()方法:
DetailFragment detailFragment = new DetailFragment();
fragmentTransaction.add(detailFragment,"");
fragmentTransaction.addToBackStack(null);
fragmentTransaction.commit();复制代码
为了检索返回栈中的fragment,必须监听onBackPressed()事件,不然的话若是栈中存在其余activity的话,就会直接返回到其余activity中;若是没有activity的话,就会直接退出应用。
@Override
public void onBackPressed() {
if (getFragmentManager().getBackStackEntryCount() > 0){
getFragmentManager().popBackStack();
}else {
super.onBackPressed();
}
}复制代码
添加改变到Transaction中的顺序可有可无,除非:
若是没有调用addBackStack(),当remove一个fragment时,commit以后会马上销毁,用户没法返回;若是添加到返回栈中,remove一个fragment时,commit以后会处于stop状态,用户能够返回到以前的状态(resume)
commit()这个操做运行在主线程中,也就意味着调用commit()以后要排序执行,并不会马上执行事务。若是想马上执行的话,能够在commit()以前调用excutePendingTransactions(),不过通常并没有此必要。
commit()必须在onSaveInstanceState()以前调用,由于在此以后,activity不会保存数据,致使commit()调用会抛出异常;若是容许状态数据丢失的话,能够调用 commitAllowingStateLoss()。
虽然fragment做为对象的实现和activity存在很大不一样,可是做为activity的一部分,fragment又和activity紧密联系。
View listView = getActivity().findViewById(R.id.list);
DetailFragment detailFragment1 = (DetailFragment) getSupportFragmentManager().findFragmentById(R.id.fragment_person_detail);复制代码
fragment建立接口和方法:
public static class FragmentA extends ListFragment {
...
// Container Activity must implement this interface
public interface OnArticleSelectedListener {
public void onArticleSelected(Uri articleUri);
}
...
}复制代码
把activity转换成接口,从而实现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");
}
}
...
}复制代码
在fragment的onListItemClick事件中调用传值方法给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); } ... }复制代码
和activity同样,fragment也有三种基本状态:
fragment和activity生命周期的一个最大差异在于两者在返回栈中的存储。activity的返回栈是由系统管理的;fragment的返回栈是由activity管理的(当fragment被remove时,调用addBackStack())。
fragment若是须要context对象的话,能够调用getActivity(),可是这个方法要在onAttach()以后、onDetach()以前调用才有效,不然将会返回null。