viewPager如何在方向改变时保留片段状态? [英] How does viewPager retain fragment states on orientation change?

查看:269
本文介绍了viewPager如何在方向改变时保留片段状态?的处理方法,对大家解决问题具有一定的参考价值,需要的朋友们下面随着小编来一起学习吧!

问题描述

此问题与以下内容相似:

不会重新加载ViewPager中的Fragment改变方向

但是区别是,当我观察到许多奇怪的事情发生时,我想知道碎片状态实际上是如何保留的.

我的观察结果如下:

使用FragmentPagerAdapter设置ViewPager:

  class HomeAdapter extends FragmentPagerAdapter
{
    HomeAdapter(FragmentManager manager)
    {
        super(manager);
    }

    @Override
    public Fragment getItem(int i) {
        try {
            return (Fragment) mTabs.get(i).getFragmentClass().newInstance();
        } catch (InstantiationException e) {
            e.printStackTrace();
        } catch (IllegalAccessException e) {
            e.printStackTrace();
        }
        return null;
    }

    @Override
    public CharSequence getPageTitle(int position) {
        return mTabs.get(position).getTitle();
    }

    @Override
    public int getCount() {
        return mTabs.size();
    }
}

在活动的onCreate中,我呼叫:

setContentView(R.layout.activity_home);
    mTabs = new ArrayList<>();
    mTabs.add(new SlidingTabItem("A", FragmentA.class));
    mTabs.add(new SlidingTabItem("B", FragmentB.class));

    mViewPager = (ViewPager) findViewById(R.id.viewpager);
    mViewPager.setAdapter(new HomeAdapter(getSupportFragmentManager()));

    mSlidingTabLayout = (SlidingTabLayout) findViewById(R.id.sliding_tabs);
    mSlidingTabLayout.setViewPager(mViewPager);

现在,当设备方向改变时,viewPager会将所有内容重新创建为片段内editText字段中的更改文本.

要弄清楚这实际上是我想要的行为,但是我不理解.

我检查了viewPager和FragmentPagerAdapter的来源.我发现FragmentPagerAdapter搜索FragmentManager(如果该位置上的Fragment已经存在).如果这样做,它将调用FragmentTransaction.attach()重新附加它.

它也使用FragmentManager.detach()断开碎片的连接

据我了解,这会将FragmentState保存在FragmentManager中.但是,实际上在哪里以及何时进行恢复.

我尝试挂接到片段中包含一个saveedInstanceState的每个生命周期方法中,并调用null为null的super.X作为saveInstanceState,但是视图仍将以相同的方式还原.

还原的片段似乎也忽略了在onCreateView中完成的视图更改.我尝试了以下方法:

mMajor = (EditText) contentView.findViewById(R.id.major);
mMajor.setText("123");

在片段第一次创建时起作用.但是,尽管实际上实际上第二次调用了onCreateView,但由于主要的editText不会重置为"123",因此该指令显然不起作用.

有趣的是,调用mMajor.getText().toString()会返回以下内容:

[ 01-31 18:29:54.634 18540:29583 I/ActivityManager ]

我也不明白.这是因为此时视图尚未完全创建吗?

所以我的主要问题仍然存在.片段实际上什么时候重新创建其实例?为什么它不关心结果,为什么还要调用生命周期方法,并且有什么方法可以在不首先创建片段的情况下设置实例变量的情况下检测到这一点?

解决方案

ViewPager分离了Fragment时,它们将经历关闭生命周期(onPause()onSaveInstanceState()onDestroyView()onDetach()等)

但是,ViewPager(更具体地说是FragmentStatePagerAdapter)将经历并手动将片段的状态保存为它自己的保存状态(分离片段时调用FragmentManager.saveFragmentInstanceState(fragment)).这就是为什么您对调用super.*(null)的方法的重载不会影响它的原因.

覆盖视图更改的原因是因为onCreateView()出现在onViewStateRestored()之前,此时它将恢复保存的状态并覆盖您的更改.无论如何,您真的不应该在该方法中设置视图状态-只需返回视图即可.

如果您确实想知道这是否是第一次创建片段,只需检查if (savedInstanceState == null)就足够了.如果是这样,这是第一次启动.否则,它将根据保存状态重新创建自己.

This question has some similarities to:

Fragment in ViewPager on Fragment doesn't reload on orientation change

However the difference is that I want to know how the fragment states are actually retained as I observe a lot of weird things happening.

My observations are the following:

Setting up a ViewPager with a FragmentPagerAdapter:

  class HomeAdapter extends FragmentPagerAdapter
{
    HomeAdapter(FragmentManager manager)
    {
        super(manager);
    }

    @Override
    public Fragment getItem(int i) {
        try {
            return (Fragment) mTabs.get(i).getFragmentClass().newInstance();
        } catch (InstantiationException e) {
            e.printStackTrace();
        } catch (IllegalAccessException e) {
            e.printStackTrace();
        }
        return null;
    }

    @Override
    public CharSequence getPageTitle(int position) {
        return mTabs.get(position).getTitle();
    }

    @Override
    public int getCount() {
        return mTabs.size();
    }
}

In my activity's onCreate I call:

setContentView(R.layout.activity_home);
    mTabs = new ArrayList<>();
    mTabs.add(new SlidingTabItem("A", FragmentA.class));
    mTabs.add(new SlidingTabItem("B", FragmentB.class));

    mViewPager = (ViewPager) findViewById(R.id.viewpager);
    mViewPager.setAdapter(new HomeAdapter(getSupportFragmentManager()));

    mSlidingTabLayout = (SlidingTabLayout) findViewById(R.id.sliding_tabs);
    mSlidingTabLayout.setViewPager(mViewPager);

Now when the device orientation changes the viewPager recreates everything down to the changed text in editText fields inside the fragments.

To clarify this is actually the behavior I WANT however I don't understand it.

I checked the source of viewPager and FragmentPagerAdapter. I found out that FragmentPagerAdapter searches the FragmentManager if a Fragment at that position already exists. If it does it calls FragmentTransaction.attach() to reattach it.

It also deattaches the Fragments using FragmentManager.detach()

As far as I understand it this preserves the FragmentState in the FragmentManager. However WHERE and WHEN does this actually get restored.

I tried hooking into each and every lifecycle method in my fragment that contains a savedInstanceState and calling super.X with null as savedInstanceState however the view would still be restored the same way.

The restored fragment also seems to ignore changes to the view done in onCreateView. I tried the following:

mMajor = (EditText) contentView.findViewById(R.id.major);
mMajor.setText("123");

which works the first time the fragment is created. However even though onCreateView is actually called a second time this directive apparently does nothing as the major editText does not reset to "123".

Interestingly calling mMajor.getText().toString() returns something like:

[ 01-31 18:29:54.634 18540:29583 I/ActivityManager ]

which I also don't understand. Is this because the view is not fully created at this point?

So my main question remains. When does the fragment actually recreate its instance and why does it call lifecycle methods even though it does not care about the result and is there any way to detect this without setting an instance variable on first fragment creation?

解决方案

When the Fragments are detached by the ViewPager, they'll go through the shutdown lifecycle (onPause(), onSaveInstanceState(), onStop(), onDestroyView(), onDetach(), etc.)

However, ViewPager (more specifically the FragmentStatePagerAdapter) will go through and manually save the fragment's state into its own saved state (calls FragmentManager.saveFragmentInstanceState(fragment) when detaching the fragments). That's likely why your overriding of the methods calling super.*(null) aren't affecting it.

The reason changes to your view are being overridden is because onCreateView() occurs before onViewStateRestored() at which point it will restore the saved state and overwrite your changes. You really shouldn't be setting up your view state in that method anyway -- just return the view.

If you do want to know if it's the first time a fragment is being created, it's sufficient to check if (savedInstanceState == null). If so, it's the first launch. Otherwise, it's recreating itself from saved state.

这篇关于viewPager如何在方向改变时保留片段状态?的文章就介绍到这了,希望我们推荐的答案对大家有所帮助,也希望大家多多支持IT屋!

查看全文
登录 关闭
扫码关注1秒登录
发送“验证码”获取 | 15天全站免登陆