最近一段时间在写自己的一个小项目(叫Piebald)时遇到一个恼人的问题:就是横竖屏切换时,Fragment的生命周期函数会重复调用。我这个项目的思路是这样的,在一个Activity布局中有一个Fragment,Fragment的布局是一个RecyclerView, 我想要实现的是:竖屏时RecyclerView,只有一列(即跟ListView 一样),横屏时RecyclerView成两列。实现过程中我发现横竖屏切换时Fragment的onCreate(), onCreateView()等函数会调用两次,这导致初始化进行的一些数据加载操作出现了严重的Bug, 让我特别头大,所以决定探索这其中是怎么回事,所以做了以下测试。
我们知道在Activity布局中放置Fragment有两种方式:1、直接在Activity布局中将Fragment完整类名加入,如<package.MyFragment />;2、在Activity布局中放一个没有子View的FrameLayout,然后在Activity的Java代码中通过getFragmentManager().beginTransaction().replace(R.id.container, MyFragment.class); 动态放入Fragment。这两种情况的测试结果如下:
1、第一种方法
新建了一个MyFragment, 在其生命周期函数中都用Log打印一些信息, 然后在acitivity_main.xml中直接放在根布局下。MainActivity代码中,onCreate()中只有如下语句
super.onCreate(savedInstanceState); Log.d("LifeCycle", "Activity:onCreate()"); setContentView(R.layout.activity_main);
然后在Activity的其他生命周期函数也用Log打印信息,然后运行程序。Log打印信息如下:
接下来旋转模拟器的屏幕,Log信息如下:
可以看到Activity和Fragment都是先销毁然后再重新初始化,Fragment的初始化方法(红色框里)在这里也调用了一次。
2、第二种方法
acitivity_main.xml代码如下
<LinearLayout android:layout_width="match_parent" android:layout_height="match_parent" android:orientation="vertical" xmlns:android="http://schemas.android.com/apk/res/android" > <FrameLayout android:id="@+id/container" android:layout_width="match_parent" android:layout_height="match_parent" /> </LinearLayout>在onCreate()中加入如下语句
fragment = new MainActivityFragment(); getSupportFragmentManager().beginTransaction() .replace(R.id.container, fragment) .commit();运行程序,打印信息如下
跟第一种方法没区别
接着旋转屏幕,Log打印信息如下
可以看到Fragment先创建,再销毁,又再创建,框里的方法调用了两次,红色框是第一次调用,蓝色框是第二次调用。
而我的项目正好就是用的第二种方法,于是搜索阅读了很多文章,大致的解决的方法就是在AndroidManifest.xml中相应activity中添加如下属性
android:configChanges="orientation|screenSize"
这样当进行横竖屏切换时,onConfigurationChanged()方法就会被调用, 同时Activity和Fragment也不会再初始化一遍了,我们来测试一下。
在Activity和Fragment都复写了onConfigurationChanged(),只执行了Log打印,然后运行程序,此时打印信息跟上面是完全一样的,接着再来切换为横屏,打印信息如下
可以发现只有Fragment和Activity的onConfigurationChanged()调用了。这样就可以解决使用第二种方法时Fragment初始化方法调用两次的问题了。
不过呢,这在我的项目里问题还是没有解决,上面的解决方法让Fragment在横竖屏切换时一次也不初始化了,但我要实现的是切换成横屏时将Fragment中的RecyclerView切换为两列,这就以为着横竖屏切换时Fragment一定要初始化一次。我的解决方法是在onConfigurationChanged()方法中执行getFragmentManager().beginTransaction().replace()这一系列方法, 这样一来,每次横竖屏切换时Fragment都只会初始化一次了,然后在Fragment的onCreateView()中加入对当前的横竖屏情况进行判断,决定RecyclerView是一列还是两列。至此,我的问题就彻底解决了。