【问题标题】:FragmentPagerAdapter doesn't recreate Fragments on orientation change?FragmentPagerAdapter 不会在方向更改时重新创建片段?
【发布时间】:2012-09-03 22:02:33
【问题描述】:

我有一个ViewPager(扩展FragmentPagerAdapter),其中包含两个Fragments。当我在它们之间滑动时,我需要为每个Fragment 刷新一个ListView。为此我实现了ViewPager.OnPageChangeListener 接口(即onPageScrollStateChanged)。为了保存对Fragments 的引用,我使用了HashTable。我将Fragments 的引用存储在HashTable 中的getItem() 方法中:

  @Override
        public Fragment getItem(int num) {
            if (num == 0) {
                Fragment itemsListFragment = new ItemsListFragment();
                mPageReferenceMap.put(num, itemsListFragment);
                return itemsListFragment;
            } else {
                Fragment favsListFragment = new ItemsFavsListFragment();
                mPageReferenceMap.put(num, favsListFragment);
                return favsListFragment;
            }
        }

因此,当我从一个 Fragment 滑动到另一个 onPageScrollStateChanged 触发器时,我使用 HashTable 在两个 Fragments 中调用所需的方法(刷新):

 public void refreshList() {
        ((ItemsListFragment) mPageReferenceMap.get(0)).refresh();
        ((ItemsFavsListFragment) mPageReferenceMap.get(1)).refresh();
    }

orientation change 事件发生之前一切正常。之后是refresh()方法中的代码,即:

public void refresh() {
        mAdapter.changeCursor(mDbHelper.getAll());
        getListView().setItemChecked(-1, true); // The last row from a exception trace finishes here (my class).
    }

结果为@​​987654342@:

java.lang.IllegalStateException: Content view not yet created
        at android.support.v4.app.ListFragment.ensureList(ListFragment.java:328)
        at android.support.v4.app.ListFragment.getListView(ListFragment.java:222)
        at ebeletskiy.gmail.com.passwords.ui.ItemsFavsListFragment.refresh(ItemsFavsListFragment.java:17)

假设确实没有创建Content 视图,我将onActivityCreated() 方法中的boolean 变量设置为true 并使用if/else 条件调用getListView(),这显示了活动和内容视图成功创建。

然后我正在调试以查看 FragmentPagerAdapter 何时调用 getItem() 并且在 orientation change 事件之后未调用该方法。所以看起来ViewPager 包含对旧Fragments 的引用。这只是我的假设。

那么,有什么办法可以强制ViewPager 再次调用getItem(),这样我就可以使用对当前Fragments 的正确引用了吗?可能是其他一些解决方案?非常感谢。

【问题讨论】:

    标签: android


    【解决方案1】:

    然后我正在调试以查看 FragmentPagerAdapter 何时调用 getItem() 并且发生方向更改事件后未调用该方法。所以看起来 ViewPager 包含对旧片段的引用。

    片段应该被自动重新创建,就像任何片段都在配置更改中一样。如果您使用setRetainInstance(true) 则例外,在这种情况下,它们应该是与以前相同的片段对象。

    那么,有什么方法可以强制 ViewPager 再次调用 getItem(),这样我就可以使用对当前 Fragments 的正确引用?

    那里的碎片有什么问题?

    【讨论】:

    • 我不在那里使用setRetainInstance()。问题出在orientation change 之后弹出的异常中。我只是假设旧的 Fragments 正在使用中,因为我检查了 onActivityCreated() 对它们都执行的条件。
    • @siik:你什么时候调用这个refresh()方法?你什么时候取出新创建的片段来填充你的(ick)Hashtable
    • 我在onPageScrollStateChanged() 中调用这个refresh() 方法(这是ViewPager.OnPageChangeListener 接口的实现,即当我在Fragments 之间滑动时)。我在扩展FragmentPagerAdapter的类的getItem()方法中填充HashTable
    • 我刚刚在两个 Fragments 中添加了一个 setRetainInstance(true),并且不再有这样的异常,这只是证实了我的假设 Hashtable 中的引用在 orientation change 之后已经过时。还是很好奇在没有setRetainInstance(true)的情况下如何处理这种情况。
    • @siik:“这只是证实了我的假设,哈希表中的引用在方向改变后已经过时”——哦,绝对的。我以为你已经知道了,抱歉。 “仍然很好奇如何在没有 setRetainInstance(true) 的情况下处理这种情况”——为 ListView 小部件提供唯一标签,然后在 ViewPager 上使用 findViewWithTag() 来查找它们,而不是维护自己的单独集合。
    【解决方案2】:

    我花了几天的时间寻找这个问题的解决方案,并且想出了很多点:

    • 使用FragmentPagerAdapter 而不是FragmentStatePagerAdapter

    • 使用FragmentStatePagerAdapter 而不是FragmentPagerAdapter

    • getItemPosition 覆盖 FragmentPagerAdapter 时返回 POSITION_NONE

    • 如果您需要动态更改 Fragment,请不要使用 FragmentPagerAdapter

    • 还有很多很多其他...

    在我的应用程序中,例如Eugene,我管理自己创建片段的实例。我将它保存在某个专门的类中的 HashMap<String,Fragment> 中,因此片段永远不会被释放,从而加快了我的应用程序的速度(但会消耗更多资源)。

    问题出在我旋转平板电脑(和手机)时。 getItem(int) 不再为该片段调用,我无法更改它。

    我真的花了很多时间才真正找到解决方案,所以我需要与 StackOverflow 社区分享,他们帮助了我很多次......

    这个问题的解决方法,虽然很辛苦,但是很简单:

    只需在 FragmentPagerAdapter extends 的构造函数中保留对 FragmentManager 的引用即可:

    public class Manager_Pager extends FragmentPagerAdapter {
    
        private final FragmentManager mFragmentManager;
        private final FragmentActivity mContext;
    
        public Manager_Pager(FragmentActivity context) {
    
            super( context.getSupportFragmentManager() );
    
            this.mContext = context;
            this.mFragmentManager = context.getSupportFragmentManager();
        }
    
        @Override
        public int getItemPosition( Object object ) {
    
    // here, check if this fragment is an instance of the
    //   ***FragmentClass_of_you_want_be_dynamic***
    
            if (object instanceof FragmentClass_of_you_want_be_dynamic) {
    
    // if true, remove from ***FragmentManager*** and return ***POSITION_NONE***
    //   to force a call to ***getItem***
    
                mFragmentManager.beginTransaction().remove((Fragment) object).commit();
                return POSITION_NONE;
            }
    
            //don't return POSITION_NONE, avoid fragment recreation.
            return super.getItemPosition(object);
        }
    
        @Override
        public Fragment getItem( int position ) {
    
    
            if ( position == MY_DYNAMIC_FRAGMENT_INDEX){
    
                Bundle args = new Bundle();
                args.putString( "anything", position );
                args.putString( "created_at", ALITEC.Utils.timeToStr() );
    
                return Fragment.instantiate( mContext, FragmentClass_of_you_want_be_dynamic.class.getName(), args );
    
            }else
    
            if ( position == OTHER ){
    
                //...
    
            }else
    
            return Fragment.instantiate( mContext, FragmentDefault.class.getName(), null );
    
        }
    }
    

    仅此而已。它会像魅力一样发挥作用......

    【讨论】:

    • 它对我不起作用 getItem 从不调用方向更改。
    • this.mFragmentManager = context.getSupportFragmentManager();
    【解决方案3】:

    可以清除保存的实例状态

        protected void onCreate(Bundle savedInstanceState) {
            clearBundle(savedInstanceState);
            super.onCreate(savedInstanceState, R.layout.activity_car);
        }
    
    
        private void clearBundle(Bundle savedInstanceState) {
            if (savedInstanceState != null) {
                savedInstanceState.remove("android:fragments");
                savedInstanceState.remove("android:support:fragments");
                savedInstanceState.remove("androidx.lifecycle.BundlableSavedStateRegistry.key");
                savedInstanceState.remove("android:lastAutofillId");
            }
        }
    

    【讨论】:

      猜你喜欢
      • 2013-08-27
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 2014-03-17
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 2012-12-29
      相关资源
      最近更新 更多