从0开始开始认识android(十四):Fragment(片段)

fragment生命周期
这里写图片描述
  fragment的生命周期受其宿主activity的影响,即Activity 暂停时,其中的所有片段也会暂停;当 Activity 被销毁时,所有片段也会被销毁,需要注意的是,一个fragment加入了activity返回栈和没有加入返回栈被removed或replaced时执行的生命周期方法是不一样的:
  加入返回栈的:被removed或replaced时,最终只会走到onDestoryView()方法仅仅销毁其界面,fragment实例依然存在,当下次再被添加到宿主时就直接走onCreateView()方法了,而不是从onAttach()方法开始。
  未加入返回栈的:被removed或replaced时,最终会走到onDestory()方法销毁fragment实例,下次重新被添加到宿主时会从onAttach()方法重头开始。那怎么把一个fragment加入到activity返回栈呢?接着往下看。
  当创建一个fragment时我们至少要实现三个方法:
  onCreate():系统创建fragment时的方法,传入的Bundle对象同Activity中onCreate方法的Bundle作用一样。不清楚onCreate()方法中的Bundle对象怎么来的和有什么用的可以参考《从0开始认识android(十一):Activity生命周期》这篇文章。
  **onCreateView():**fragment首次加载其布局文件的地方,如果你不想这个fragment有界面,只是执行一些用户不必看见的功能时,可以返回null。该方法中的Bundle对象同onCreate()方法。
  onPause():同activity,没啥可说的。
创建Fragment
  以下有关引用内容均来自安卓文档。

  通常,片段向宿主 Activity 贡献一部分 UI,作为 Activity 总体视图层次结构的一部分嵌入到 Activity 中。可以通过两种方式向 Activity 布局添加片段:
  在 Activity 的布局文件内声明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>

< fragment> 中的 android:name 属性指定要在布局中实例化的 Fragment 类。
当系统创建此 Activity 布局时,会实例化在布局中指定的每个片段,并为每个片段调用 onCreateView() 方法,以检索每个片段的布局。系统会直接插入片段返回的 View 来替代 < fragment> 元素。
  注意:每个片段都需要一个唯一的标识符,重启 Activity 时,系统可以使用该标识符来恢复片段(您也可以使用该标识符来捕获片段以执行某些事务,如将其移除)。 可以通过三种方式为片段提供 ID:
  为 android:id 属性提供唯一 ID。
  为 android:tag 属性提供唯一字符串。
  如果您未给以上两个属性提供值,系统会使用容器视图的 ID。
  或者通过编程方式将fragment添加到某个现有 ViewGroup:
  您可以在 Activity 运行期间随时将片段添加到 Activity 布局中。您只需指定要将片段放入哪个 ViewGroup。
  要想在您的 Activity 中执行片段事务(如添加、移除或替换片段),您必须使用 FragmentTransaction 中的 API。您可以像下面这样从 Activity 获取一个 FragmentTransaction 实例:

FragmentManager fragmentManager = getFragmentManager();
FragmentTransaction fragmentTransaction = fragmentManager.beginTransaction();

   然后,您可以使用 add() 方法添加一个片段,指定要添加的片段以及将其插入哪个视图。例如:

ExampleFragment fragment = new ExampleFragment();
fragmentTransaction.add(R.id.fragment_container, fragment);
fragmentTransaction.commit(); 

   传递到 add() 的第一个参数是 ViewGroup,即应该放置片段的位置,由资源 ID 指定,第二个参数是要添加的片段。
  一旦您通过 FragmentTransaction 做出了更改,就必须调用 commit() 以使更改生效。
  

示例代码1(点击两个按钮实现Fragment1和Fragment2在容器container里的转换): 

switch (view.getId()) {
            case R.id.button:
                if (mFragment1 != null) {
                    FragmentTransaction transaction = getFragmentManager()
                            .beginTransaction();
                    if (mFragment1.isAdded()) {
                        transaction.replace(R.id.container, mFragment1);
                    } else {
                        transaction.add(R.id.container, mFragment1);
                    }
                    transaction.commit();
                } else {
                    mFragment1 = new Fragment1();
                    FragmentTransaction transaction = getFragmentManager()
                            .beginTransaction();
                    transaction.add(R.id.container, mFragment1);
                    transaction.commit();
                }
                break;
            case R.id.button2:
                if (mFragment2 != null) {
                    FragmentTransaction transaction = getFragmentManager()
                            .beginTransaction();
                    if (mFragment2.isAdded()) {
                        transaction.replace(R.id.container, mFragment2);
                    } else {
                        transaction.add(R.id.container, mFragment2);
                    }
                    transaction.commit();
                } else {
                    mFragment2 = new Fragment2();
                    FragmentTransaction transaction = getFragmentManager()
                            .beginTransaction();
                    transaction.add(R.id.container, mFragment2);
                    transaction.commit();
                }
                break;
                }
          }

  添加没有 UI 的片段: 

  上例展示了如何向您的 Activity 添加片段以提供 UI。不过,您还可以使用片段为 Activity 提供后台行为,而不显示额外 UI。
  要想添加没有 UI 的片段,请使用 add(Fragment, String) 从 Activity 添加片段(为片段提供一个唯一的字符串“标记”,而不是视图 ID)。 这会添加片段,但由于它并不与 Activity 布局中的视图关联,因此不会收到对 onCreateView() 的调用。因此,您不需要实现该方法。
  并非只能为非 UI 片段提供字符串标记 — 您也可以为具有 UI 的片段提供字符串标记 — 但如果片段没有 UI,则字符串标记将是标识它的唯一方式。如果您想稍后从 Activity 中获取片段,则需要使用 findFragmentByTag()。

  示例代码2:

if (mFragment3 != null) {
      FragmentTransaction transaction = getFragmentManager()
                            .beginTransaction();
                    if (mFragment3.isAdded()) {
                        transaction.remove(mFragment3);
                    } else {
                        transaction.add(mFragment3, "no_ui_f");
                    }
                    transaction.commit();
     } else {
                    mFragment3 = new Fragment3();
                    FragmentTransaction transaction = getFragmentManager()
                            .beginTransaction();
                    transaction.add(mFragment3, "no_ui_f");
                    transaction.commit();
                }

执行片段事务

  在 Activity 中使用片段的一大优点是,可以根据用户行为通过它们执行添加、移除、替换以及其他操作。 您提交给 Activity 的每组更改都称为事务,您可以使用 FragmentTransaction 中的 API 来执行一项事务。您也可以将每个事务保存到由 Activity 管理的返回栈内,从而让用户能够回退片段更改(类似于回退 Activity)。
  您可以像下面这样从 FragmentManager 获取一个 FragmentTransaction 实例:

FragmentManager fragmentManager = getFragmentManager();
FragmentTransaction fragmentTransaction = fragmentManager.beginTransaction(); 

  每个事务都是您想要同时执行的一组更改。您可以使用 add()、remove() 和 replace() 等方法为给定事务设置您想要执行的所有更改。然后,要想将事务应用到 Activity,您必须调用 commit()。
   不过,在您调用 commit() 之前,您可能想调用 addToBackStack(),以将事务添加到片段事务返回栈。 该返回栈由 Activity 管理,允许用户通过按返回按钮返回上一片段状态。
  例如,以下示例说明了如何将一个片段替换成另一个片段,以及如何在返回栈中保留先前状态:

// 获取事务对象
Fragment newFragment = new ExampleFragment(); 
FragmentTransaction transaction = getFragmentManager().beginTransaction();

// 替换旧片段
transaction.replace(R.id.fragment_container, newFragment);
//保留状态
transaction.addToBackStack(null); 

// 执行
transaction.commit(); 

  在上例中,newFragment 会替换目前在 R.id.fragment_container ID 所标识的布局容器中的任何片段(如有)。通过调用 addToBackStack() 可将替换事务保存到返回栈,以便用户能够通过按返回按钮撤消事务并回退到上一片段
  如果您向事务添加了多个更改(如又一个 add() 或 remove()),并且调用了 addToBackStack(),则在调用 commit() 前应用的所有更改都将作为单一事务添加到返回栈,并且返回按钮会将它们一并撤消。
  向 FragmentTransaction 添加更改的顺序无关紧要,不过:
  您必须最后调用 commit()
  如果您要向同一容器添加多个片段,则您添加片段的顺序将决定它们在视图层次结构中的出现顺序
  如果您没有在执行移除片段的事务时调用 addToBackStack(),则事务提交时该片段会被销毁,用户将无法回退到该片段。 不过,如果您在删除片段时调用了 addToBackStack(),则系统会停止该片段,并在用户回退时将其恢复。
  提示:对于每个片段事务,您都可以通过在提交前调用 setTransition() 来应用过渡动画。
  调用 commit() 不会立即执行事务,而是在 Activity 的 UI 线程(“主”线程)可以执行该操作时再安排其在线程上运行。不过,如有必要,您也可以从 UI 线程调用 executePendingTransactions() 以立即执行 commit() 提交的事务。通常不必这样做,除非其他线程中的作业依赖该事务
   注意:您只能在 Activity 保存其状态(用户离开 Activity)之前使用 commit() 提交事务。如果您试图在该时间点后提交,则会引发异常。 这是因为如需恢复 Activity,则提交后的状态可能会丢失。 对于丢失提交无关紧要的情况,请使用 commitAllowingStateLoss()。

  需要注意的是:如示例代码1中的情形,如果你往同一个容器里先添加了fragment1,再添加fragment2时,即显示的是fragment2,且fragment1和fragment2均未添加到返回栈。如果此时替换到fragment1,fragment2会依次走onPause——onStop——onDestoryView——onDestory——onDetach生命周期方法,但重新显示的fragment1却不会重走onAttach——onCreate——onCreateView——onActivityCreated——onStart——onResume生命周期。之后如此循环切换都是这样。
  但如果你往同一个容器里添加了3个以上的片段时,你不停的切换显示时,唯一能保证的是最后添加的那个片段会依次走完生命周期方法,你永远不能保证前面先添加的片段重新被切换显示时会什么时候重走生命周期方法。
  另外,每执行一次addToBackStack事务,都会有一个对应的片段被添加到返回栈中,你执行了n次,就有n个对应片段,所以这时你点击手机返回键的话会发现返回的是上个片段;如果没有片段被添加到返回栈,这时你点击返回键发现会直接退出了当前activity。

  管理片段

  要想管理您的 Activity 中的片段,您需要使用 FragmentManager。要想获取它,请从您的 Activity 调用 getFragmentManager()。
  您可以使用 FragmentManager 执行的操作包括:
  通过 findFragmentById()(对于在 Activity 布局中提供 UI 的片段)或    findFragmentByTag()(对于提供或不提供 UI 的片段)获取 Activity 中存在的片段。
  通过 popBackStack()(模拟用户发出的返回命令)将片段从返回栈中弹出。
  通过 addOnBackStackChangedListener() 注册一个侦听返回栈变化的侦听器。

几个问题:
  1、v7包中的Fragment和Android.app包中的Fragment如何选择?
    如果你的APP需要兼容4.0(API14)以下的设备,那建议用v7包中的。这时,你的宿主activity要继承的是AppCompatActivity,并且通过getSupportFragmentManager()获取片段管理器。
    不需要兼容低版本的话,用Android.app包中的就行了。宿主activity只要继承Activity,通过getFragmentManager()获取片段管理器。
  2、如何将上个activity带过来的数据传给当前activity的某个fragment?
    以下为示例代码:

//起始activity
Intent intent = new Intent(this, FragmentTestActivity.class);
intent.putExtra("test","测试数据");
startActivity(intent);

//设置起始activity的数据给目标fragmeng
mFragment1 = new Fragment1();
//通过片段的setArguments(Bundle bundle)将上个activity带过来的数据设置给自
//己。注意:这个bundle对象必须是上个activity带过来的!!你在这里new一个Bundle
//传过去是收不到的哦!
mFragment1.setArguments(getIntent().getExtras());
getFragmentManager()
 .beginTransaction().add(R.id.container, mFragment1).commit();

//目标fragment接收即可
String data = getActivity().getIntent().getStringExtra("test");

最后,附上一张activity生命周期对其片段生命周期的影响图片:
这里写图片描述

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值