Fragment数据传递及回退栈

上一篇文章分析了使用replace 和 使用hide/show 两种方式实现Fragment的切换,及对应的生命周期,这一篇文章在介绍Fragment回退栈之前先介绍一下FragmentManager和FragmentTransaction。

一、动态创建Fragment与FragmentManager

上一章最后,我们已经实现了一个通过FragmentManager、FragmentTransaction动态创建Fragment的例子,关键代码如下:

FragmentManager fm = getSupportFragmentManager();
FragmentTransaction ft = fm.beginTransaction();

首先创建了一个Fragment的实例,接着调用getSupportFragmentManager()(如果使用app包下的Fragment使用getFragmentManager())获取到FragmentManager对象,该对象负责维护Fragment列表及回退栈,然后通过FragmentManager的beginTransaction()方法获取FragmentTransaction对象,FragmentTransaction对象是对单个Fragment进行添加、隐藏、移除、替换以及将Fragment添加进回退栈等操作。

FragmentTransaction完成对Fragment的操作后需要通过commit()方法提交才能生效

并且一个FragmentTransaction对象无法执行两次commit(),每次需要commit()都要调用FragmentManager的beginTransaction()方法获取FragmentTransaction对象。

二、Fragment与Activity的交互

大家都知道Activity之间一般时通过Intent携带Bundle实现数据的交互,但Activity向Fragment发送数据无法使用Intent,并且不建议使用带参数的构造方法,因为Activity重启后会重新调用Fragment无参数的构造方法,导致我们传递的数据丢失,这里我们就需要使用Android SDK为我们提供的setArguments(Bundle args)方法来解决这个问题。该方法原型如下:

    public void setArguments(Bundle args) {
        if (mIndex >= 0 && isStateSaved()) {
            throw new IllegalStateException("Fragment already active and state has been saved");
        }
        mArguments = args;
    }

如果在Activity中调用该方法,就可以将Activity的数据通过Bundle发送给Fragment了,但是有一点需要注意,该方法的注释上有一句This method cannot be called if the fragment is added to a FragmentManager,就是说这个方法要在FragmentTransaction.commit()之前调用。这与方法回调时机有关,fragment的创建涉及子线程,FragmentTransaction.commit()之前调用该方法能保证在Fragment的生命周期回调中能获取到传递的参数。

实例化Fragment常用的一种写法:

  public static MyFragment getInstance(Bundle bundle){
        MyFragment fragment = new MyFragment();
        fragment.setArguments(bundle);
        return fragment;
    }

fragment.setArguments(bundle);这个方法中将bundle设为Fragment的成员变量,并且对其数据进行了保存,在Activity重建后,也能获取到该数据,避免了数据丢失的情况。

三、Fragment回退栈

Fragment回退栈由FragmentManager负责出栈及监听,FragmentTransaction调用addToBackStack方法负责Fragment入栈。

具体来说就是每次调用FragmentTransaction的commit之前,调用一下FragmentTransaction的addToBackStack(String)这个方法,参数String指定了回退栈记录的名称,该参数能传null。

这样调用commit时就将该commit操作加入了回退栈,使用FragmentManager的popBackStack()方法能对该操作进行出栈,从而实现Fragment的切换。值得注意的是,一个Fragment对象可以多次入栈,直到回退栈中不再存在该Fragment对象,才会执行该Fragment对象的最终销毁方法(onDestroy,onDetach),否则该F ragment对象的生命周期将一直在onCreateView和onDestroyView之间徘徊。

写段代码验证一下,主体还是基于上一篇文章

public class FragmentShowActivity extends AppCompatActivity {
    RelativeLayout mRoot;
    ShowFragment show1;
    ShowFragment show2;
    Button jump;
    Button pop;
    FragmentManager fm;
    boolean is1 = true;

    private static final String TAG = "FragmentShowActivity";

    @Override
    protected void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        setContentView(R.layout.activity_fragment_show);

        mRoot = findViewById(R.id.rl);
        jump = findViewById(R.id.jump);
        pop = findViewById(R.id.pop);

        show1 = ShowFragment.getInstance("show1");
        show2 = ShowFragment.getInstance("show2");

        jump.setOnClickListener(new View.OnClickListener() {
            @Override
            public void onClick(View v) {
                FragmentManager fm = getSupportFragmentManager();
                FragmentTransaction ft = fm.beginTransaction();
                if (!is1) {
                    ft.replace(R.id.fragment,show2);
                    ft.addToBackStack(null);
                    ft.commit();
                } else {
                    ft.replace(R.id.fragment,show1);
                    ft.addToBackStack(null);
                    ft.commit();
                }
                is1 = !is1;
            }
        });

        pop.setOnClickListener(new View.OnClickListener() {
            @Override
            public void onClick(View v) {
                Log.d("出栈","poppoppoppop");
                fm = getSupportFragmentManager();
                fm.popBackStack();
            }
        });
    }
}

activity布局文件

<?xml version="1.0" encoding="utf-8"?>
<RelativeLayout xmlns:android="http://schemas.android.com/apk/res/android"
    xmlns:app="http://schemas.android.com/apk/res-auto"
    xmlns:tools="http://schemas.android.com/tools"
    android:layout_width="match_parent"
    android:layout_height="match_parent"
    android:id="@+id/rl"
    tools:context=".FragmentShowActivity">
    <FrameLayout
        android:layout_width="match_parent"
        android:layout_height="match_parent"
        android:id="@+id/fragment"/>
    <Button
        android:layout_width="wrap_content"
        android:layout_height="wrap_content"
        android:text="jump"
        android:layout_alignParentBottom="true"
        android:layout_alignParentLeft="true"
        android:layout_marginLeft="80dp"
        android:layout_marginBottom="50dp"
        android:id="@+id/jump"/>

    <Button
        android:layout_width="wrap_content"
        android:layout_height="wrap_content"
        android:text="pop"
        android:layout_marginRight="80dp"
        android:layout_alignParentBottom="true"
        android:layout_alignParentRight="true"
        android:layout_marginBottom="50dp"
        android:id="@+id/pop"/>
</RelativeLayout>

 Fragment部分的代码与上一篇文章一致,主要就是打印了各个生命周期方法。

启动app,连续执行两次入栈操作,依次将Fragment show1、show2 入栈。生命周期打印如下

可以看出,Fragment show1并没有被完全销毁,生命周期终止在了onDestroyView。

再执行一次出栈操作,打印如下:

 由于栈中不再有Fragment show2,所以show2被完全销毁,而show1仍然存在,所以show1生命周期是从onCreateView开始执行。

我们可以推测,再次执行出栈操作,show1将被完全销毁,生命周期执行顺序为onPause,onStop,onDestroyView,onDestroy,onDetach,看看是不是这样。

点击出栈,查看日志

与预测结果一致,到此,算是摸到了Fragment的皮毛,生活还在继续,大家继续努力。

冲鸭~!

 

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值