Fragment栈管理

11 篇文章 1 订阅
2 篇文章 0 订阅

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方法实现切换。

演示:

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值