1. Fragment 是什么
Fragment是一个类, 它实现了一个Activity的一个部分.
• Fragment表示运行在一个更大的Activity中的具体的操作或者界面.
• Fragments使得Activity的设计更加模块化, 让应用程序适配不同的屏幕方向和多屏幕尺寸
更加容易.
• Fragments必须要嵌入到Activities中, 它们不能独立于Activities而运行.
• 大部分的Fragments定义它们自己的视图布局, 这些布局存在于它们所依附的Activities的
视图层次中.
然而, Fragment也可以实现没有用户界面组件的行为.
• Fragment有它们自己的生命周期, 并且它的生命周期与它依附的Activity是紧密关联的.
• Fragment可以是Activity的静态部分, 在Activity创建的工程中自动的实例化.
• 或者, 可以在运行时在Activity中动态的创建、添加和移除Fragments.
2. Fragments:在Honeycomb(3.0)或以后版本的实现
所有Fragment定义的基类
android.app.FragmentManager
在Activity中与Fragment对象进行交互的类
android.app.FragmentTransaction
对Fragment进行一系列原子操作的类
3. Fragments:在Dotnut(1.6)或以后版本的实现
所有使用基于兼容包中Fragment的特性的Activity的基类
android.support.v4.app.Fragment
所有Fragment定义的基类
android.support.v4.app.FragmentManager
在Activity中与Fragment对象进行交互的类
android.support.v4.app.FragmentTransaction
对Fragment进行一系列原子操作的类
4. 下载兼容包
5. 使用兼容包
6. Fragment生命周期
7. 创建Fragment类
8. 创建一个Fragment布局
为了给一个Fragment提供一个布局, 自定义的Fragment必须实现onCreateView回调方法.
• 当Fragment需要创建它的布局时Android系统会调用这个方法.
• 这个方法必须返回一个View, 这个View是Fragment的布局的根
和Activity一样, 可以编程实现直接实例化并配置View对象实现布局或者通过提供一个xml
布局文件来实现布局.
• 声明xml的方式通常更简单和容易
• 为了辅助声明式的方法, 系统在Activity的布局提供了LayoutInflater和ViewGroup的引用,
ViewGroup作为自定义Fragment布局的父布局.
9. 创建一个Fragment布局(例子)
<?xml version="1.0" encoding="utf-8"?>
<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
android:orientation="vertical"
android:layout_width="match_parent"
android:layout_height="match_parent"
android:id="@+id/first_fragment_root">
<TextView android:layout_height="wrap_content"
android:text="@string/text_first_fragment_title"
android:layout_width="match_parent"
android:gravity="center_horizontal" />
<EditText android:layout_height="wrap_content"
android:layout_width="match_parent"
android:id="@+id/edit_first_msg" />
<Button android:layout_height="wrap_content"
android:layout_gravity="center_horizontal"
android:layout_width="wrap_content"
android:id="@+id/button_first"
android:text="@string/button_first_text" />
</LinearLayout>
public class FirstFragment extends Fragment implements OnClickListener {
@Override
public View onCreateView(LayoutInflater inflater, ViewGroup container, Bundle savedInstanceState) {
View view = inflater.inflate(R.layout.first_fragment, container, false);
Button nextButton = (Button) view.findViewById(R.id.button_first);
nextButton.setOnClickListener(this);
return view;
}
// ...
}
10. 在Activity的布局中"静态地"包含Fragment
在Activity的布局文件中, 在你想要包括Fragment的地方使用<fragment>元素(是的, 确实是小写) .
• 使用android:name属性提供Fragment的全限定的类名
• 指定布局属性, 以控制Fragment的大小和位置
例如:
Activity布局
<?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>
11. 动态地把Fragment添加到Activity中
在Activity运行的任何时候, 都可以把Fragment添加到Activity的布局中.
1. 第一, 使用Activity.getFragmentManager()获得一个FragmentManager的引用.
如果使用了兼容包, 那么使用Activity.getSupportFragmentManager()
2. 调用FragmentManager.beginTransaction()获得一个FragmentTransaction实例.
3. 实例化一个自定义Fragment的实例.
4. 使用FragmentTransaction.add()方法把Fragment添加到Activity的一个ViewGroup中,
使用ID来指定Fragment. 或者, 也可以提供一个字符串标签来确定此Fragment.
5. 使用FragmentTransaction.commit()提交这个事务.
例如:
FragmentManager fragmentManager = getFragmentManager()
// Or: FragmentManager fragmentManager = getSupportFragmentManager()
FragmentTransaction fragmentTransaction = fragmentManager.beginTransaction();
ExampleFragment fragment = new ExampleFragment();
fragmentTransaction.add(R.id.fragment_container, fragment);
fragmentTransaction.commit();
12. 处理运行时配置更改
当系统因为运行时配置更改销毁并重建一个Activity时, Activity会自动重新实例化已经
存在的Fragment.
• 对于在Activity布局文件中声明的静态的Fragment不是问题.
• 但是对于动态的Fragment, 应该检测这种情况, 并防止自定义Fragment再次实例化.
为了检查系统是否重建了Activity, 检查Activity的onCreate()方法中传递的Bundle是否为null.
• 如果是non-null, 系统正在重新建立Activity, 这种情况下, Activity会自动重新实例化
已存在的Fragment.
• 如果是null的, 就可以安全地重新实例化动态的Fragment. 例如:
public void onCreate(Bundle savedInstanceState) {
// ...
if (savedInstanceState != null) {
FragmentManager fragmentManager = getFragmentManager()
// getSupportFragmentManager();<span style="font-family: Arial, Helvetica, sans-serif;"> </span>
FragmentTransaction fragmentTransaction = fragmentManager.beginTransaction();
ExampleFragment fragment = new ExampleFragment();
fragmentTransaction.add(R.id.fragment_container, fragment);
fragmentTransaction.commit();
}
}
13. 保存Fragment的状态
Fragment类支持onSaveInstanceState(Bundle)方法(而不是onRestoreInstanceState()) , 在很大程度
上与Activity类是一样的.
• 默认的实现保存所有有Id的Fragment中的视图的状态.
• 可以重写这个方法, 以保存Fragment的附加的状态信息.
• 如果系统从一个之前保存的状态重新创建Fragment, 系统在onCreate(), onCreateView(), 和
onActivityCreated()方法中都提供了一个Bundle的引用, 这个Bundle引用包含了之前保存的状态.
14. 跨Activity重建时保持Fragment
默认情况下, 当Activity重建时(比如响应运行时配置更改), 它的Fragment也会被销毁然后自动重建.
用true调用Fragment.setRetainInstance(boolean)方法请求系统如果重建Activity, 那么保留
当前Fragment的实例.
• 如果被设置了, 当Activity重建时, Fragment的onDestroy和onCreate方法不会被调用.
• Fragment的所有其他生命周期方法都会以他们典型的顺序被调用.
15. 使用不带布局的Fragment
Fragment不一定必须有用户界面.
• 例如:如果Fragment的唯一目的就是为了维护状态信息或者管理一个线程, 它就不需要用户界面.
当使用不带用户界面的Fragment时:
• 没有必要覆盖onCreateView()方法
• 必须通过FragmentTransaction.add(Fragment, String)把Fragment添加到Activity上,
提供一个唯一的String标签用于标识这个Fragment.
• 例如:
FragmentManager fragmentManager = getFragmentManager()
// Or: FragmentManager fragmentManager = getSupportFragmentManager()
FragmentTransaction fragmentTransaction = fragmentManager.beginTransaction();
BackgroundFragment fragment = new BackgroundFragment();
fragmentTransaction.add(fragment, "thread_manager");
fragmentTransaction.commit();
16. 检索一个Fragment
FragmentManager类提供了在Activity中查找一个Fragment的方法:
findFragmentById(int id)
根据指定的id查找Fragment
findFragmentByTag(String tag)
根据指定的tag查找Fragment
这两个方法返回一个Fragment的引用, 或者null如果没有找到匹配的Fragment.
17. Fragment操作
对于动态的Fragment除了把他们添加到Activity上, 还有许多其他的操作,如
移除它们或者改变它们的可见性.
• 提交到Activity的一系列更改叫做一个事务.
• 通过FragmentTransaction类中的方法对Fragment进行操作. 包括:
add()
把Fragment添加到Activity中.
remove()
从Activity中移除Fragment.这个方法会销毁Fragment实例, 除非这个事务被添加到
事务栈中.
replace()
在用户界面上移除一个Fragment并用另一个替换它.
hide()
在用户界面上隐藏一个Fragment(设置它的可见性为隐藏, 而不会销毁它的视图层次).
show()
显示之前被隐藏的Fragment.
detach()
从用户界面分离Fragment, 销毁其视图层次, 但不销毁Fragment实例.
attach()
把之前分离的Fragment重新附着到用户界面上, 重建其视图层次.
18. 执行Fragment事务
为了执行Fragment事务:
1. 通过调用FragmentManager.beginTransaction()方法获得一个FragmentTransaction实例.
2. 通过事务实例执行任何数量的Fragment操作.
3. 调用commit将事务提交到Activity.
FragmentManager fragmentManager = getFragmentManager()
// Or: FragmentManager fragmentManager = getSupportFragmentManager()
fragmentManager.beginTransaction()
.remove(fragment1)
.add(R.id.fragment_container, fragment2)
.show(fragment3)
.hide(fragment4)
.commit();
对FragmentTransaction的操作顺序不会有影响, 除非:
19. 管理Fragment回退栈
与系统自动维护一个Activity的回退栈类似, 可以选择把Fragment事务保存在一个由Activity
维护的回退栈.
• 如果把Fragment事务加到回退栈中, 那么用户可以通过点击设备的Back键导航回Fragment的变化.
• 一旦所有的Fragment事务都从回退栈移除后, 再次点击Back键将销毁Activity.
为了把Fragment事务添加到回退栈, 在提交事务之前调用
FragmentTransaction.addToBackStack(String)方法.
• String参数是一个可选的名字用于标示回退栈的状态或者是null, FragmentManager有一个
popBackStack()方法能够返回之前的回退栈状态, 并返回它的名字.
• 如果在一个事务中提交了多个更改, 并且了addToBackStack(), 那么在调用commit之前的所有更改
都被作为一个事务添加到回退栈中, 并且Back键会把他们一起回退掉.
如果在移除或者替换一个Fragment时调用addToBackStack()
• 当Fragment被放到回退栈后, 系统会调用这个Fragment的onPause(), onStop和onDestroyView.
• 如果用户点击Back键, 系统会调用onCreateView(),onActivityCreated(), onStart()和onResume().
20. 集成Fragment ActionBar/Options Menu Items
21. 集成Fragment ActionBar/Options Menu Items(例子)
Activity 源代码
public class MyActivity extends Activity {
// ...
@Override
public boolean onCreateOptionsMenu(Menu menu) {
super.onCreateOptionsMenu(menu);
getMenuInflater().inflate(R.menu.activity_options, menu);
return true;
}
@Override
public boolean onOptionsItemSelected(MenuItem item) {
switch (item.getItemId()) {
case R.id.menu_activity_info:
// Handle activity menu item
return true;
default:
// Handle fragment menu items
return super.onOptionsItemSelected(item);
}
}
// ...
}
Fragment 源代码
public class MyFragment extends Fragment {
// ...
@Override
public void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setHasOptionsMenu(true);
}
@Override
public void onCreateOptionsMenu(Menu menu, MenuInflater inflater) {
inflater.inflate(R.menu.myfragment_options, menu);
}
@Override
public boolean onOptionsItemSelected(MenuItem item) {
switch (item.getItemId()) {
case R.id.menu_first_info:
// Handle fragment menu item
return true;
default:
// Not one of ours. Perform default menu processing
return super.onOptionsItemSelected(item);
}
}
// ...
}
22. Fragment与Activity之间的通信
一个给定的Fragment实例与包含它的Activity是直接绑定的.View listView = getActivity().findViewById(R.id.list);
23. 最佳实践:Activities与Fragments松耦合
避免Activity与Fragment紧耦合
• 尤其是, 当你让Fragment太关心它的宿主Activity或者其他Fragment时, 就会增加它的复杂性,
同时降低了复用性.
• 另一方面, Activity需要对它持有的Fragments更加的关注.
24. 最佳实践:定义Fragment接口调用Activity的行为
public class TitlesFragment extends ListFragment {
private OnTitleSelectedListener listener;
public interface OnTitleSelectedListener {
public void onTitleSelected(int index);
}
public void onAttach(Activity activity) {
super.onAttach(activity);
try {
showSecondFragmentListener = (OnTitleSelectedListener) activity;
} catch (ClassCastException e) {
throw new ClassCastException(activity.toString()
+ " must implement OnTitleSelectedListener");
}
}
@Override
public void onListItemClick(ListView l, View v, int position, long id) {
listener.onTitleSelected(position);
}
// ...
}
Activity源代码
public class MyActivity extends Activity
implements TitlesFragment.OnTitleSelectedListener {
public void onTitleSelected(int index) {
// ...
}
// ...
}
25. 最佳实践:Activity作为切换器
26. Fragment高级初始化
27. Fragment高级初始化:参数
• 实例化一个Fragment之后, 可以给他提供一系列的参数
创建一个Bundle对象, 把要作为参数传递给Fragment的任何值放到Bundle中.
在这个Fragment中调用setArguments(Bundle), 传递这个Bundle.
• 在Fragment内, 当准备好接受参数的时候调用getArguments().
• 通常, 尤其是Fragment支持仅1个或者2个参数时, 定义一个静态的工厂方法实例化Fragment
并传递参数是最简单的方法.
Fragment源代码
public static class DetailsFragment extends Fragment {
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);
}
// ...
}
Activity源代码
DetailsFragment detail = DetailsFragment.newInstance(2);
28. 用Fragment实现对话框
29. 使用基于Fragment的Dialog
30. 例子: 一个简单的确认DialogFragment
public class ConfirmationDialogFragment extends DialogFragment
implements DialogInterface.OnClickListener {
private ConfirmationDialogFragmentListener listener;
public static ConfirmationDialogFragment newInstance(int title) {
ConfirmationDialogFragment frag = new ConfirmationDialogFragment();
Bundle args = new Bundle();
args.putInt("title", title);
frag.setArguments(args);
return frag;
}
public interface ConfirmationDialogFragmentListener {
public void onPositiveClick();
public void onNegativeClick();
}
public void setConfirmationDialogFragmentListener(
ConfirmationDialogFragmentListener listener) {
this.listener = listener;
}
@Override
public Dialog onCreateDialog(Bundle savedInstanceState) {
int title = getArguments().getInt("title");
return new AlertDialog.Builder(getActivity())
.setIcon(android.R.drawable.ic_dialog_alert)
.setTitle(title)
.setPositiveButton(android.R.string.ok, this)
.setNegativeButton(android.R.string.cancel, this)
.create();
}
@Override
public void onClick(DialogInterface dialog, int which) {
if (listener != null) {
switch (which) {
case DialogInterface.BUTTON_POSITIVE:
listener.onPositiveClick();
default:
listener.onNegativeClick();
}
}
}
}
Activity源代码
public class SimpleConfirmationDialogFragmentActivity
extends FragmentActivity
implements OnClickListener, ConfirmationDialogFragmentListener {
@Override
public void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.main);
Button buttonPostDialog = (Button) findViewById(R.id.button_post_dialog);
buttonPostDialog.setOnClickListener(this);
}
@Override
public void onClick(View v) {
ConfirmationDialogFragment confirmationDialog
= ConfirmationDialogFragment.newInstance(R.string.dialog_format_title);
confirmationDialog.setConfirmationDialogFragmentListener(this);
confirmationDialog.show(getSupportFragmentManager(), null);
}
@Override
public void onPositiveClick() {
Toast.makeText(this, android.R.string.ok, Toast.LENGTH_LONG).show();
}
@Override
public void onNegativeClick() {
Toast.makeText(this, android.R.string.cancel, Toast.LENGTH_LONG).show();
}
}