Android - fragment Manager

fragment基本使用:

http://www.cnblogs.com/qlky/p/5415679.html

Fragmeng优点

  • Fragment可以使你能够将activity分离成多个可重用的组件,每个都有它自己的生命周期和UI。
  • Fragment可以轻松得创建动态灵活的UI设计,可以适应于不同的屏幕尺寸。从手机到平板电脑。
  • Fragment是一个独立的模块,紧紧地与activity绑定在一起。可以运行中动态地移除、加入、交换等。
  • Fragment提供一个新的方式让你在不同的安卓设备上统一你的UI。
  • Fragment 解决Activity间的切换不流畅,轻量切换。
  • Fragment 替代TabActivity做导航,性能更好。
  • Fragment 在4.2.版本中新增嵌套fragmeng使用方法,能够生成更好的界面效果。
  • Fragment做局部内容更新更方便,原来为了到达这一点要把多个布局放到一个activity里面,现在可以用多Fragment来代替,只有在需要的时候才加载Fragment,提高性能

 

生命周期

 

1、FragmentManager

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

常用的方法有:

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

2、FragmentTransaction

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

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

 

三、有关回滚——FragmentTransaction

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

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

transaction.addToBackStack(String tag); 

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

manager.popBackStack();  

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

  1. void popBackStack(int id, int flags);  
  2. 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时,表示连着参数一指定的这一层一起退出栈; 
    • 除了这几个函数,还有下面几个函数:有关他们的使用,我们在这小部分结尾时会提到
  1. popBackStackImmediate()  
  2. popBackStackImmediate(String tag)  
  3. popBackStackImmediate(String tag, int flag)  
  4. popBackStackImmediate(int id, int flag)  

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

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

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

通过添加监听器,就可以在回退栈内容改变时,及时收到通知;

 

3、Transaction事务回退的原则

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

 

一、hide()、show()

1、基本使用

这两个函数的功能非常简单, 

  1. public FragmentTransaction hide(Fragment fragment);//将指定的fragment隐藏不显示   
  2. public FragmentTransaction show(Fragment fragment);//将以前hide()过的fragment显示出来 

2、在实战中的运用方法

如果我们使用replace来切换页面,那么在每次切换的时候,Fragment都会重新实例化,重新加载一边数据,这样非常消耗性能和用户的数据流量。
这是因为replace操作,每次都会把Container中的现有的fragment实例清空,然后再把指定的fragment添加进去,就就造成了在切换到以前的fragment时,就会重新实例会fragment。
正确的切换方式是add(),切换时hide(),add()另一个Fragment;再次切换时,只需hide()当前,show()另一个。
这样就能做到多个Fragment切换不重新实例化:(基本算法如下)

public void switchContent(Fragment from, Fragment to) {  
    if (!to.isAdded()) {    // 先判断是否被add过  
        transaction.hide(from).add(R.id.content_frame, to).commit(); // 隐藏当前的fragment,add下一个到Activity中  
    } else {  
        transaction.hide(from).show(to).commit(); // 隐藏当前的fragment,显示下一个  
    }  
}

大家可能觉得这里有个问题,如果我们要show()的fragment不在最顶层怎么办?如果不在ADD队列的队首,那显然show()之后是不可见的;那岂不影响了APP逻辑。大家有这个想法是很棒的,但在APP中不存在这样的情况,因为我们的APP的fragment是一层层ADD进去的,而且我们的fragment实例都是唯一的,用TAG来标识,当退出的时候也是一层层剥离的,所以当用户的动作导致要添加某个fragment时,那说明这个fragment肯定是在栈顶的。

 

二、detach()、attach()

这两个函数的声明如下:

  1. public FragmentTransaction detach(Fragment fragment);  
  2. public abstract FragmentTransaction attach(Fragment fragment);  

 

detach():会将view与fragment分离,将此将view从viewtree中删除!而且将fragment从Activity的ADD队列中移除!所以在使用detach()后,使用fragment::isAdded()返回的值是false;但此fragment实例并不会删除,此fragment的状态依然保持着使用,所以在fragmentManager中仍然可以找到,即通过FragmentManager::findViewByTag()仍然是会有值的。 
attach():显然这个方法与detach()所做的工作相反,它一方面利用fragment的onCreateView()来重建视图,一方面将此fragment添加到ADD队列中;这里最值得注意的地方在这里:由于是将fragment添加到ADD队列,所以只能添加到列队头部,所以attach()操作的结果是,最新操作的页面始终显示在最前面!这也就解释了下面的例子中,为了fragment2 detach()后,当再次attach()后,却跑到了fragment3的前面的原因。还有,由于这里会将fragment添加到Activity的ADD队列中,所以在这里调用fragment::isAdded()将返回True; 

 

三、Fragment参数传递

 

在关Fragment间参数的传递,有两种情况:

  • 第一种情况:同一个container中不同fragment间的参数传递。这种情况一般发生在fragment跳转时,上一个Fragment将参数传递给下一个Fragment。
  • 第二种情况:是同一个Activity中,不个container间Fragment的参数传递。

有关第一种情况,以前写过一篇文章,详细说明了上一个Fragment将参数传递给下一个Fragment,及数据回传的方法。详细参见:《Fragment跳转时传递参数及结果回传的方法》

 

当我们实例化自定义Fragment时,官方推荐Fragment.setArguments(Bundle bundle)这种方式来传递参数,而不推荐通过构造方法直接来传递参数

在屏幕旋转时,activity会重构,而依附在activity上的fragment最终会通过反射无参构造实例化一个新的Fragment,并且给mArgments初始化为原先的值,而原来的Fragment实例的数据都丢失了,并重新进行了初始化

通过上面的分析,我们可以知道Activity重新创建时,会重新构建它所管理的Fragment,原先的Fragment的字段值将会全部丢失,但是通过Fragment.setArguments(Bundle bundle)方法设置的bundle会保留下来。所以尽量使用Fragment.setArguments(Bundle bundle)方式来传递参数

 

二、同一个Activity,不同container间的参数传递

这里到了这篇文章的重点内容了哦,这可并不是说上一部分不重要哈,其实上一部分要比这部分重要!同一个Container中不同Fragment间的参数传递一般的工程都会用到的,所以大家一定要看

这里有多种实现方法,最可取的是方法三。我们由简到易慢慢讲。


我们想使两个fragment实例要能通信,那如果我们都能通过findViewById()找到所有的控件,直接操控的话,岂不就实现了。而通过findViewById()能找到所有控件实例的地方就是在Activity中了,所以这就有了方法一。

方法一:直接在Activity中操作

可见,直接在activity中操作各个fragment的控件就可以实现消息互传。但,这样真的好吗?如果每个fragment中的控件都在Activity中操作,那还要fragment干嘛!最最起码,应该每个fragment负责自己的控件操作才对嘛!
所以,我们对这种方法进行改进,将点击Item的赋值操作放到fragment1中去。所以,这就有方法二;

方法二:直接在fragment中操作

在这里我们会把所有方法写在Fragment1中,这里涉及到两方面的内容:
第一:在Fragment中如何获得自己控件的引用,比较这里Fragment1里的listview控件。
第二:在Fragment中如何获得其它Fragment页面中控件的引用,比如这里Fragment2里的TextView控件。 

首先,获取自己控件引用的方法: 


方法一:在onCreateView()中获取。 

由于在onCreateView()中,还没有创建视图,所以在这里如果使用getView()方法将返回空。所以如果要获取其实图中指定控件的引用,只用用inflater.inflate()返回的rootView;在这个rootView()中用findViewById来查找。


方法二:在onActivityCreated()函数中获取。
《Fragment详解之一——概述》的流程图中可以看到,onActivityCreated()回调会在Activity的OnCreate()执行完成后再执行,也就是说,onActivityCreated()会在Activity的OnCreate()工作完成以后才会执行。所以当执行到onActivityCreated()的时候Activity已经创建完成,它其中的各个fragment也视图等等的也都已经创建完成。所在可以在这里获取跟Activity相关的各种资源。第二个问题中的获取其它Fragment页面中控件的引用也是在onActivityCreated()中来做的。先看看在onActivityCreated()中如何获得自己视图中控件的引用吧

 

然后,获得其它Fragment页面中控件的引用的方法
在上面已经说了,要获取Activity中的资源,就必须等Acitivity创建完成以后,所以必须放在onActivityCreated()回调函数中。
其获取方法为:

public void onActivityCreated(Bundle savedInstanceState) {  
    super.onActivityCreated(savedInstanceState);  
  
    mFragment2_tv = (TextView) getActivity().findViewById(R.id.fragment2_tv);//获取其它fragment中的控件引用的唯一方法!!!  
  
}

 

我们这里直接在fragment1中操作了fragment2的控件,这样就违背了模块分离的思想,我们应该让他们各自处理各自的代码才好。所以,考虑到将他们分离,我们这里就出现了方法三。

方法三:在各自的fragment中操作

好,我们先想想要怎么解决这个问题,首先,我们把各自的事件放在各自的fragment中处理,即在fragment1中能得到当前用户点击的ITEM的String字符串,在fragment2中可以设置textview的值。那问题来了,各自获得了各自的东东,那通过什么让他们交互呢?
答案显然是activity。
很显然,在activity中可以直接通过FragmentManager::findFragmentById()来获取fragment2的实例。进而调用fragment2中的任意方法,这就实现了与fragment2的通信。
那fragment1又怎么把结果回传给Activity呢?大家如果在看到这里之前先看了《Fragment跳转时传递参数及结果回传的方法》就很容易想到,用回调!请记住。在不同页面中回传结果,回调是万能的解决方案。

 

posted @ 2017-07-20 11:46  qlky  阅读(1394)  评论(0编辑  收藏  举报