FragmentTransaction API文档(需要翻墙)
常用方法详解
1. add(int containerViewId, Fragment fragment, String tag)、 remove(Fragment fragment)
API文档说明:
add(int containerViewId, Fragment fragment, String tag) : 向Activity state中添加一个Fragment。
remove(Fragment fragment) : 移除一个已经存在的Fragment.
补充:
add方法 : 参数containerViewId一般会传Activity中某个视图容器的id。如果containerViewId传0,则这个Fragment不会被放置在一个容器中(不要认为Fragment没添加进来,只是我们添加了一个没有视图的Fragment,这个Fragment可以用来做一些类似于service的后台工作)。
remove方法 : Fragment被remove后,Fragment的生命周期会一直执行完onDetach,之后Fragment的实例也会从FragmentManager中移除。
下面看一个使用add方法添加Fragment的坑,相信很多人都遇到过 -> “Fragment被重复添加的问题”
Fragment被重复添加的问题:现在,我们承接上一篇Fragment进阶 - 基本用法中“Fragment动态加载”的事例,给布局容器(fl_main)附加一个点击事件,Toast当前Activity中的所有Fragment信息...
public class MainActivity extends AppCompatActivity {
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_main);
getSupportFragmentManager().beginTransaction().add(R.id.fl_main, new ContentFragment(), null).commit();
findViewById(R.id.fl_main).setOnClickListener(new View.OnClickListener() {
@Override
public void onClick(View v) {
Toast.makeText(MainActivity.this, getSupportFragmentManager().getFragments().toString(), Toast.LENGTH_LONG).show();
}
});
}
}
接下来运行一下程序,我们进行几次横竖屏切换(模拟Activity的异常重启),并且每次切换都点击下布局容器,Toast下信息...
(PS:因录像软件原因,手机横竖屏切换显得有点别扭,各位见谅,Toast看清就Ok)
现象:我们发现Activity里的Fragment数量在不断的增加。
原因:其实,当我们Activty被异常销毁时,Activity会对自身状态进行保存(这里面包含了我们添加的Fragment)。在Activity被重新创建时,又对我们之前保存的Fragment进行了恢复,但是我们在onCreate时却又添加了1次Fragment,所以我们的Fragment数量在不断增加...
添加Fragment的正确姿势:(2种解决方法)
解决方法1(推荐):添加Fragment前检查是否有保存的Activity状态。如果没有状态保存,说明Acitvity是第1次被创建,我们添加Fragment。如果有状态保存,说明Activity刚刚出现过异常被销毁过,现在正在恢复,我们不再添加Fragment。
public class MainActivity extends AppCompatActivity {
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_main);
if(savedInstanceState == null) {
getSupportFragmentManager().beginTransaction().add(R.id.fl_main, new ContentFragment(), null).commit();
}
findViewById(R.id.fl_main).setOnClickListener(new View.OnClickListener() {
@Override
public void onClick(View v) {
Toast.makeText(MainActivity.this, getSupportFragmentManager().getFragments().toString(), Toast.LENGTH_LONG).show();
}
});
}
}
运行下程序,查看下效果:
解决方法2(不推荐):Activity在每次异常退出前,移除当前所有的Fragment。
public class MainActivity extends AppCompatActivity {
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_main);
getSupportFragmentManager().beginTransaction().add(R.id.fl_main, new ContentFragment(), null).commit();
findViewById(R.id.fl_main).setOnClickListener(new View.OnClickListener() {
@Override
public void onClick(View v) {
Toast.makeText(MainActivity.this, getSupportFragmentManager().getFragments().toString(), Toast.LENGTH_LONG).show();
}
});
}
@Override
protected void onSaveInstanceState(Bundle outState) {
if(getSupportFragmentManager().findFragmentById(R.id.fl_main) != null) {
getSupportFragmentManager().beginTransaction().remove(getSupportFragmentManager().findFragmentById(R.id.fl_main)).commit();
}
super.onSaveInstanceState(outState);
}
}
第2种解决方法的运行效果和第1种一样,就不再上图了...
对比两种解决方法:可能我们感觉方法2更加合理,但是我还是推荐第1种方法。
原因1:第2种方法很坑,移除操作只能放在super.onSaveInstanceState(outState)之前,之后移除需要使用commitAllowingStateLoss来替换commit,但是此时的移除操作会在恢复时丢失。
原因2:其实第2种方法还有个Bug,把手机灭屏再打开,会发现Fragment消失了,因为手机灭屏会调用onSaveInstanceState这个方法。
加入第2种方法是为了说明remove的用法,以及Fragment手动移除的坑。总之,Fragment的移除操作需要谨慎。
最后补充一点: add操作可以执行多次,然后一并提交(举个例子,代码如下)。
public class MainActivity extends AppCompatActivity {
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_main);
if(savedInstanceState == null) {
getSupportFragmentManager().beginTransaction()
.add(R.id.fl_main, new ContentFragment(), null)
.add(R.id.fl_main, new ContentFragment(), null)
.add(R.id.fl_main, new ContentFragment(), null)
.commit();
}
}
}
最后运行下程序,我们用Hierarchy Viewer看下视图的层次结构。
和我们想象的一样,我们的Activity视图容器(fl_main)里包含了3个Fragment的视图。
2. replace(int containerViewId, Fragment fragment)、replace(int containerViewId, Fragment fragment, String tag)#####
API文档说明:替换一个已被添加进视图容器的Fragment。
补充:
- 方法作用:类似于先remove掉视图容器所有的Fragment,再add一个想要添加的Fragment。
(举个简单的例子 - 代码如下,我们add三次,replace一次)
public class MainActivity extends AppCompatActivity {
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_main);
getSupportFragmentManager().beginTransaction()
.add(R.id.fl_main, new ContentFragment(), null)
.add(R.id.fl_main, new ContentFragment(), null)
.add(R.id.fl_main, new ContentFragment(), null)
.replace(R.id.fl_main, new ContentFragment(), null)
.commit();
}
}
用Hierarchy Viewer看下视图的层次结构:
我们发现只有一个Fragment的视图,说明之前add的Fragment都在replace时被视图容器移除了。
3. addToBackStack(String name)
API文档说明:将此事务添加到后台堆栈。这意味着该事务被提交后将会被记住后,回退操作后,事务会从堆栈中弹出。
补充:
方法作用:记录已提交的事务(transation),可用于回退操作。
参数说明:name是这次回退操作的一个名称(或标识),不需要可以传null。
下面还是以add方法举例:
public class MainActivity extends AppCompatActivity {
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_main);
if(savedInstanceState == null) {
getSupportFragmentManager().beginTransaction()
.add(R.id.fl_main, new ContentFragment(), null)
.addToBackStack(null)
.commit();
}
}
@Override
public void onBackPressed() {
Toast.makeText(this, "onBackPressed", Toast.LENGTH_SHORT).show();
super.onBackPressed();
}
}
我们如果按下手机的back键,Activity会回调onBackPressed()方法...接下来看下运行效果...
第1次按下手机的back键,程序没有退出而是变成了白板,其实是回退了之前的add操作,相当于没有添加Fragment。
第2次按下手机的back键,因为当前回退栈已空,程序就直接退出了。
这里是以add方法为例,当然addToBackStack也适用于remove、hide、show、attach、detach等操作。
4. show(Fragment fragment)、hide(Fragment fragment)
API文档说明:
hide(Fragment fragment) : 隐藏一个存在的Fragment
show(Fragment fragment) : 显示一个以前被隐藏过的Fragment
补充:
Fragment被hide/show,仅仅是隐藏/显示Fragment的视图,不会有任何生命周期方法的调用。
在Fragment中重写onHiddenChanged方法可以对Fragment的hide和show状态进行监听。
5. attach(Fragment fragment)、detach(Fragment fragment)
API文档说明:
detach(Fragment fragment) : 分离指定Fragment的UI视图
attach(Fragment fragment) : 重新关联一个Fragment(当这个Fragment的detach执行之后)
补充:
当Fragment被detach后,Fragment的生命周期执行完onDestroyView就终止了,这意味着Fragment的实例并没有被销毁,只是UI界面被移除了(注意和remove是有区别的)。
当Fragment被detach后,执行attach操作,会让Fragment从onCreateView开始执行,一直执行到onResume。
attach无法像add一样单独使用,单独使用会抛异常。方法存在的意义是对detach后的Fragment进行界面恢复。
6. setCustomAnimations(int enter, int exit)、setCustomAnimations(int enter, int exit, int popEnter, int popExit)
API文档说明:为Fragment的进入/退出设置指定的动画资源。
补充:
该方法用于给Fragment设置自定义切换动画。
四个参数方法的后两个参数也是传入指定动画资源,在某个事务被addToBackStack或回退时起动画效果。
getSupportFragmentManager不支持属性动画,仅支持补间动画。getFragmentManager支持属性动画。
使用setCustomAnimations一定要放在add、remove...等操作之前。
举个简单的使用例子:
public class MainActivity extends AppCompatActivity {
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_main);
}
public void add(View view){
getSupportFragmentManager().beginTransaction()
.setCustomAnimations(R.anim.enter_anim, R.anim.exit_anim)
.add(R.id.fl_main, new ContentFragment(), null)
.commit();
}
public void remove(View view){
if(getSupportFragmentManager().findFragmentById(R.id.fl_main) != null) {
getSupportFragmentManager().beginTransaction()
.setCustomAnimations(R.anim.enter_anim, R.anim.exit_anim)
.remove(getSupportFragmentManager().findFragmentById(R.id.fl_main))
.commit();
}else{
Toast.makeText(this, "未找到可移除的指定id的Fragment",Toast.LENGTH_SHORT).show();
}
}
}
我们在布局文件中添加了两个按钮,一个用于添加Fragment,一个用于移除Fragment...运行效果如下图:
7. addSharedElement(View sharedElement, String name)
API文档说明:用于将View从一个被删除的或隐藏的Fragment映射到另一个显示或添加的Fragment上。
补充:
方法作用:设置共享元素(或着说共享View),第二个参数name是这个共享元素的标识。
8. commit()、 commitAllowingStateLoss()、 commitNow()、commitNowAllowingStateLoss()
API文档说明:
commit():安排一个事务的提交。
commitAllowingStateLoss():和commit一样,但是允许Activity的状态保存之后提交。
commitNow():同步的提交这个事务。(API_24添加的)
commitNowAllowingStateLoss():和commitNow()一样,但是允许Activity的状态保存之后提交。(API_24添加的)
补充:
commit():安排一个针对该事务的提交。提交并没有立刻发生,会安排到在主线程下次准备好的时候来执行。
commitNow():同步的提交这个事务。任何被添加的Fragment都将会被初始化,并将他们完全带入他们的生命周期状态。
使用commitNow()类似于先调用commit()方法,之后调通FragmentManager的
executePendingTransactions() 方法,但commitNow()比后者的这种操作更好,后者在尝试提交当前事务时会有副作用(具体的副作用是什么,API文档没有给出说明)。使用commitNow()时不能进行添加回退栈的操作,如果使用 addToBackStack(String)将会抛出一个 IllegalStateException的异常。
如何选择正确的提交方式:(博客推荐)
①The many flavors of commit()(英文版)
②选择正确的 Fragment#commitXXX() 函数(中文版)
后续小编还会写一篇博客:介绍commit的使用细节以及踩坑分析。
FragmentTransaction的全部方法(API 24)
最后我们给出FragmentTransaction提供的全部方法,方便各位快速了解和查阅。
- add(Fragment fragment, String tag) // 调用add(int, Fragment, String),填入为0的containerViewId.
- add(int containerViewId, Fragment fragment) // 调用add(int, Fragment, String),填入为null的tag.
- add(int containerViewId, Fragment fragment, String tag) // 向Activity中添加一个Fragment.
- addSharedElement(View sharedElement, String name) // 添加共享元素
- addToBackStack(String name) // 将事务添加到回退栈
- attach(Fragment fragment) // 重新关联Fragment(当Fragment被detach时)
- commit() // 提交事务
- commitAllowingStateLoss() // 类似commit(),但允许在Activity状态保存之后提交(即允许状态丢失)。
- commitNow() // 同步提交事务
- commitNowAllowingStateLoss() // 类似commitNow(),但允许在Activity状态保存之后提交(即允许状态丢失)。
- detach(Fragment fragment) // 将fragment保存的界面从UI中移除
- disallowAddToBackStack() // 不允许调用addToBackStack(String)操作
- hide(Fragment fragment) // 隐藏已存在的Fragment
- isAddToBackStackAllowed() // 是否允许添加到回退栈
- isEmpty() // 事务是否未包含的任何操作
- remove(Fragment fragment) // 移除一个已存在的Fragment
- replace(int containerViewId, Fragment fragment) // 调用replace(int, Fragment, String)填入为null的tag.
- replace(int containerViewId, Fragment fragment, String tag) // 替换已存在的Fragment
- setBreadCrumbShortTitle(int res) // 为事务设置一个BreadCrumb短标题
- setBreadCrumbShortTitle(CharSequence text) // 为事务设置一个BreadCrumb短标题,将会被FragmentBreadCrumbs使用
- setBreadCrumbTitle(int res) // 为事务设置一个BreadCrumb全标题,将会被FragmentBreadCrumbs使用
- setBreadCrumbTitle(CharSequence text) // 为事务设置一个BreadCrumb全标题
- setCustomAnimations(int enter, int exit, int popEnter, int popExit) // 自定义事务进入/退出以及入栈/出栈的动画效果
- setCustomAnimations(int enter, int exit) // 自定义事务进入/退出的动画效果
- setTransition(int transit) // 为事务设置一个标准动画
- setTransitionStyle(int styleRes) // 为事务标准动画设置自定义样式
- show(Fragment fragment) // 显示一个被隐藏的Fragment