1.基本栈管理
定义两个Fragment并且分别实现他们的add,remove和repalce方法。
public class MainActivity extends AppCompatActivity {
private Fragment1 fragment1;
private Fragment2 fragment2;
private FragmentManager manager;
private FragmentTransaction transaction;
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_main);
fragment1 = new Fragment1();
fragment2 = new Fragment2();
manager = getSupportFragmentManager();
}
public void add_fragment1(View view) {
transaction = manager.beginTransaction();
if (!fragment1.isAdded()) {
transaction.add(R.id.fragment_contain, fragment1, "Fragment1");
}else {
Log.d("MainActivity", "Fragment1 is already added");
}
transaction.commit();
}
public void add_fragment2(View view) {
transaction = manager.beginTransaction();
if (!fragment2.isAdded()) {
transaction.add(R.id.fragment_contain, fragment2, "Fragment2");
} else {
Log.d("MainActivity", "Fragment2 is already added");
}
transaction.commit();
}
public void remove_fragment1(View view) {
transaction = manager.beginTransaction();
transaction.remove(fragment1);
transaction.commit();
}
public void remove_fragment2(View view) {
transaction = manager.beginTransaction();
transaction.remove(fragment2);
transaction.commit();
}
public void replace_fragment1(View view) {
transaction = manager.beginTransaction();
transaction.replace(R.id.fragment_contain,fragment1);
transaction.commit();
}
public void replace_fragment2(View view) {
transaction = manager.beginTransaction();
transaction.replace(R.id.fragment_contain,fragment2);
transaction.commit();
}
}
特性一:同一个Fragment不可以add两次。
会报Fragment already added的异常,程序直接奔溃。
java.lang.IllegalStateException: Fragment already added: Fragment2{4a0ff15 #1 id=0x7f070044 Fragment2}
at androidx.fragment.app.FragmentManagerImpl.addFragment(FragmentManager.java:1916)
特性二:多次执行remove某个Fragment或者remove一个没有add的Fragment都不会报错,也不会有任何反应。
源码确实也没有效果的异常判断,感觉不是特别合理,不过影响不大。
特性三:执行replace操作会清空所有的Fragment,在添加需要replace的那个Fragment。
操作步骤:
第一步:add Fragment1 视图显示Fragment1
第二步:add Fragment2 视图显示Fragment2
第三步:replace Fragment1 视图显示Fragment1
第四步:remove Fragment1 视图显示空白
录像演示:
通过上面的出入栈示意图和录像操作,非常明显在执行第三步 replace Fragment1的时候,栈里面的Fragment1和Fragment2先被移除,再添加进Fragment1。
2.高级栈管理(通过FragmentManamger和FragmentTransation)
和add remove replace方式的区别
前面的add,remove,replace方法不是已经实现了类似栈管理的功能了吗?为什么还需要别的栈管理?
区别一:更灵活。因为add,remove,replace实现简单的栈操作,如果需要更灵活的操作,就要借助于FragmentManager和FragmentTransation提供的栈操作方法。
区别二:颗粒度不同。高级栈管理的单位是commit,一个commit可以包含例如多个add 等操作。也体现了他的灵活性。(具体下面的2.2小节会讲)。
2.1 addToBackStack和popBackStack();方法
顾名思义这两个方法就是分别执行入栈和出栈的操作
addToBackStack是FragmentTransation的方法。
popBackStack是FragmentManager的方法。
transaction.addToBackStack("Fragment1");
transaction.commit();
manager.popBackStack();
完整代码:
public class PopStackActivity extends AppCompatActivity {
private Fragment1 fragment1;
private Fragment2 fragment2;
private Fragment3 fragment3;
private FragmentManager manager;
private FragmentTransaction transaction;
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_pop_stack);
fragment1 = new Fragment1();
fragment2 = new Fragment2();
fragment3 = new Fragment3();
manager = getSupportFragmentManager();
}
public void add_fragment1(View view) {
transaction = manager.beginTransaction();
if (!fragment1.isAdded()) {
transaction.add(R.id.fragment_contain, fragment1, "Fragment1");
}else {
Log.d("BasicStackActivity", "Fragment1 is already added");
}
transaction.addToBackStack("Fragment1");
transaction.commit();
}
public void add_fragment2(View view) {
transaction = manager.beginTransaction();
if (!fragment2.isAdded()) {
transaction.add(R.id.fragment_contain, fragment2, "Fragment2");
} else {
Log.d("BasicStackActivity", "Fragment2 is already added");
}
transaction.addToBackStack("Fragment2");
transaction.commit();
}
public void add_fragment3(View view) {
transaction = manager.beginTransaction();
if (!fragment3.isAdded()) {
transaction.add(R.id.fragment_contain, fragment3, "Fragment3");
} else {
Log.d("BasicStackActivity", "Fragment3 is already added");
}
transaction.addToBackStack("Fragment3");
transaction.commit();
}
public void pop_fragment(View view) {
manager.popBackStack();
}
}
演示1:分别将三个Fragment1 Fragment2 Fragment3依次入栈,再出栈。
演示2:将Fragment1入栈3次,再出栈三次。
确实有三个Fragment1入栈了。这个演示其实是为了说明这种方式和add方法的不同之处。add方法只能add一次,在add就会直接抛异常,前面已经验证过。而这种方式不会。
2.2 popBackStack操作的单位
在进行pop操作的时候,是以commit为操作单位的。如果将add Fragment1 commit一次,再把add Fragment2 和add Fragment3和起来commit一次,再执行popBackStack.
那么Fragment2和Fragment3将一起出栈。
public class PopBackStackUnitActivity extends AppCompatActivity {
private Fragment1 fragment1;
private Fragment2 fragment2;
private Fragment3 fragment3;
private FragmentManager manager;
private FragmentTransaction transaction;
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_pop_back_stack_unit);
fragment1 = new Fragment1();
fragment2 = new Fragment2();
fragment3 = new Fragment3();
manager = getSupportFragmentManager();
}
public void add_fragment1(View view) {
transaction = manager.beginTransaction();
if (!fragment1.isAdded()) {
transaction.add(R.id.fragment_contain, fragment1, "Fragment1");
}else {
Log.d("BasicStackActivity", "Fragment1 is already added");
}
transaction.addToBackStack("Fragment1");
transaction.commit();
}
public void add_fragment2And3(View view) {
transaction = manager.beginTransaction();
if (!fragment2.isAdded()) {
transaction.add(R.id.fragment_contain, fragment2, "Fragment2");
} else {
Log.d("BasicStackActivity", "Fragment2 is already added");
}
if (!fragment3.isAdded()) {
transaction.add(R.id.fragment_contain, fragment3, "Fragment2");
} else {
Log.d("BasicStackActivity", "Fragment3 is already added");
}
transaction.addToBackStack("Fragment2and3");
transaction.commit();
}
public void pop_fragment(View view) {
manager.popBackStack();
}
}
和我们想的一样Fragment2和3同时一起入栈也同时一起出栈。
2.3通过commit返回的Id管理栈
前面讲过,popBackStack的操作单位是commit。实际上每次commit完成都会放回一个commit Id。这个Id就代表了这次commit。
而popBackStack有重载方法,通过传入Id可以对栈实现指定位置的操作。
popBackStack(int id, int flags);
id就是前面提到的commit返回的id。
还需要一个flags参数,传入POP_BACK_STACK_INCLUSIVE或者0。
查看POP_BACK_STACK_INCLUSIVE的说明,简单翻译下就是会查找所有相同的id的实例,直到底部或者完全没有匹配到id实例。如果匹配到,那么这个id实例自身以及上面的内容也会一起移除。如果传入的值是0,那么这个id实例上面的内容也会移除(不包括自身)。
其实这个0很奇怪,个人觉得应该再定义一个常量,而不是用0来做参数。
/**
* Pop all back stack states up to the one with the given identifier.
* This function is asynchronous -- it enqueues the
* request to pop, but the action will not be performed until the application
* returns to its event loop.
*
* @param id Identifier of the stated to be popped. If no identifier exists,
* false is returned.
* The identifier is the number returned by
* {@link FragmentTransaction#commit() FragmentTransaction.commit()}. The
* {@link #POP_BACK_STACK_INCLUSIVE} flag can be used to control whether
* the named state itself is popped.
* @param flags Either 0 or {@link #POP_BACK_STACK_INCLUSIVE}.
*/
public abstract void popBackStack(int id, int flags);
/**
* Flag for {@link #popBackStack(String, int)}
* and {@link #popBackStack(int, int)}: If set, and the name or ID of
* a back stack entry has been supplied, then all matching entries will
* be consumed until one that doesn't match is found or the bottom of
* the stack is reached. Otherwise, all entries up to but not including that entry
* will be removed.
*/
public static final int POP_BACK_STACK_INCLUSIVE = 1<<0;
演示1 flags的值为POP_BACK_STACK_INCLUSIVE:
1.分别commit三次,添加Fragment1 Fragment2 Fragment3。
2.记录Fragment2 commit返回的Id.
3.执行popBackStack(Fragment2 commit的id , POP_BACK_STACK_INCLUSIVE);
效果和我们想象的一样。通过pop Fragment2的commit id。Fragment2和Fragment3一起被移除了。
同时我们打印出三次commit的id.
演示2:flags的值为0
com.visual.org.fragmentstack D/PopStackActivity: fragment1stackId:0
com.visual.org.fragmentstack D/PopStackActivity: fragment2stackId:1
com.visual.org.fragmentstack D/PopStackActivity: fragment3stackId:2
完整代码:
public class PopStackActivity extends AppCompatActivity {
private Fragment1 fragment1;
private Fragment2 fragment2;
private Fragment3 fragment3;
private FragmentManager manager;
private FragmentTransaction transaction;
private int fragment1stackId;
private int fragment2stackId;
private int fragment3stackId;
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_pop_stack);
fragment1 = new Fragment1();
fragment2 = new Fragment2();
fragment3 = new Fragment3();
manager = getSupportFragmentManager();
}
public void add_fragment1(View view) {
transaction = manager.beginTransaction();
if (!fragment1.isAdded()) {
transaction.add(R.id.fragment_contain, fragment1, "Fragment1");
}else {
Log.d("BasicStackActivity", "Fragment1 is already added");
}
transaction.addToBackStack("Fragment1");
fragment1stackId=transaction.commit();
Log.d("PopStackActivity", "fragment1stackId:" + fragment1stackId);
}
public void add_fragment2(View view) {
transaction = manager.beginTransaction();
if (!fragment2.isAdded()) {
transaction.add(R.id.fragment_contain, fragment2, "Fragment2");
} else {
Log.d("BasicStackActivity", "Fragment2 is already added");
}
transaction.addToBackStack("Fragment2");
fragment2stackId=transaction.commit();
Log.d("PopStackActivity", "fragment2stackId:" + fragment2stackId);
}
public void add_fragment3(View view) {
transaction = manager.beginTransaction();
if (!fragment3.isAdded()) {
transaction.add(R.id.fragment_contain, fragment3, "Fragment3");
} else {
Log.d("BasicStackActivity", "Fragment3 is already added");
}
transaction.addToBackStack("Fragment3");
fragment3stackId=transaction.commit();
Log.d("PopStackActivity", "fragment3stackId:" + fragment3stackId);
}
public void pop_fragment(View view) {
manager.popBackStack();
}
public void pop_fragment2(View view) {
manager.popBackStack(fragment2stackId,FragmentManager.POP_BACK_STACK_INCLUSIVE);
//manager.popBackStack(fragment2stackId,0);
}
}
2.4 通过设置name管理栈
和2.3小节一样,popBackStack还有一个重载方法。
popBackStack(String name, int flags);
这里的name指的是transaction.addToBackStack("Fragment2");这里设置的name。
manager.popBackStack(“Fragment2”,FragmentManager.POP_BACK_STACK_INCLUSIVE);
实现的效果和通过commit id的效果一样,这里就不演示了。
2.5 popBackStackImmediate方法
public boolean popBackStackImmediate();
public boolean popBackStackImmediate(@Nullable String name, int flags);
public boolean popBackStackImmediate(int id, int flags);
和popBackStack的用法一模一样。有什么区别呢?
popBackStack是异步操作。而且会比popBackStackImmediate多一个入队列的操作,所以可能会慢一点。不过他是线程安全的。
简单查看下源码就知道了。
@Override
public void popBackStack() {
enqueueAction(new PopBackStackState(null, -1, 0), false);
}
@Override
public boolean popBackStackImmediate() {
checkStateLoss();
return popBackStackImmediate(null, -1, 0);
}
关键就是popBackStack调用的enqueueAction,里面有同步代码块,说明是线程安全的,而且文档也说了是异步操作。
/**
* Adds an action to the queue of pending actions.
*
* @param action the action to add
* @param allowStateLoss whether to allow loss of state information
* @throws IllegalStateException if the activity has been destroyed
*/
public void enqueueAction(OpGenerator action, boolean allowStateLoss) {
if (!allowStateLoss) {
checkStateLoss();
}
synchronized (this) {
if (mDestroyed || mHost == null) {
if (allowStateLoss) {
// This FragmentManager isn't attached, so drop the entire transaction.
return;
}
throw new IllegalStateException("Activity has been destroyed");
}
if (mPendingActions == null) {
mPendingActions = new ArrayList<>();
}
mPendingActions.add(action);
scheduleCommit();
}
}
而popBackStackImmediate最终会调用一个叫execPendingActions的方法,是同步的。
/**
* Only call from main thread!
*/
public boolean execPendingActions() {
ensureExecReady(true);
boolean didSomething = false;
while (generateOpsForPendingActions(mTmpRecords, mTmpIsPop)) {
mExecutingActions = true;
try {
removeRedundantOperationsAndExecute(mTmpRecords, mTmpIsPop);
} finally {
cleanupExec();
}
didSomething = true;
}
doPendingDeferredStart();
burpActive();
return didSomething;
}
所以如果没有线程安全问题的话用popBackStackImmediate会更快一点。
3.hide和show方法
3.1hide和show基本使用
用法非常简单,和add差不多。
public void show_fragment3(View view) {
transaction = manager.beginTransaction();
transaction.show(fragment3);
transaction.commit();
}
public void hide_fragment3(View view) {
transaction = manager.beginTransaction();
transaction.hide(fragment3);
transaction.commit();
}
演示:
1.依次添加三个Fragment,最后显示的是Fragment3.
2.执行Hide 3,Fragment3隐藏,Fragment2显示在了最前面。
3.执行Hide 2,Fragment2也隐藏,Fragment1显示在了最前面。
4.执行Hide1,Fragment1也隐藏,界面空白,所有的Fragment都隐藏了。
5.执行Show1,Fragment1显示在最前面。
6.执行Show2,Fragment2挡住了Fragment1显示在最前面。
7.执行Show3,Fragment3显示在最前面。
8.执行Hide2,由于Fragment2被Fragment3挡住了,所以前面显示的还是Fragment3,但实际上Fragment2已经隐藏了。
9.执行Hide3,由于前一步的Hide2操作,Fragment2已经隐藏,现在Fragment3也隐藏,所以最前面显示Fragment1。
3.2 利用hide和show实现Fragment的切换
主要方法就是switchContent方法。
public void switchContent(Fragment from, Fragment to) {
transaction = manager.beginTransaction();
if (!to.isAdded()) { // 先判断是否被add过
transaction.hide(from).add(R.id.fragment_contain, to).commit(); // 隐藏当前的fragment,add下一个到Activity中
} else {
transaction.hide(from).show(to).commit(); // 隐藏当前的fragment,显示下一个
}
curruntFragment=to;
}
完整代码。
public class SwitchFragmentActivity extends AppCompatActivity {
private Fragment1 fragment1;
private Fragment2 fragment2;
private Fragment3 fragment3;
private Fragment curruntFragment;
private FragmentManager manager;
private FragmentTransaction transaction;
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_switch_fragment);
fragment1 = new Fragment1();
fragment2 = new Fragment2();
fragment3 = new Fragment3();
manager = getSupportFragmentManager();
initDefaultFragment();
}
private void initDefaultFragment() {
transaction = manager.beginTransaction();
if (!fragment1.isAdded()) {
transaction.add(R.id.fragment_contain, fragment1, "Fragment1");
// curruntFragment=fragment1;
}else {
Log.d("BasicStackActivity", "Fragment1 is already added");
}
curruntFragment=fragment1;
transaction.commit();
}
public void switch_fragment1(View view) {
switchContent(curruntFragment,fragment1);
}
public void switch_fragment2(View view) {
switchContent(curruntFragment,fragment2);
}
public void switch_fragment3(View view) {
switchContent(curruntFragment,fragment3);
}
public void switchContent(Fragment from, Fragment to) {
transaction = manager.beginTransaction();
if (!to.isAdded()) { // 先判断是否被add过
transaction.hide(from).add(R.id.fragment_contain, to).commit(); // 隐藏当前的fragment,add下一个到Activity中
} else {
transaction.hide(from).show(to).commit(); // 隐藏当前的fragment,显示下一个
}
curruntFragment=to;
}
}
默认加载Fragment1。通过switchContent方法实现切换。
演示: