编写:fastcome1985 - 原文:https://developer.android.com/training/basics/fragments/creating.html
创建Fragment
可以把 Fragment 想象成 Activity 的模块,它拥有自己的生命周期、接收输入事件,可以在 Acvitity 运行过程中添加或者移除(有点像“子 Activity”,可以在不同的 Activity 里重复使用)。
创建Fragment
继承并创建 Fragment,然后在关键的生命周期方法中插入代码(就和在处理 Activity 时一样)。
其中一个区别是:创建 Fragment 时,必须重写 onCreateView() 回调方法来定义布局。事实上,这是唯一一个为使 Fragment 运行起来需要重写的回调方法。比如,下面是一个自定义布局的示例 Fragment:
import android.os.Bundle;
import android.support.v4.app.Fragment;
import android.view.LayoutInflater;
import android.view.ViewGroup;
public class ArticleFragment extends Fragment {
@Override
public View onCreateView(LayoutInflater inflater, ViewGroup container,
Bundle savedInstanceState) {
// 拉伸该 Fragment 的布局
return inflater.inflate(R.layout.article_view, container, false);
}
}
和 Activity 一样,当 Fragment 从 Activity 添加或者移除、或 Activity 生命周期发生变化时,Fragment 通过生命周期回调函数管理其状态。例如,当 Activity 的 onPause() 被调用时,它内部所有 Fragment 的 onPause() 方法也会被触发。
更多关于 Fragment 的声明周期和回调方法,详见 Fragments 开发指南.
用 XML 将 Fragment 添加到 Activity
Fragments 是可重用的、模块化的 UI 组件。每个 Fragment 实例都必须与一个 FragmentActivity 关联。我们可以在 Activity 的 XML 布局文件中逐个定义 Fragment 来实现这种关联。
注: FragmentActivity 是 Support Library 提供的一种特殊 Activity,用于处理 API 11 版本以下的 Fragment。如果我们 APP 中的最低版本大于等于 11,则可以使用普通的 Activity。
以下是一个 XML 布局的例子:当屏幕被认为是 "large"(用目录名称中的 large
字符来区分)时,它在布局中增加了两个 Fragment。
res/layout-large/news_articles.xml
<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
android:orientation="horizontal"
android:layout_width="fill_parent"
android:layout_height="fill_parent">
<fragment android:name="com.example.android.fragments.HeadlinesFragment"
android:id="@+id/headlines_fragment"
android:layout_weight="1"
android:layout_width="0dp"
android:layout_height="match_parent" />
<fragment android:name="com.example.android.fragments.ArticleFragment"
android:id="@+id/article_fragment"
android:layout_weight="2"
android:layout_width="0dp"
android:layout_height="match_parent" />
</LinearLayout>
提示: 更多关于不同屏幕尺寸创建不同布局的信息,请阅读 兼容不同屏幕尺寸。
然后将这个布局文件用到 Activity 中。
import android.os.Bundle;
import android.support.v4.app.FragmentActivity;
public class MainActivity extends FragmentActivity {
@Override
public void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.news_articles);
}
}
如果使用 v7 appcompat 库,Activity 应该改为继承自 AppCompatActivity,AppCompatActivity 是 FragmentActivity 的子类(更多关于这方面的内容,请阅读 添加 App Bar)。
注: 当通过 XML 布局文件的方式将 Fragment 添加进 Activity 时,Fragment 是不能被动态移除的。如果想要在用户交互的时候把 Fragment 切入与切出,必须在 Activity 启动后,再将 Fragment 添加进 Activity。
使用Fragment创建动态UI
你可以在 Activity 运行时向其添加 Fragment,而不用使用 <fragment>
元素在布局文件中为 Activity 定义 Fragment。如果你打算在 Activity 运行周期内更改 Fragment,就必须这样做。
要执行添加或移除 Fragment 等事务,你必须使用 FragmentManager 创建一个 FragmentTransaction,后者可提供用于执行添加、移除、替换以及其他 Fragment 事务的 API。
如果 Activity 中的 Fragment 可以移除和替换,你应在调用 Activity 的 onCreate() 方法期间为 Activity 添加初始 Fragment(s)。
在处理 Fragment(特别是在运行时添加的 Fragment)时,请谨记以下重要规则:必须在布局中为 Fragment 提供 View 容器,以便保存 Fragment 的布局。
要用一个 Fragment 替换另一个 Fragment,Activity 的布局中需要包含一个作为 Fragment 容器的空 FrameLayout。
请注意,该文件名与XML中布局文件的名称相同,但布局目录没有 large
这一限定符。因此,此布局会在设备屏幕小于“large”的情况下使用,原因是尺寸较小的屏幕不适合同时显示两个 Fragment。
res/layout/news_articles.xml:
<FrameLayout xmlns:android="http://schemas.android.com/apk/res/android"
android:id="@+id/fragment_container"
android:layout_width="match_parent"
android:layout_height="match_parent" />
在 Activity 中,用 Support Library API 调用 getSupportFragmentManager() 以获取 FragmentManager,然后调用 beginTransaction() 创建 FragmentTransaction,然后调用 add() 添加 Fragment。
你可以使用同一个 FragmentTransaction 对 Activity 执行多 Fragment 事务。当你准备好进行更改时,必须调用 commit()。
例如,下面介绍了如何为上述布局添加 Fragment:
import android.os.Bundle;
import android.support.v4.app.FragmentActivity;
public class MainActivity extends FragmentActivity {
@Override
public void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.news_articles);
// 确认 Activity 使用的布局版本包含 fragment_container FrameLayout
if (findViewById(R.id.fragment_container) != null) {
// 不过,如果我们要从先前的状态还原,则无需执行任何操作而应返回,否则
// 就会得到重叠的 Fragment。
if (savedInstanceState != null) {
return;
}
// 创建一个要放入 Activity 布局中的新 Fragment
HeadlinesFragment firstFragment = new HeadlinesFragment();
// 如果此 Activity 是通过 Intent 发出的特殊指令来启动的,
// 请将该 Intent 的 extras 以参数形式传递给该 Fragment
firstFragment.setArguments(getIntent().getExtras());
// 将该 Fragment 添加到“fragment_container” FrameLayout 中
getSupportFragmentManager().beginTransaction()
.add(R.id.fragment_container, firstFragment).commit();
}
}
}
由于该 Fragment 已在运行时添加到 FrameLayout 容器中,而不是在 Activity 布局中通过 <fragment>
元素进行定义,因此该 Activity 可以移除和替换这个 Fragment。
用一个 Fragment 替换另一个 Fragment
替换 Fragment 的步骤与添加 Fragment 的步骤相似,但需要调用 replace() 方法,而非 add()。
请注意,当你执行替换或移除 Fragment 等 Fragment 事务时,最好能让用户向后导航和“撤消”所做更改。要通过 Fragment 事务允许用户向后导航,你必须调用 addToBackStack(),然后再执行 FragmentTransaction。
注: 当你移除或替换 Fragment 并向返回堆栈添加事务时,已移除的 Fragment 会停止(而不是销毁)。如果用户向后导航,还原该 Fragment,它会重新启动。如果你没有向返回堆栈添加事务,那么该 Fragment 在移除或替换时就会被销毁。
替换 Fragment 的示例:
// 创建 Fragment 并为其添加一个参数,用来指定应显示的文章
ArticleFragment newFragment = new ArticleFragment();
Bundle args = new Bundle();
args.putInt(ArticleFragment.ARG_POSITION, position);
newFragment.setArguments(args);
FragmentTransaction transaction = getSupportFragmentManager().beginTransaction();
// 将 fragment_container View 中的内容替换为此 Fragment,
// 然后将该事务添加到返回堆栈,以便用户可以向后导航
transaction.replace(R.id.fragment_container, newFragment);
transaction.addToBackStack(null);
// 执行事务
transaction.commit();
addToBackStack() 方法可接受可选的字符串参数,来为事务指定独一无二的名称。除非你打算使用 FragmentManager.BackStackEntry API 执行高级 Fragment 操作,否则无需使用此名称。
Fragment之间的交互
定义接口
为了让 Fragment 与包含它的 Activity 进行交互,可以在 Fragment 类中定义一个接口,并在 Activity 中实现。该 Fragment 在它的 onAttach() 方法生命周期中获取该接口的实现,然后调用接口的方法,以便与 Activity 进行交互。(译注:意即,若该 Fragment 中实现了 onAttach() 方法,则会被自动调用。)
以下是 Fragment 与 Activity 交互的例子:
public class HeadlinesFragment extends ListFragment {
OnHeadlineSelectedListener mCallback;
// 容器 Activity 必须实现该接口
// (译注:“容器 Activity”意即“包含该 Fragment 的 Activity”)
public interface OnHeadlineSelectedListener {
public void onArticleSelected(int position);
}
@Override
public void onAttach(Activity activity) {
super.onAttach(activity);
// 确认容器 Activity 已实现该回调接口。否则,抛出异常
try {
mCallback = (OnHeadlineSelectedListener) activity;
} catch (ClassCastException e) {
throw new ClassCastException(activity.toString()
+ " must implement OnHeadlineSelectedListener");
}
}
...
}
现在 Fragment 可以通过调用 mCallback
(OnHeadlineSelectedListener
接口的实例)的 onArticleSelected()
方法(也可以是其它方法)与 Activity 进行消息传递。
例如,当用户点击列表条目时,Fragment 中的下面的方法将被调用。Fragment 用回调接口将事件传递给父 Activity。
@Override
public void onListItemClick(ListView l, View v, int position, long id) {
// 向宿主 Activity 传送事件
mCallback.onArticleSelected(position);
}
实现接口
为了接收回调事件,宿主 Activity 必须实现在 Fragment 中定义的接口。
例如,下面的 Activity 实现了上面例子中的接口。
public static class MainActivity extends Activity
implements HeadlinesFragment.OnHeadlineSelectedListener{
...
public void onArticleSelected(int position) {
// 用户从 HeadlinesFragment 选择了一篇文章的标题
// 在这里做点什么,以显示该文章
}
}
向 Fragment 传递消息
宿主 Activity 通过 findFragmentById() 获取 Fragment 的实例,然后直接调用 Fragment 的 public 方法向 Fragment 传递消息。
例如,假设上面所示的 Activity 可能包含另一个 Fragment,该 Fragment 用于展示从上面的回调方法中返回的指定的数据。在这种情况下,Activity 可以把从回调方法中接收到的信息传递到这个展示数据的 Fragment。
public static class MainActivity extends Activity
implements HeadlinesFragment.OnHeadlineSelectedListener{
...
public void onArticleSelected(int position) {
// 用户从 HeadlinesFragment 选择了一篇文章的标题
// 在这里做点什么,以显示该文章
ArticleFragment articleFrag = (ArticleFragment)
getSupportFragmentManager().findFragmentById(R.id.article_fragment);
if (articleFrag != null) {
// 若 articleFrag 有效,则表示我们正在处理两格布局(two-pane layout)……
// 调用 ArticleFragment 的方法,以更新其内容
articleFrag.updateArticleView(position);
} else {
// 否则,我们正在处理单格布局(one-pane layout)。此时需要 swap frags...
// 创建 Fragment,向其传递包含被选文章的参数
ArticleFragment newFragment = new ArticleFragment();
Bundle args = new Bundle();
args.putInt(ArticleFragment.ARG_POSITION, position);
newFragment.setArguments(args);
FragmentTransaction transaction = getSupportFragmentManager().beginTransaction();
// 无论 fragment_container 视图里是什么,用该 Fragment 替换它。并将
// 该事务添加至回栈,以便用户可以往回导航(译注:回栈,即 Back Stack。
// 在有多个 Activity 的 APP 中,将这些 Activity 按创建次序组织起来的
// 栈,称为回栈)
transaction.replace(R.id.fragment_container, newFragment);
transaction.addToBackStack(null);
// 执行事务
transaction.commit();
}
}
}
网友评论