Fragment-管理Fragment1

前面给大家稍微看了要怎么使用fragment,在上篇中,我们也初步接触到了add,replace这些fragment操作的函数,下面就再详细讲讲如何管理Fragment页面吧。

一、概述

1、FragmentManager

要管理activity中的fragments,你就需要使用FragmentManager。通过getFragmentManager()或getSupportFragmentManager()获得 
常用的方法有:

manager.findFragmentById();  //根据ID来找到对应的Fragment实例,主要用在静态添加fragment的布局中,因为静态添加的fragment才会有ID
manager.findFragmentByTag();//根据TAG找到对应的Fragment实例,主要用于在动态添加的fragment中,根据TAG来找到fragment实例
manager.getFragments();//获取所有被ADD进Activity中的Fragment  

2、FragmentTransaction

一般用来对当前的Fragment进行管理,包括add,replace,remove;
常用的针对Fragment的方法有:

//将一个fragment实例添加到Activity的最上层
add(int containerViewId, Fragment fragment, String tag);
//将一个fragment实例从Activity的fragment队列中删除
remove(Fragment fragment);
//替换containerViewId中的fragment实例,注意,它首先把containerViewId中所有fragment删除,然后再add进去当前的fragment
replace(int containerViewId, Fragment fragment);

还有hide()、show()、detach()、attach()这些函数,我们下篇再讲,这节先对Fragment的用法有一个初步了解;

二、add()、replace()、remove()使用方法示例

下面就通过例子来看看以上几个函数的使用方法吧。 
效果图如下:

  • 点击“ADD Fragment1”,在将Fragment1添加到Activity的container中;
  • 点击“ADD Fragment2”,将Fragment2添加到Activity的container中;
  • 点击“Remove Fragment2”,将Fragment2的实例从container中移除,移除之后,就显示出其下方的fragment1的视图出来了。
  • 再点击”replace Fragment1”,将container中的视图移除,然后添加上fragment2的视图。

那现在我们从头开始构建这个工程:

1、新建两个fragment1.xml 和 fragment2.xml:

从效果图中也可以看出,这两个XML什么都没有,只是通过背景色和文字来区别当前是哪个Fragment的XML布局文件而已,他们的布局代码如下:

fragment1.xml:

<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
    android:layout_width="match_parent"
    android:layout_height="match_parent"
    android:background="#ff00f0"
    android:orientation="vertical" >  

    <TextView
        android:layout_width="wrap_content"
        android:layout_height="wrap_content"
        android:text="This is fragment 1"
        android:textColor="#000000"
        android:textSize="25sp" />  

</LinearLayout>

fragment2.xml:

<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
    android:layout_width="match_parent"
    android:layout_height="match_parent"
    android:background="#ffff00"
    android:orientation="vertical" >  

    <TextView
        android:id="@+id/fragment2_tv"
        android:layout_width="wrap_content"
        android:layout_height="wrap_content"
        android:text="This is fragment 2"
        android:textColor="#000000"
        android:textSize="25sp" />  

</LinearLayout>  

2、建立对应的Fragment类:Fragment1和Fragment2

Fragment1:

import android.os.Bundle;
import android.support.v4.app.Fragment;
import android.view.LayoutInflater;
import android.view.View;
import android.view.ViewGroup;  

public class Fragment1 extends Fragment {
    @Override
    public View onCreateView(LayoutInflater inflater, ViewGroup container,
            Bundle savedInstanceState) {
        return inflater.inflate(R.layout.fragment1, container, false);
    }  

}

与上一篇一样,也只是在onCreateView()时返回对应的布局。同样,Fragment2的定义如下:

import android.os.Bundle;
import android.support.v4.app.Fragment;
import android.view.LayoutInflater;
import android.view.View;
import android.view.ViewGroup;  

public class Fragment2 extends Fragment {
    @Override
    public View onCreateView(LayoutInflater inflater, ViewGroup container,
                             Bundle savedInstanceState) {
        return inflater.inflate(R.layout.fragment2, container, false);
    }
}

3、MainActivity的布局

从上面的的效果图中也可以看出大概的布局,首先是三个Button,最下方是一个FrameLayout布局,它是用来做为container动态盛装fragment的;它就像是一个占位符,你设置多大,它其中的fragment就最大能有多大。记住,fragment也是Activity中的一个普通控件而已,只不过它可以像Activity一样用于显示的同时还能用来盛装其它控件!作为fragment的容器,即可以用FrameLayout也可以用LinearLayout或者RelativeLayout,都是一样的。activity_main.xml的布局代码如下:

<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
    android:layout_width="match_parent"
    android:layout_height="match_parent"
    android:orientation="vertical">  

    <Button
        android:id="@+id/btn_add_frag1"
        android:layout_width="match_parent"
        android:layout_height="wrap_content"
        android:text="ADD  Fragment1" />  

    <Button
        android:id="@+id/btn_add_frag2"
        android:layout_width="match_parent"
        android:layout_height="wrap_content"
        android:text="ADD  Fragment2" />  

    <Button
        android:id="@+id/btn_remove_frag2"
        android:layout_width="match_parent"
        android:layout_height="wrap_content"
        android:text="Remove  Fragment2" />  

    <Button
        android:id="@+id/btn_repalce_frag1"
        android:layout_width="match_parent"
        android:layout_height="wrap_content"
        android:text="replace  Fragment1" />  

    <FrameLayout
        android:id="@+id/fragment_container"
        android:layout_width="match_parent"
        android:layout_height="match_parent"/>  

</LinearLayout>

4、MainActivity的实现:

(1)首先,先写一个添加fragment到Activity中的函数:

private void addFragment(Fragment fragment, String tag) {
    FragmentManager manager = getSupportFragmentManager();
    FragmentTransaction transaction = manager.beginTransaction();
    transaction.add(R.id.fragment_container, fragment, tag);
    transaction.commit();
}  
  • 先是传进来两个参数,一个是要添加的fragment实例,另一个是一个TAG
  • 至于FragmentManager、FragmentTransaction就没什么好讲的,这是这么个流程,要获取它们的实例就得调用这些函数。
  • 然后是add(R.id.fragment_container, fragment, tag)函数:第一个参数是要将fragment盛装的container,即我们上面的FrameLayout!第三个参数是tag,当传进去这个TAG,它就会跟这个fragment关联起来,当我们通过findFragmentByTag()时,根据这个TAG就能找到这个Fragment实例。进而对它进行操作,比如,我们下面的remove();
  • 在通过transaction对Fragment操作完以后,一定要记得调用transaction.commit(),这样才会将操作提交到系统中,这里的代码才会最终起作用。

有没有觉得这个流程挺像数据库的回滚操作!对,其实就是这样的,这里其实就是一个针对Fragment的回滚操作,首先通过beginTransaction()来定义回滚的开始,然后通过transaction对Fragment进行一系列操作(我们这里只是进行了ADD,其实可以做一串操作),等操作完了利用commit()提交。这里就先初步讲到这,下节会细讲有关transaction的回滚过程。
(2)添加Fragment1和Fragment2:
好了,上面我们写好了一个函数addFragment(Fragment fragment, String tag),下面我们就要通过这个函数来添加Fragment1和Fragment2的实例了。
当点击“ADD Fragment1”按钮时:

Fragment1 fragment1 = new Fragment1();
addFragment(fragment1, "fragment1");

当点击“ADD Fragment2”按钮时:

Fragment2 fragment2 = new Fragment2();
addFragment(fragment2, "fragment2");

这里需要注意的是,当我们添加每个Fragment实例时,都传进去一个对应的TAG,fragment1对应的是“fragment1”,fragment2对应的是“fragment2”,通过这些TAG,我们就可以通过findFragmentByTag()来获取它们了。
(3)RemoveFragment2:
下面就是当点击“RemoveFragment2”按钮时的代码操作了:

private void removeFragment2() {
    FragmentManager manager = getSupportFragmentManager();
    Fragment fragment = manager.findFragmentByTag("fragment2");
    FragmentTransaction transaction = manager.beginTransaction();
    transaction.remove(fragment);
    transaction.commit();
}

首先是通过

Fragment fragment = manager.findFragmentByTag("fragment2");

找到我们当时ADD进Activity的fragment2实例,然后通过transaction.remove(fragment);将它删除;从效果图中可以看到,由于我们移除了fragment2的实例,而又由于fragment2是在界面最上方的,所以把它删除了之后,自然就剩下了fragment1在最上方了,所以我们就看到了fragment1的界面。那如果我们移除的是fragment1呢?答案是界面没有任何变化!因为fragment1的实例是在fragment2的下方的,我们是根本看不到它的。
(4)ReplaceFragment1:

最后一个操作:ReplaceFragment1:

咱们先回到上面的RemoveFragment2操作,在RemoveFragment2之后,要知道我们的Activity的ADD队列中,就只有fragment1了。知道这一点之后,咱们看下面ReplaceFragment1的代码:

private void replaceFragment1() {
    FragmentManager manager = getSupportFragmentManager();
    Fragment2 fragment2 = new Fragment2();
    FragmentTransaction transaction = manager.beginTransaction();
    transaction.replace(R.id.fragment_container, fragment2);
    transaction.commit();
}

这里有个注意的问题:

transaction.replace(R.id.fragment_container, fragment2);

这里replace传进去的第一个参数是容器ID,第二个参数是要新增的fragment。既然要replace(),那它是针对哪个fragment进行replace()呢?怎么没有指定要替换的fragment!为什么这里的第一个参数是盛装Activity中所有Fragment的container的ID呢?没错,这里的replace操作会把这个cotainer中所有fragment清空!!!!然后再把fragment2添加进去!
从上面的的讲解,大家可能也已经觉查到FragmentTransaction的Add()操作是维持着一个队列的,在这个队列中,根据ADD进去的先后顺序形成了一个链表,我们上面的操作在这个列表中的形式变化如下图所示:

到这里add,replace,remove的使用方法就讲完了,但这里有个问题,必须根大家讲一下:我们上面说过replace操作会把container中的所有fragment全部删除,然后再将指定的fragment添加进去!但Android在实现时出现了BUG!在replace()时,并不能把以前所有Fragment清空,就因为这个系统工程产了BUG就会导致add()和Replace()不能共用!关于add()和Replace()不能共用的问题,我们会在下篇再讲。下面先给大家说说有关回滚的问题。

三、有关回滚——FragmentTransaction

1、FragmentTransaction事务回滚使用方法:

上部分,我们讲了有关添加、删除Fragment的操作,想将上一次commit的操作返回时,要怎么做呢。这就需要FragmentTransaction的回滚功能了。 
要使用回滚功能,只需要要使用下面两个代码: 
在transaction.commit()之前,使用addToBackStack()将其添加到回退栈中。

transaction.addToBackStack(String tag);

在需要回退时,使用popBackStack()将最上层的操作弹出回退栈。

manager.popBackStack();

这里的popBackStack()是弹出默认的最上层的栈顶内容。
当栈中有多层时,我们可以根据id或TAG标识来指定弹出到的操作所在层。函数如下:

void popBackStack(int id, int flags);
void popBackStack(String name, int flags);

其中

  • 参数int id是当提交变更时transaction.commit()的返回值。
  • 参数string name是transaction.addToBackStack(String tag)中的tag值;
  • 至于int flags有两个取值:0或FragmentManager.POP_BACK_STACK_INCLUSIVE;
  • 当取值0时,表示除了参数一指定这一层之上的所有层都退出栈,指定的这一层为栈顶层;
  • 当取值POP_BACK_STACK_INCLUSIVE时,表示连着参数一指定的这一层一起退出栈;
  • 除了这几个函数,还有下面几个函数:有关他们的使用,我们在这小部分结尾时会提到
popBackStackImmediate()
popBackStackImmediate(String tag)
popBackStackImmediate(String tag, int flag)
popBackStackImmediate(int id, int flag)

下面我们就通过一个例子来讲解一下有关回退栈中的操作过程:

先看下效果图:

整体流程是这样的:
1、逐个将Fragment1,2,3,4添加到窗口中,在添加时,每添加一个Fragment要利用transaction的addToBackStack将此次操作加入到回退栈中。
2、然后点击"PopBackStack"方法,将栈顶最上层的操作回退。退将最后一次添加回退出来,显示Fragment3.
3、点击“ADD Fragment4”将栈还原到1,2,3,4依次ADD进栈的状态,即操作1完成后的栈状态,然后点击“BackToStack2_0”,其实调用的方法是:

manager.popBackStack("fragment2",0);//方法一,通过TAG回退

从这里可以看出,要回退到添加ADD Fragment2的状态,注意最后一个参数,这里设为0,表明,要回退ADD Fragment2的之后的操作,将ADD Fragment2的操作置为栈顶。从效果图中也可以看出,点击后的视图在Fragment2的位置
4、最后仍然是先点击"Add Fragment3"和"ADD Fragment4",将栈还原到操作1完成后的栈状态。然后点击“BackToStack2_INCLUSIVE”;其调用的方法是:

manager.popBackStack("fragment2",FragmentManager.POP_BACK_STACK_INCLUSIVE);//方法一,通过TAG回退

这里与上面的主要不同点在于第二个参数,这里设置为POP_BACK_STACK_INCLUSIVE,即在出栈时连带ADD Fragment2的操作一块出栈,放在栈顶的是ADD Fragment1的操作,所以放在界面上就是显示的是Fragment1的视图。
下面我们看看具体实现:
(1)、首先,与上部分一样,先添加四个Fragment,并用背景色和文字来区分。这部分代码我们就不讲了。
主要看看点击按钮的代码处理方法。
(2)、首先是添加Fragment1:

Fragment1 fragment1 = new Fragment1();
stackID1 = addFragment(fragment1,"fragment1");

其中:

private int stackID1,stackID2,stackID3,stackID4;
private int addFragment(Fragment fragment,String stackName){
    FragmentManager manager = getSupportFragmentManager();
    FragmentTransaction transaction = manager.beginTransaction();
    transaction.add(R.id.fragment_container,fragment);
    transaction.addToBackStack(stackName);
    return transaction.commit();
}

首先,这里的stackID1,stackID2,stackID3,stackID4是用来保存每次commit()后返回的Transaction的ID值。在void popBackStack(int id, int flags);时,其中的参数id就是这个值
然后在每次ADD操作后,利用addToBackStack(string name)将每次ADD操作添加进回退栈中;
同样,添加Fragment2的代码如下,添加Fragment3,Fragment4的方法同理

Fragment2 fragment2 = new Fragment2();
stackID2 = addFragment(fragment2,"fragment2");

(3)、然后是回退栈顶内容:

private void popBackStack(){
    FragmentManager manager = getSupportFragmentManager();
    manager.popBackStack();
}

(4)、接着是点击BackToFrag2_0按钮的内容,这里有两种方法实现,一种是指定TAG,一种是利用Commit()返回的ID

private void popBackStackToFrag2_0(){
    FragmentManager manager = getSupportFragmentManager();
    manager.popBackStack("fragment2",0);//方法一,通过TAG回退
    // manager.popBackStack(stackID2,0);//方法二,通过Transaction ID回退
}

(5)、最后是点击BackToFrag2_INCLUSIVE按钮的代码:

private void popBackStackToFrag2_Inclusive(){
    FragmentManager manager = getSupportFragmentManager();
    manager.popBackStack("fragment2",FragmentManager.POP_BACK_STACK_INCLUSIVE);//方法一,通过TAG回退
 // manager.popBackStack(stackID2,FragmentManager.POP_BACK_STACK_INCLUSIVE);//方法二,通过Transaction ID回退
}

好了,到这里,有关回滚的基本使用就结束了,需要要注意的是:
使用popBackStack()来弹出栈内容的话,调用该方法后会将事物操作插入到FragmentManager的操作队列,只有当轮询到该事物时才能执行。如果想立即执行事物的话,需要使用下面几个对应的方法:

popBackStackImmediate()
popBackStackImmediate(String tag)
popBackStackImmediate(String tag, int flag)
popBackStackImmediate(int id, int flag) 

2、回退栈(back stack)状态改变监听

FragmentManager还为我们提供了监控回退栈状态改变的方法:

FragmentManager::addOnBackStackChangedListener(listener);//添加监听器
FragmentManager::removeOnBackStackChangedListener(listener);//移除监听器

通过添加监听器,就可以在回退栈内容改变时,及时收到通知;
我们在上面代码的基础上,在MainAcitivy中为FragmentManager添加一个监听器,当回退栈状态改变时,打出一个LOG。具体实现如下:
(1)、OnCreate()中:
为fragmentManger添加一个监听器:

FragmentManager manager = getSupportFragmentManager();
listener = new FragmentManager.OnBackStackChangedListener() {  

    @Override
    public void onBackStackChanged() {
        // TODO Auto-generated method stub
        Log.d("qijian","backstack changed");
    }
};
manager.addOnBackStackChangedListener(listener);

(2)、当onDestory()中将监听器remove掉:

protected void onDestroy() {
    // TODO Auto-generated method stub
    super.onDestroy();
    FragmentManager manager = getSupportFragmentManager();
    manager.removeOnBackStackChangedListener(listener);
}

大家一定要注意,不管是这里的回退栈的监听还是其它的监听器,在页面对应的销毁时,都要记得remove掉,不然会造成页面不释放,这也是造成OOM的问题之一。

这样当回退栈内容出现变动时,变会打LOG出来,如图:

3、Transaction事务回退的原则

这里我们着重讲一下,回退是以commit()提交的一次事务为单位的,而不是以其中的add,replace等等操作为单位回退的,即,如果我们在一次提交是添加了fragment2,fragment3,fragment4,那么回退时,会依据添加时的顺序,将它们一个个删除,返回到没有添加fragment4,fragment3,fragment2的状态。
下面我们仍然写一个例子来讲明一下事务的回退原则,效果图如下:

  • 1、首先,添加Fragment1,提交一次事务
  • 2、然后,一次添加Fragment2,Fragment3,Fragment4,然后提交一次事务
  • 3、利用popBackStack()将顶层事务出栈,可以看到把Fragment2,Fragment3,Fragment4一次出栈,界面显示在了Fragment1的位置,这就充分说明了,回滚是以提交的事务为单位进行的!

下面是代码实现部分:
1、同样,新建四个Fragment,分别利用背景色和文字来表明之间的不同。
2、然后添加Fragment1的代码如下:

private void addFragment1() {
    Fragment1 fragment1 = new Fragment1();
    FragmentManager manager = getSupportFragmentManager();
    FragmentTransaction transaction = manager.beginTransaction();
    transaction.add(R.id.fragment_container, fragment1);
    transaction.addToBackStack("fragment1");
    transaction.commit();
}

3、然后是添加其它三个Fragment的代码如下:

private void addOtherFragments() {
    Fragment2 fragment2 = new Fragment2();
    Fragment3 fragment3 = new Fragment3();
    Fragment4 fragment4 = new Fragment4();
    FragmentManager manager = getSupportFragmentManager();
    FragmentTransaction transaction = manager.beginTransaction();
    transaction.add(R.id.fragment_container, fragment2);
    transaction.add(R.id.fragment_container, fragment3);
    transaction.add(R.id.fragment_container, fragment4);
    transaction.addToBackStack("other fragments");
    transaction.commit();
}

再一次重申,从代码中也可以看到,是依次将Fragment2、Fragment3、Fragment4加入容器之后,再提交一次事务,所以下面回滚时,会将他们反顺序的依次删除。即remove(fragment4)、remove(fragment3)、remove(fragment2)

4、点击popBackStack按钮时的代码

private void popBackStack() {
    FragmentManager manager = getSupportFragmentManager();
    manager.popBackStack();
}
时间: 2024-10-07 19:41:04

Fragment-管理Fragment1的相关文章

Fragment管理工具类

Fragment相关→FragmentUtils.java→Demo addFragment : 新增fragment removeFragment : 移除fragment replaceFragment : 替换fragment popFragment : 出栈fragment popToFragment : 出栈到指定fragment popAddFragment : 先出栈后新增fragment hideFragment : 隐藏fragment showFragment : 显示fra

Fragment管理最佳实践

现在的app视图中tab+fragment是最常用的一种布局,但是如果使用才是更简洁更有效的呢?下面通过一个demo的分类测试来分析下: add remove replace detach attach hide show这些方法的使用对Fragment生命周期的影响分析: 顺便分析下Fragment所依赖的Activity的生命周期: 测试代码如下: MainActivity的代码如下: package com.testfragmentlifecircle; import android.co

Fragment管理

Fragments 设计理念 在设计应用时特别是Android 应用 ,有众多的分辨率要去适应,而fragments 可以让你在屏幕不同的屏幕上动态管理UI.例如:通讯应用程序(QQ),用户列表可以在左边,消息窗口在右边的设计.而在手机屏幕用户列表填充屏幕当点击某一用户时,则弹出对话窗口的设计,如下图: Fragments的生命周期 每一个fragments 都有自己的一套生命周期回调方法和处理自己的用户输入事件. 对应生命周期可参考下图: 其中大多数程序必须使用Fragments 必须实现的三

[Android]Fragment源码分析(肆) Fragment栈管理

Fragment的栈是Fragment管理颇为出彩的一部分,它跟Activity栈的本质差异除了在数据结构上和逻辑上的不同之外,主要区别还在于: 1.Fragment管理是在进程空间内的 2.Fragment的管理一般情况下是一个Window下进行的. Fragment的管理在一个进程空间内是比较好理解的,因为我们知道Activity的管理其实相对复杂,它的管理是通过IPC调用,IPC的一端是我们的Client,而作为Server的是Ams服务.Activity的管理是基于Window的,而Fr

android 77 fragment

fragment是3.0之后才有的,之前平板是3.0专用,后来手机和平板都用3.0 Activity: package com.itheima.fragment; import android.os.Bundle; import android.app.Activity; import android.app.FragmentManager; import android.app.FragmentTransaction; import android.view.Menu; import andr

Android UI编程(7)——Fragment

Fragment是Activity的界面中的一部分或一种行为.你可以把多个Fragment们组合到一个Activity中来创建一个多面界面并且可以在多个Activity中重用一个Fragment.也可以把Fragment认为模块化的一段Activity,它具有自己的生命周期,接收它自己的事件,并可以在Activity运行时被添加或删除. Fragment不能独立存在,它必须嵌入到activity中,而且Fragment的生命周期直接受所在的Activity的影响.例如:当Activity暂停时,

Android基础笔记(十八)- Fragment

博客的感悟终点-开始 什么是Fragment 添加fragment到Activity的两种方式 Fragment的生命周期 Fragment的向下兼容 Fragment之间的通信 博客的感悟,终点-开始 这个是基础的最后一篇博客了,学习了很多,也有很多感触. 就在这里大致总结一下. 坚持往往很难,完美的坚持下去更难.这是写这十八篇博客的感悟. 时间流失的很快,总是感觉时间不够用.慢慢的就会让自己博客的质量下降.今天反思了一下,我这样不就是在制造"破窗户"吗?(破窗户理论不知道的可以去看

Android进阶——Fragment详解之操作原理(三)

引言 前一篇文章总结了Fragment 的基本概念和基本的用法,相信大家也能够掌握一些知识了,但是对于一些操作可能还是不知其所以然,说实话曾经很长一段时间为也是晕乎乎的,后来才慢慢重视去学习了解,才略知一二,遂分享之. 一.管理Fragement所涉及到的关键类 应用Fragment的过程中涉及到的关键类主要有:FragmentManager和.FragmentManagerImpl.FragmentTransaction和BackStackRecord等. 二.Fragment操作原理详述 1

[android](学习笔记1)利用activity托管fragment的基本方法

activity托管fragment,需要做到以下两点: 1. 在布局中为fragment视图安排位置: 2. 管理fragment实例的生命周期. fragment的生命周期: 参考:http://www.cnblogs.com/purediy/p/3276545.html fragment在代表activity工作,其生命周期状态反应了activity的生命周期状态.fragment生命周期和activity生命周期的关键区别是:fragment的生命周期方法是由托管activity而不是操

Android实习札记(4)---Fragment(碎片)基本概念解析

Android实习札记(4)---Fragment(碎片)基本概念解析 转载请注明出处:coder-pig Fragment相信大家都不会陌生吧,侧滑啦,抽屉效果啦,DialogFragment啊等等,一堆地方都会 用到Fragment,对于Fragment的基本概念在前面的文章中其实已经给出的了,这里回顾一遍, 温故而知新嘛,同时结合Google官方的api进行复习! 1)什么是Fragment以及它的生命周期: 核心要点: 1)3.0版本后引入,即minSdk要大于11 2)Fragment