Android 学习笔记之 Fragment

本文整理自:Google官方文档中文学习手册,笔者省略了对自己帮助不大的章节,拜读原文请点链接。

一、动态创建灵活的 Fragment

1.在运行时向 Activity 添加 Fragment

你可以在 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 替换另一个 Fragment,Activity 的布局中需要包含一个作为 Fragment 容器的空 FrameLayout

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。

2.用一个 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 之间可能会需要交互,比如基于用户事件的内容变更。所有 Fragment 之间的交互应通过与之关联的 Activity 来完成。两个 Fragment 之间不应直接交互。

1.定义接口

为了让 Fragment 与包含它的 Activity 进行交互,可以在 Fragment 类中定义一个接口,并在 Activity 中实现。该 Fragment 在它的 onAttach() 方法生命周期中获取该接口的实现,然后调用接口的方法,以便与 Activity 进行交互。(若该 Fragment 中实现了 onAttach() 方法,则会被自动调用。)

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);
    }

2.实现接口

为了接收回调事件,宿主 Activity 必须实现在 Fragment 中定义的接口。

例如,下面的 Activity 实现了上面例子中的接口。

public static class MainActivity extends Activity
        implements HeadlinesFragment.OnHeadlineSelectedListener{
    ...

    public void onArticleSelected(int position) {
        // 用户从 HeadlinesFragment 选择了一篇文章的标题
        // 在这里做点什么,以显示该文章
    }
}

3.向 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();
        }
    }
}

不要给自己的人生设限
最后编辑于
©著作权归作者所有,转载或内容合作请联系作者
  • 序言:七十年代末,一起剥皮案震惊了整个滨河市,随后出现的几起案子,更是在滨河造成了极大的恐慌,老刑警刘岩,带你破解...
    沈念sama阅读 194,491评论 5 459
  • 序言:滨河连续发生了三起死亡事件,死亡现场离奇诡异,居然都是意外死亡,警方通过查阅死者的电脑和手机,发现死者居然都...
    沈念sama阅读 81,856评论 2 371
  • 文/潘晓璐 我一进店门,熙熙楼的掌柜王于贵愁眉苦脸地迎上来,“玉大人,你说我怎么就摊上这事。” “怎么了?”我有些...
    开封第一讲书人阅读 141,745评论 0 319
  • 文/不坏的土叔 我叫张陵,是天一观的道长。 经常有香客问我,道长,这世上最难降的妖魔是什么? 我笑而不...
    开封第一讲书人阅读 52,196评论 1 263
  • 正文 为了忘掉前任,我火速办了婚礼,结果婚礼上,老公的妹妹穿的比我还像新娘。我一直安慰自己,他们只是感情好,可当我...
    茶点故事阅读 61,073评论 4 355
  • 文/花漫 我一把揭开白布。 她就那样静静地躺着,像睡着了一般。 火红的嫁衣衬着肌肤如雪。 梳的纹丝不乱的头发上,一...
    开封第一讲书人阅读 46,112评论 1 272
  • 那天,我揣着相机与录音,去河边找鬼。 笑死,一个胖子当着我的面吹牛,可吹牛的内容都是我干的。 我是一名探鬼主播,决...
    沈念sama阅读 36,531评论 3 381
  • 文/苍兰香墨 我猛地睁开眼,长吁一口气:“原来是场噩梦啊……” “哼!你这毒妇竟也来了?” 一声冷哼从身侧响起,我...
    开封第一讲书人阅读 35,215评论 0 253
  • 序言:老挝万荣一对情侣失踪,失踪者是张志新(化名)和其女友刘颖,没想到半个月后,有当地人在树林里发现了一具尸体,经...
    沈念sama阅读 39,485评论 1 290
  • 正文 独居荒郊野岭守林人离奇死亡,尸身上长有42处带血的脓包…… 初始之章·张勋 以下内容为张勋视角 年9月15日...
    茶点故事阅读 34,578评论 2 309
  • 正文 我和宋清朗相恋三年,在试婚纱的时候发现自己被绿了。 大学时的朋友给我发了我未婚夫和他白月光在一起吃饭的照片。...
    茶点故事阅读 36,356评论 1 326
  • 序言:一个原本活蹦乱跳的男人离奇死亡,死状恐怖,灵堂内的尸体忽然破棺而出,到底是诈尸还是另有隐情,我是刑警宁泽,带...
    沈念sama阅读 32,215评论 3 312
  • 正文 年R本政府宣布,位于F岛的核电站,受9级特大地震影响,放射性物质发生泄漏。R本人自食恶果不足惜,却给世界环境...
    茶点故事阅读 37,583评论 3 299
  • 文/蒙蒙 一、第九天 我趴在偏房一处隐蔽的房顶上张望。 院中可真热闹,春花似锦、人声如沸。这庄子的主人今日做“春日...
    开封第一讲书人阅读 28,898评论 0 17
  • 文/苍兰香墨 我抬头看了看天上的太阳。三九已至,却和暖如春,着一层夹袄步出监牢的瞬间,已是汗流浃背。 一阵脚步声响...
    开封第一讲书人阅读 30,174评论 1 250
  • 我被黑心中介骗来泰国打工, 没想到刚下飞机就差点儿被人妖公主榨干…… 1. 我叫王不留,地道东北人。 一个月前我还...
    沈念sama阅读 41,497评论 2 341
  • 正文 我出身青楼,却偏偏与公主长得像,于是被迫代替她去往敌国和亲。 传闻我的和亲对象是个残疾皇子,可洞房花烛夜当晚...
    茶点故事阅读 40,697评论 2 335

推荐阅读更多精彩内容