ViewPager中的Fragment在android 禁止屏幕旋转转后怎么恢复

在 SegmentFault,解决技术问题
每个月,我们帮助 1000 万的开发者解决各种各样的技术问题。并助力他们在技术能力、职业生涯、影响力上获得提升。
一线的工程师、著名开源项目的作者们,都在这里:
获取验证码
已有账号?
问题对人有帮助,内容完整,我也想知道答案
问题没有实际价值,缺少关键内容,没有改进余地
正常情况下,Fragment会随着活动的销毁而销毁,随着活动的重建而重建。所以为了避免重复创建Fragment,需要对Fragment进行检查是否为null。那现在放入ViewPager的Fragment是不是也是遵循这个规则的?是否有必要要创建Fragment前进行检查?要检查的话应该怎么获取这些fragment?因为以我现在的知识找不到可以设置id或者tag的地方1.在xml中直接对fragment设置id2.在add,repalce中设置tag,但是现在这两种方法都用不了了。。。
答案对人有帮助,有参考价值
答案没帮助,是错误的答案,答非所问
你想获取嵌在ViewPager中的Fragment的目的是什么?更新Fragment吗?如果是这个目的的话,需要实现ViewPager的FragmentPagerAdapter,然后override方法getItemPosition(Object object),把入口参数object强制转型为你定义的Fragment类;所以你还需要为自定义的Fragment实现一个public方法,比如public void update()用来更新界面。
你需要调用这个方法PagerAdapter.notifyDataSetChanged(), getItemPosition才会被调用到。不建议通过记录tag或者id的方式来管理ViewPager内的Fragment。
之前对问题的理解错了,你的问题应该从生命周期和ViewPager的adapter来理解。
Fragment具有和Activity相似的生命周期,并且其生命周期方法由托管它的Activity调用。当把一些Fragment放入ViewPager时,就需要adapter的支持,以管理这些Fragment。对于常用的两种adapter,FragmentStatePagerAdapter会销毁掉不需要的fragment,而FragmentPagerAdapter只是销毁了fragment的视图。
这些都是交由Adapter来管理的,你只需要覆写adapter相应的方法以创建Fragment并提供给adapter。不需要做检查,检查的工作是由adapter来完成的。比如,
最开始处于第0页时,adapter不仅为第0页创建Fragment实例,还为相邻的第1页创建了Fragment实例,此时覆写的Fragment getItem(int position);就会被调用2次;第1次从第0页滑到第1页,adapter同样会为相邻的第2页创建Fragment实例;Fragment getItem(int position);又被调用1次;此时,从第2页返回第0页,对于FragmentPagerAdapter,第0页仅视图被销毁了,所以它的onCreateView()会被调用到。对于FragmentStatePagerAdapter,第0页的实例被销毁了,所以getItem(0)会被再次调用,整个Fragment对应的生命周期函数会再走一遍。
所以结论是,ViewPager中的Fragment交由Adapter管理,你不需要担心是否重建。你只需要根据应用场景选择合适的adapter。
更详细的说明,你可以参考我之前整理的一篇文章中的一部分
下面是相关的生命周期函数调用情况:
当刚启动一个Activity时,Activity和Fragment都将被创建:
10-14 00:13:36.135: D/FragmentActivity(23141): onCreate()
10-14 00:13:36.371: D/FragmentActivity(23141): onStart()
10-14 00:13:36.371: D/FragmentActivity(23141): onResume()
10-14 00:13:36.449: D/FragmentActivity(23141): getItem(0)
10-14 00:13:36.452: D/Fragment0(23141): newInstance()
10-14 00:13:36.452: D/FragmentActivity(23141): getItem(1)
10-14 00:13:36.453: D/Fragment1(23141): newInstance(Page1)
10-14 00:13:36.453: D/Fragment0(23141): onAttach()
10-14 00:13:36.453: D/Fragment0(23141): onCreate()
10-14 00:13:36.453: D/Fragment0(23141): onCreateView()
10-14 00:13:36.457: D/Fragment0(23141): onViewStateRestored
10-14 00:13:36.458: D/Fragment0(23141): onStart()
10-14 00:13:36.458: D/Fragment0(23141): onResume()
10-14 00:13:36.458: D/Fragment1(23141): onAttach()
10-14 00:13:36.458: D/Fragment1(23141): onCreate()
10-14 00:13:36.458: D/Fragment1(23141): onCreateView()
10-14 00:13:36.465: D/Fragment1(23141): onStart()
10-14 00:13:36.465: D/Fragment1(23141): onResume()
当Activity进入后台后:
10-14 00:14:13.273: D/Fragment0(23141): onPause()
10-14 00:14:13.273: D/Fragment1(23141): onPause()
10-14 00:14:13.273: D/FragmentActivity(23141): onPause()
10-14 00:14:14.190: D/Fragment0(23141): onSaveInstanceState()
10-14 00:14:14.191: D/Fragment1(23141): onSaveInstanceState()
10-14 00:14:14.191: D/Fragment0(23141): onStop()
10-14 00:14:14.191: D/Fragment1(23141): onStop()
10-14 00:14:14.191: D/FragmentActivity(23141): onStop()
当Activity返回前台后:
10-14 00:14:37.410: D/Fragment0(23141): onStart()
10-14 00:14:37.410: D/Fragment1(23141): onStart()
10-14 00:14:37.411: D/FragmentActivity(23141): onStart()
10-14 00:14:37.411: D/FragmentActivity(23141): onResume()
10-14 00:14:37.431: D/Fragment0(23141): onResume()
10-14 00:14:37.432: D/Fragment1(23141): onResume()
当Activity被销毁后:
10-14 00:14:58.725: D/Fragment0(23141): onPause()
10-14 00:14:58.726: D/Fragment1(23141): onPause()
10-14 00:14:58.726: D/FragmentActivity(23141): onPause()
10-14 00:14:59.412: D/Fragment0(23141): onStop()
10-14 00:14:59.412: D/Fragment1(23141): onStop()
10-14 00:14:59.412: D/FragmentActivity(23141): onStop()
10-14 00:14:59.413: D/Fragment0(23141): onDestroyView()
10-14 00:14:59.413: D/Fragment0(23141): onDestroy()
10-14 00:14:59.413: D/Fragment0(23141): onDetach()
10-14 00:14:59.414: D/Fragment1(23141): onDestroyView()
10-14 00:14:59.414: D/Fragment1(23141): onDestroy()
10-14 00:14:59.414: D/Fragment1(23141): onDetach()
10-14 00:14:59.414: D/FragmentActivity(23141): onDestroy()
分享到微博?
关闭理由:
删除理由:
忽略理由:
推广(招聘、广告、SEO 等)方面的内容
与已有问题重复(请编辑该提问指向已有相同问题)
答非所问,不符合答题要求
宜作评论而非答案
带有人身攻击、辱骂、仇恨等违反条款的内容
无法获得确切结果的问题
非开发直接相关的问题
非技术提问的讨论型问题
其他原因(请补充说明)
我要该,理由是:下次自动登录
现在的位置:
& 综合 & 正文
Fragment防止自动清理 (ViewPager滑动时,滑出屏幕后被清理)
这个问题网上搜一搜发现帖子很多,但是博主试了几种好像没有说的那么好用
一.比如给ViewPager设置长度,以增加缓存的Fragment数目。(不靠谱)
二.滑出屏幕的时候存储加载的数据,createView的时候重新读取(需要读写数据,不推荐)
下面介绍我自己用到的方法,你根本想不到原来这么简单。
你先要明白Fragment的生命周期如下
此图能清楚的看出Fragment被后台运行后重新创建的过程
onDestoryView():Fragment被FragmentManagerfag放在后台运行,注意,这里只是在后台运行了,并没有真正的销毁,也就是说,这个片段的进程还是存在的,只是放在了后台运行。
onCreateView():再次显示的时候和第一次创建的时候调用,
所以说,只要你的Fragment没有被真正的销毁,你的Fragment的当中的数据是存在的,那么为什么会出现Fragment重复加载的现象呢,这里大多数人犯了一个错误,把控件和数据的初始化放在了onCreateView当中,包括我,之前一直是这么做的,这就导致了你fragment再次唤醒的时候重新加载了界面和数据。
解决方案:
onCreate()是在onCreateView()之前调用的,那么你在onCreate中初始化好在onCreateView中要返回的view和数据,onCreateView只负责返回一个界面视图View就好了。赶紧去试试吧,我今天刚发现的(如下代码示例)。
补充说明:
错误示范(这种不会导致程序异常,但是会在重新加载Fragment时重新绘制界面):
public class FragmentTest extends Fragment {
private View mV
public View onCreateView(LayoutInflater inflater,
@Nullable ViewGroup container, @Nullable Bundle savedInstanceState) {
mView = inflater.inflate(R.layout.activity_main, container, false);
推荐示例(不会导致Fragment重新加载,但是需要在销毁View的时候remove掉父控件):
public class FragmentTest extends Fragment {
private View mV
private TextView mTextV
public void onCreate(Bundle savedInstanceState) {
// TODO Auto-generated method stub
super.onCreate(savedInstanceState);
//初始化视图和数据
mView = getActivity().getLayoutInflater().inflate(R.layout.activity_main, null);
mTextView=(TextView)mView.findViewById(R.id.textview1);
mTextView.setText("HelloWorld");
public View onCreateView(LayoutInflater inflater,
@Nullable ViewGroup container, @Nullable Bundle savedInstanceState) {
return mV//这里是返回的视图
public void onDestroyView() {
// TODO Auto-generated method stub
super.onDestroyView();
//在销毁视图的时候把父控件remove一下,不然重新加载的时候会异常导致奔溃,提示should remove parent view
ViewGroup mGroup=(ViewGroup) mView.getParent();
if(mGroup!=null){
mGroup.removeAllViewsInLayout();
&&&&推荐文章:
【上篇】【下篇】14:29 提问
viewpager中的fragment之间跳转传值问题?
有一个是Activity,在里面有viewpager的实现,ViewPager显示的是10个子fragment,ViewPager中使用的是FragmentStatePagerAdapter适配器。现在已经可以左右滑动切换1-10个Fragment了,我现在想实现的功能是:每个Fragment中都有几个button,点击第一个Fragment中的button,相当于右滑了一下,跳到了下一个Fragment。能跳过去了,但是从第一个fragment跳过去的时候第二个fragment为null?是怎么回事?
按赞数排序
默认不是所有fragment都会创建,当你滑动到某个索引时,先判断是否为null,是就new一个fragment
一般会把创建的fragment放到list中,通过索引访问list指定位置
----------------------biu~biu~biu~~~在下问答机器人小D,这是我依靠自己的聪明才智给出的答案,如果不正确,你来咬我啊!
可以用标志判断去做跳转传值
楼主能否把button的实现贴出来?
其他相关推荐3被浏览436分享邀请回答List&Fragment& listFragment = getSupportFragmentManager().getFragments();
这样能得到Fragment
0添加评论分享收藏感谢收起Fragment在ViewPager中的生命周期
1.一直以来的疑问
Fragment在ViewPager到底经历了哪些生命周期方法?到底发生了什么?
常会TabLayout和ViewPager配合起来使用,针对这套组合,就想也做一些学习了解。在一个ViewPager中经常会存在多个Fragment,Fragemnt在ViewPager中的生命周期一直没有闹明白。这周正好在测试Api的时候又用到了TabLayout和ViewPager组合。ViewPager中的Fragment并想做到延迟加载,在可见的时候再进行网络请求。在敲代码的时候想到到几个问题:
在ViewPager中,滑动时,Fragment会经历哪些生命周期? ViewPager的setOffscreenPageLimit()方法对Fragment有哪些影响? 在ViewPager中,Fragment的setUserVisibleHint()对Fragment的生命周期有哪些影响? 点击TabLayout的Tab时,Fragment经历的生命周期和滑动ViewPager有啥不一样?
现在有个明确的需要:在TabLayout和ViewPager这个组合下,实现Fragment的延迟加载。
2.Tablayout、ViewPager组合
代码很简单,就是新建一个 Stuido工程,一个Activity里面有一个TabLayout和ViewPager,ViewPager中四个Fragment。
2.1布局文件
Acivity的布局文件:
主要是CoordinatorLayout、AppBarLayout和Toolabr的使用。如果基础的用法不知道的话可以看看,我写的很基础的用法。啊哈哈 :)
Fragment的布局:
&framelayout android:layout_height="match_parent" android:layout_width="match_parent" xmlns:android="/apk/res/android"&
&/framelayout&
Fragmentde的布局更加简单,主要就一个TextView。4个Fragment布局一样,就贴出一个。
2.2Actiity代码
public class MainActivity extends AppCompatActivity {
private TabLayout tabL
private ViewPager viewP
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_main);
Toolbar toolbar = (Toolbar) findViewById(R.id.toolbar);
setSupportActionBar(toolbar);
initView();
private void initView() {
tabLayout = (TabLayout) findViewById(R.id.tab_main_activity);
viewPager = (ViewPager) findViewById(R.id.vp_main_activity);
ViewPagerAdapter adapter = new ViewPagerAdapter(getSupportFragmentManager());
viewPager.setAdapter(adapter);
data = new ArrayList&&();
data.add(new Fragment_1());
data.add(new Fragment_2());
data.add(new Fragment_3());
data.add(new Fragment_4());
adapter.setFragmentList(data);
tabLayout.setupWithViewPager(viewPager);
for (int i = 0 ; i & adapter.getCount() ; i ++){
tabLayout.getTabAt(i).setText(&Tab_&+(i+1));
主要就是initView()这个方法。主要就是把4个Fragment加入到ViewPager中。
2.3Fragment代码
Fragemnt的生命周期方法共有11个,为了下面能够更加清晰记住这些生命周期,我给这些生命周期方法从1到11定了编号。如下:
1 onAttach()
--& 2 onCreate()
--& 3 onCreateView()
--& 4 onCreateActivity()
--& 5 onStart()
--& 6 onResume()
--& 7 onPause()
--& 8 onStop()
--& 9 onDestroyView()
--& 10 onDestroy()
--& 11 onDetach()
Fragment原本打算偷懒,只想写一套,不想写4个。但写一个的话onAttach()不好做区分展示,就又老老实实写了四个。
public class Fragment_1 extends Fragment {
private final String TAG = &Fragment_1&;
public void onAttach(Context context) {
super.onAttach(context);
1__onAttach&);
public void onCreate(@Nullable Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
2__onCreate&);
public View onCreateView(LayoutInflater inflater, @Nullable ViewGroup container, @Nullable Bundle savedInstanceState) {
3_onCreateView&);
return inflater.inflate(R.layout.fragment_layout_1,container,false);
public void onActivityCreated(@Nullable Bundle savedInstanceState) {
super.onActivityCreated(savedInstanceState);
4__onActivityCreated&);
public void onStart() {
super.onStart();
5__onStart&);
public void onResume() {
super.onResume();
6__onResume&);
public void onPause() {
super.onPause();
7__onPause&);
public void onStop() {
super.onStop();
8__onStop&);
public void onDestroyView() {
super.onDestroyView();
9__onDestroyView&);
public void onDestroy() {
super.onDestroy();
10__onDestroy&);
public void onDetach() {
super.onDetach();
11__onDetach&);
private void log (String methodName){
Log.e(TAG,&--------&&+methodName);
代码全部贴完。都很简单。前面一个说了4个问题,到了思考第2,3个问题时,代码需要小改动。
3.开始分析问题 1
在ViewPager中,滑动时,Fragment会经历哪些生命周期?
开始前,这里没有图,就简单交代一下,每个Fragment有一个对应的数字。TAB-1 对应Fragment-1,以此类推,4个TAB对应于4个Fragment。默认显示Fragment1。
3.1 滑动ViewPager,切换Fragment
这部分所有的切换Fragment都是通过滑动ViewPager。不是点击Tab。在Activity中,ViewPager此时啥都没有设置。
3.1.1 加载Fragment_1
当Activity中ViewPager加载完成时,手机屏幕显示默认的Fragment1。此时,打印的Log信息:
此时打印的Log信息与预想的有很大不同。知道ViewPager会有预加载的特性,我以为Fragment_1会从 1 onAttach() 开始直到 6 onResume()后,Fragment_2才会开始从 1onAttach()开始,到了3 onCreateView()会停止。然而,并不是预想的这样。
首先,Fragment_1 经历 1_onAttach() 和 2_onCreate() 后, Fragment_2也开始走了 1_onAttach()和 2_onCreate()方法。vcD4NCjxwPr3T18WjrEZyYWdtZW50XzHSwLTOvq3A+iAzX29uQ3JlYXRlVmlldygpLCA0X29uQ3JlYXRlQWN0aXZpdHkoKSw1IF9vblN0YXJ0ICw2X29uUmVzdW1lKCmho7TLyrGjrEZyYWdtZW50XzG78bXDvbm146Os0tG+rdW5yr7U2srWu/rGwcS7oaNGcmFnbWVudF8y0rK909fFtNMgM19vbkNyZWF0ZSgpv6rKvNaxtb3Ssta00NC1vSA2X29uUmVzdW1lKCm3vbeooaPSyc7Kvs3U2tXio6zO0rj2yMu40L71vMjIu0ZyYWdtZW50XzLDu9PQ1NrGwcS7z9TKvqOsvs2yu7vh1rTQ0LW9IDZfb25SZXN1bWUoKbe9t6gsyLu2+ExvZ9DFz6LItM/Uyr7WtNDQtb3ByyA2X29uUmVzdW1lKCmho9XiwO+8x8K80rvPwqGjPC9wPg0KPGhyIC8+DQo8aDMgaWQ9"312-由fragment1向右滑动到fragment2">3.1.2 由Fragment_1向右滑动到Fragment2_
滑动一下屏幕,ViewPager中由Fragment_1切换到Fragment_2,,屏幕显示Fragment_2时,下面是Log信息:
哎,我擦,咋就只有Fragment_3的信息,Fragment_1和Fragment_2的呢?这Fragment_2没有Log信息可以理解,可Fragment_1应该会有啊。这里又和预想的大不一样。预想中Fragment_1会走 7__onPause(),8__onStop(),9__DestroyView()和10__ononDestroy()。而实际却没有走,开始还以为搞错了,又反复测试,发现就是只有Fragment_3的Log信息。
根据Log信息,可以看出,当滑到Fragment_2后,Fragment_3经历了从 1__onCreate()方法到6__onResume()方法。Fragment_1却是没有走任何生命周期方法。这里考虑了一下,感觉也蛮合理。ViewPager展示的时候,用户在实际使用的时候,经常会从一个Fragment滑到另一个Fragment后又切换回来。这时如果按照我预想的,Fragment_1中的View已经被销毁,再次切换回来又需要重新绘制,这样频繁请求内存空间也不好。
这时,Fragment_3已经时刻准备好,如果在onCreateView()方法中有网络请求的话,网络请求在滑动到Fragment_2后就会被调用。就等着滑动ViewPager后,来展示内容。
这里先预留问题滑到Fragment_2后,Fragment_1没有走任何生命周期方法,Fragment_1的生命周期方法会在啥时候走?
当Fragment_2在屏幕时,这时候就有了两个方向可以选择,左滑到Fragment_1 或者 右滑到 Fragment_3。同理,当屏幕显示Fragment_3的时候,也是有两个方向可选择。
3.1.3 由Fragment_2向右滑到Fragment_3
前面已经提到,当由Fragment_1滑到Frament_2后,Fragment_3的生命周期方法已经从 1_onCreate()走到了 6_onResume()。当滑到Fragment_3后,看下此时的Log信息。
这次,Fragment_4先走了 1_onAttach(),2_onCreate()后,Fragment_1走 7_onPause,8_onStop,9_onDestroyView()。Fragment_1的生命周期终于开始走。而此时,Frment_4也已经完成了预加载。这里也可以解答3.1.2最后预留的那个问题。
看到了这次的Log信息,终于感觉看出了点ViewPager中嵌套Fragment后一些特点。
当ViewPager中的Fragment大于等于3个的时候,除去展示开头和结尾两个Fragment的情况,ViewPager会保留一个Fragment左右两侧以及自身3个Fragment的信息。例如,当滑到Fragment_2的时候,Fragment_3也已经走到了6_onResume()这个生命周期方法,而此时,Fragemnt_1没有走任何的生命周期方法,还在ViewPager中保留着。此外还有,当加载本身或者预加载下一个Frgment时,只是先走1_onAttach()和2_onCreate()两个生命周期方法后,才会根据当前情况确定继续走相应的Fragment的生命周期方法。
3.1.4 由Fragment_2向左滑到Fragment_1
当由Frament_1滑到Fragment_2时,并没有走Fragment_1和Fragment_2的生命周期,而是走了Fragment从1_onCreate()到6_onResume()。接下来,屏幕向左滑,由Fragment_2滑到Fragment_1。Log信息:
由Fragment_1滑到Fragment_2只是走了Fragment_3的生命周期方法,而由Fragment_2滑到Fragment_1时,也是只走了Fragment的生命周期方法。在3.1.2中,当滑到Fragment_2后,Fragment_3已经走到了6_onResume()方法。再滑到Fragment_1后,Fragment_3走了7_onPause(),8_onStop(),9_onDestoryView()。到了这里发现,在ViewPager中,相邻的3个Fragment之间来回切换,都没有走10_onDestroy()和11_onDetach()。
到了这里,四个Fragment滑动的情况再分析查看下面的3种情况。其实,下面的这些情况和前面的情况重复了,本质是一样的。
由Fragment_3向右滑到Fragment_4 由Fragment_3向左滑到Fragment_2 由Fragment_4向左滑到Fragment_3
3.1.5 由Fragment_3向右滑到Fragment_4
滑动前,当屏幕显示Fragment_3时,此时,由3.1.3知道,Fragment_1走到了9_onDestroyiew()。Fragment_4走到了6_onResume()。
滑到Fragment_4后:
Fragment_4是ViewPager中最后一个Fragment所以也就没了下一个Fragment预加载。只是不相邻的Fragment_2走了7_pause(),8_onStop,9_onDestroyView()方法。此时,就可以结合3.1.3情况来看。实际的情况是一样的,只是区别在于Fragment_4已经是最后一个Fragment了。
3.1.6 由Fragment_3向左滑到Fragment_2
这里结合3.1.4,很容易就理解了。
滑动前,当屏幕显示Fragment_3时,此时,由3.1.3知道,Fragment_1走到了9_onDestroyiew()。Fragment_4走到了6_onResume()。
滑动到Fragment_2后:
根据3.1.3中的Log信息,Frgment_1在由Fragment_2滑到Fragment_3的时候,生命周期已经走到了9_onDestroyView(),并没有走到10_onDestroy()。当由Fragment_3滑到Fragment_2后,ViewPager再次预加载Fragment_1时,是从3_onCreateView()开始的,走到4_onCreateAcitivty()后,开始走Fragment_4的生命周期方法7_onPause(),8_onStop(),9_onDestroyView(),之后,Fragment_1的生命周期走4_onCreateActivity(),5_onStart(),6_onResume()。
3.1.7 由Fragment_4向左滑到Fragment_3
滑动前,当显示Fragment_4时,根据3.1.5的Log信息,Fragment_2走到了9_onDestroyView()生命周期方法。而Fragment_3和Fragment_4此时都处于6_onResume()这个生命周期方法。
滑动到Fragment_3后:
到了这里,就比较容易理解此时的Log信息。Fragment_2从3_onCreateView()走到了6_onResume()方法。到了此时,Fragment_2、Fragment_3和Fragment_4此时3个Fragment都处于6_onResume()这个生命周期方法。
到了这里,ViewPager中Fragment滑动的情况就差不多分析完了。其他都是些重复的情况了。根据3.1.1到3.1.7的Log来看,ViewPager中嵌套Fragment默认不设置其他方法时,若Fragment的数量大于等于3时,ViewPager会保留包括一个Fragment在内左右两侧3个Fragment的信息。然而这里也留下了个问题,Fragment的10_onDestroy和11_onDetach()什么时候会走?
3.1.8 点击回退键finish掉Acitivty时
根据3.1.7的信息,此时,Fragment_1处于9_onDestroyView()这个生命周期方法,Fragment_2、Fragment_3和Fragment_4此时3个Fragment都处于6_onResume()这个生命周期方法。
此时,点击back键结束当前的Acitivty,我这里整个测试的App就一个Activity,点击了back键就会退出应用。看下此时的Log信息:
根据Log信息,Fragment_2,3,4先走了7_onPause()后,走了8_onStop()。接着,便是Fragment_1走10_onDestroy(),11_onDetach()。之后便是Fragment_2,3,4依次走9_onDestroyView(),10_onDestroy(),11_onDetach()。
到了这里,3.1.7的问题也就有了答案。ViewPager中,Fragment的10_onDestroy()以及11_onDetach()会在ViewPager所在的Activity结束后被调用。
这里还有一点需要说的是,点击back键后,图(点击Back键时)中的Log信息并不是一次打印出来的。一开始我以为是我是看错了,又测试了几次,发现,Fragment_2,3,4走了7_onPause()这个方法后,确实会短暂停一下,非常快的又打印出下面的Log信息。
3.1.8 当屏幕显示Fragment_3时,点击电源键关闭屏幕
当屏幕正在展示某个Fragment时,点击了电源键或者手机自动息屏时,这种情况下,会和以上的几种情况有所不同,下面还是以Fragment_3为例来尝试分析一下。
根据3.1.7的信息,此时,Fragment_1处于9_onDestroyView()这个生命周期方法,Fragment_2、Fragment_3和Fragment_4此时3个Fragment都处于6_onResume()这个生命周期方法。
点击电源键之后:
Fragment_2,3,4依次走了7_onPause(),8_onStop()。接下来,再点亮屏幕,显示Fragment_3:
Fragment_2,3,4依次走了5_onStart(),6_onResume()。
到了此时,ViewPager中Fragment滑动的情况就结束了。这些情况,写一个很简单的测试demo就可以搞的比较清楚了。要一次就记得清楚也不算特别现实,多想几次就可以了。
4.尝试分析问题2
ViewPager的setOffscreenPageLimit()方法对Fragment有哪些影响?
直白翻译就是设置幕后页面限制 :) 。其实就是设置预加载Fragment的数量。
public void setOffscreenPageLimit(int limit) {
if (limit & DEFAULT_OFFSCREEN_PAGES) {
Log.w(TAG, &Requested offscreen page limit & + limit + & defaulting to & +
DEFAULT_OFFSCREEN_PAGES);
limit = DEFAULT_OFFSCREEN_PAGES;
if (limit != mOffscreenPageLimit) {
mOffscreenPageLimit =
populate();
通过ViewPager中可以看出,传入的参数如果小于DEFAULT_OFFSCREEN_PAGES(就是1)这个值是无效的。所以尝试使用setOffscreenPageLimit(0)来关闭ViewPager的预加载是无效。也就是说,limit只有大于等于2时才会有效。Ps:ViewPager调用这个方法后,里面populate()这个方法又做了啥,我目前想关心也关心不了,大概看了下,ViewPager共有3000多行代码,我目前的代码能力还很低,即使想做到只是阅读相关代码也困难,目前想读通比较困难,以后代码阅读能力提升再来看了。
接下来,改动代码,在ViewPager中,加入viewPager.setOffscreenPageLimit(2)这行代码,再次运行,看看Log信息。
根据问题1的分析,看到这个Log信息就很好理解了,多了Fragment_3的Log信息,而且走的方法和Frgment_1和2是一样的。后面的情况的Log信息便不再贴出来了,本质是一样的,只是多预加载了一个Fragment。但此时ViewPager依然只是保留3个Fragment的信息。当滑到Fragment_4的时候,Fragment_1走了7_onPause(),8_onStop(),9_onDestroyView()。Fragment_2,3,4则处于6_onResume()。
这个方法对Fragment生命周期方法的调用顺序上并没有什么影响,只是预加载的Fragment的数量又设置的limit参数决定。
6.尝试分析问题3
在ViewPager中,Fragment的setUserVisibleHint()对Fragment的生命周期有哪些影响?
再次顾名思义:设置用户可见暗示 。:) 这个方法可以用来判断Fragment是否可见。这里也不再贴源码了。
6.1 Fragment加入setUserVisibleHint()
修改代码前,我把在分析问题2时加入的viewPager.setOffscreenPageLimit(2)注释掉。这样Log信息会少一些。在每个Fragment中代码做了一些修改加入了一个setUserVisibleHint()。
public void setUserVisibleHint(boolean isVisibleToUser) {
super.setUserVisibleHint(isVisibleToUser);
log(&setUserVisibleHint&);
再次运行Demo,查看Log信息:
和预想的大不一样。setUserVisibleHint()竟然最先被调用。预想的是1_onAttach()执行后,才会走setUserVisibleHint()这个方法。而且,Fragment_1走了两次setUserVisibleHint()这个方法。其他的Log信息倒是比较熟悉了。
滑动屏幕,滑动到Fragment_2,看下此时的Log信息:
这时不仅先执行了Fragment_3的setUserVisibleHint(),连Fragment_1,2的setUserVisibleHint()方法也比Fragment_3的1_onAttach()。此时,Fragment_1,2,3都处于6_onResume()。再由Fragment_2滑到Fragment_1,看下Log:
此时,Fragment_1,2的setUserVisibleHint()方法优先被调用,Fragment_3走到9_onDestroyView()方法。
到了这里,感觉setUserVisibleHint()这个方法每次滑动都会被调用,而且最先被调用,ViewPager中保存信息的Fragment都会被调用。
接下来模拟一下延迟加载网络请求。
6.2 延迟加载,模拟网络请求
在每个Fragment中,加入一个Boolean值,并将代码做一些改动。
public void onCreate(@Nullable Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
isCreate =
2__onCreate&);
public void setUserVisibleHint(boolean isVisibleToUser) {
super.setUserVisibleHint(isVisibleToUser);
if (isCreate && isVisibleToUser){
log(&开始进行网络请求了&);
isCreate =
当Fragment可见时,进行网络请求。网络请求加上了前提条件。除了isVisibleToUser为true外还有一个isCreate。Fragment走了2_onCreate()iSCreate设为了true。我通常会用Fragment.newInstrance()这个方法进行初始化Fragment时来传值,在2_onCreate()方法中通过getArguments()来接收值,所以这里加了一个条件isCreate为true。
再次运行Demo,看Log信息:
我擦,我的&开始进行网络请求了&呢?最关键的Log信息却没有看到。根据6.1,每次肯定会调用setUserVisibleHint()这个方法的啊,可Fragment_1明明都已经可见了,怎么没有出现&开始进行网络请求了&?于是,看了下源码,&,白看,没瞧出啥。好吧,我再滑下屏幕,Fragment_2显示,看下Log:
滑到Fragment_2后,第一行倒是打印出来了&开始进行网络请求了&。继续滑,Fragment_3,4也都打印出来了。然后我又依次从Fragment_4滑到了Fragment_1后,Log信息也打出了&E/Fragment_1: &&&&开始进行网络请求了&。
可为啥Fragment_1第一次显示时,没有打印呢?
这里结合6.1想了一下,setUserVisibleHint()这个方法在1_onAttach()方法之前。Fragment_1第一次加载时,2_onCreate()方法还没走呢,此时isCreate的值还是fasle,就不会打印&开始进行网络请求了&。根据6.1的Log信息,Fragment_1两次调用setUserVisibleHint()时,isCreate都为false。而isCreate值变为true后,却不在执行setUserVisibleHint()方法了。
考虑到了原因了,把判断条件isCreate去掉也不是很合适。因为去掉了,此时也拿不到Fragment.newInstrance()传递过来的对象,往往网络请求的需要的参数需要这个传递过来的对象,会造成空指针。那怎么办?嗯,这是Fragment_1,第一个Fragment干嘛还要延迟加载啊。延迟加载的目的就是为了等到Fragment_2,3,4&这些后面的Fragment可见后再进行网络请求。Fragment_1没有太多必要加载。如果是考虑到用户从Fragment_4向左滑,滑到Fragment_2时,Fragment_1不会再次进行网络请求的话,可以考虑使用其他的方法。例如,设置下拉刷新,只有下拉刷新Fragment_1才再次进行网络请求。
于是,关于Fragment_1,我的处理是,不使用延迟加载。
7.尝试分析问题4
点击TabLayout的Tab时,Fragment经历的生命周期和滑动ViewPager有啥不一样?
问题1,2,3都是通过滑动来分析的,没有通过点击Tab。现在来看看点击Tab。
7.1 点击Tab时,Fragment所走的生命周期
开始前,把每个Fragment中的setUserVisibleHint()先注释掉,之后运行Demo。这里点击Tab_2是体现不出啥效果的,因为Fragment_2已经预加载了。我这里点击Tab_3,看Log信息:
到了这里,这个Log信息还是比较容易理解了。此时的Fragment_2,3,4处于6_onResume()这个方法。Fragment_1走到9_onDestroyView()。和滑动时其实没有本质的区别。就是点击可以从Fragment_1越过Fragment_2而直接到Fragment_3这种交互上的区别而造成的少了滑动时对Fragment_3的预加载。如果是从Fragment_1滑到Fragment_3,在滑到Fragment_2时,就已经完成了对Fragment_3的预加载。而通过点击的方式时,点击Tab_3后,Fragment_3是从1_onAttach()这个生命周期方法开始的。
7.2 点击Tab的方式和Fragment延迟加载遇到的问题
在6.2中遇到的一个问题就是,Fragment_1用了我写的那种延迟加载的方法时,第一次加载不会进行网络请求,只有再次滑到Fragment_1后才会进行网络请求。到了这里,同样也会引起这个问题。少了预加载,例如7.1中,直接点击Tab_3时,此时Fragment_3并没有预加载。setUesrVisibleHint()的方法又比1_onAttach()方法调用的早。会有和6.2中遇到的问题一样。第一次点击Tab_3后,并不会进行网络请求,只有再次滑动到Fragment_3后才会进行网络请求。
我的解决办法就是利用ViewPager.setoffscreenLimit(int limit);这个方法。根据情况,我将litmit设为了3。我这个方法比较粗暴,虽然有效但不好,而且局限性很大。一旦Tab可以编辑,并且数量不是固定的时候,这个方法就不好用了。而且预加载的数量多,肯定会需要更多的内存。这里,也希望有哪位大神有好的解决办法可以告诉我。
这个周末用了一天半,写好了这篇博客,用来记录下我的学习过程。写的内容也是比较浅显,水平太菜,如果能有能力阅读源码,对于问题2,3,4会有更深入的了解,会有好的办法,而不是这篇博客中折中的方法。如果有啥错误,请赶紧指出。:)
这篇博客刚刚发出去,就看到了另外一篇博客,专门讲的懒加载。看了下,发现我遗留的问题很容易就可以解决。
修改每个Fragment的代码如下:
public void onCreate(@Nullable Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
isCreate =
2__onCreate&);
public void setUserVisibleHint(boolean isVisibleToUser) {
super.setUserVisibleHint(isVisibleToUser);
private void load() {
if (isCreate && getUserVisibleHint() && !isHasLaodOnce){
log(&开始进行网络请求了&);
isCreate =
isHasLaodOnce =
public void onActivityCreated(@Nullable Bundle savedInstanceState) {
super.onActivityCreated(savedInstanceState);
4__onActivityCreated&);
load()方法有3个前提条件,isCreate,可见,没有进行网络请求过。
根据6.1,setUserVisibleHint()这个方法会在预加载Fragment时,会在Fragment的1_onAttach()前调用。此时,在setUserVisibleHint()里面调用也不用害怕空指针的问题,因为有3个前条件。当通过滑动ViewPger时,根据6.1知道,每次滑动都会调用setUserVisibleHint()这个方法,进行预加载后,当滑到Fragment_2,3,4时,就会调用里面的load()方法。
针对6.2和7.2的问题。解决办法就是在Fragment的4_onCreateActivity()中调用load。我个人习惯把网络请求放在这个生命周期。在Fragment_1和点击Tab时,引起问题的原因就是setUserVisibleHint()先于1_onAttach()调用,不能满足前提条件中的isCreate,所以load方法不会被调用。而4_onCreateActivity()中会再次调用load()方法,此时还满足3个前提条件。这样,遗留的问题也解决了。ViewPager中Fragment延迟加载这个需求也可以实现了。如果此时Fragment中有一个轮播图的话,也可以通过getUserVisibleHint()这个方法来选择关闭轮播图线程的时机。
如果你看到了这里,真爱啊。十分感谢。

我要回帖

更多关于 ipad升级后屏幕不旋转 的文章

 

随机推荐