FragmentPagerAdapter在方向改变时不重新创建Fragments?

9 人关注

我有一个 ViewPager (扩展了 FragmentPagerAdapter ),里面有两个 Fragments 。我需要的是,当我在每个 Fragment 中滑动时,为它们刷新一个 ListView 。为此我实现了 ViewPager.OnPageChangeListener 接口(即 onPageScrollStateChanged )。为了保存对 Fragment 的引用,我使用了一个 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).

results in IllegalStateException:

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
Eugene
Eugene
发布于 2012-09-04
3 个回答
CommonsWare
CommonsWare
发布于 2021-03-07
已采纳
0 人赞同

然后我在调试中看到FragmentPagerAdapter调用getItem()时,碰巧该方法在方向改变事件后没有被调用。所以看起来ViewPager持有对旧片段的引用。

这些片段应该被自动重新创建,就像任何片段在配置改变时一样。例外的情况是,如果你使用了 setRetainInstance(true) ,在这种情况下,它们应该是和以前一样的片段对象。

那么,有没有办法强制ViewPager再次调用getItem(),以便我可以使用对当前片段的适当引用?

那里的碎片有什么问题呢?

我没有在其中使用 setRetainInstance() 。问题是在 orientation change 之后弹出的异常。我只是假设旧的Fragments在使用,因为我检查了 onActivityCreated() 对它们都被执行的条件。
@siik:你什么时候调用这个 refresh() 方法?你又是什么时候把新创建的片段拉出来填充到你的 Hashtable 中的呢?
我在 onPageScrollStateChanged() 中调用这个 refresh() 方法(它是 ViewPager.OnPageChangeListener 接口的实现,即当我在 Fragments 之间滑动时)。而我在 getItem() 类的 FragmentPagerAdapter 的方法中填充 HashTable ,该类延伸了【替换代码】。
我刚刚在两个片段中都添加了 setRetainInstance(true) ,现在已经没有这样的异常了,这正好证实了我的假设,即在 orientation change 之后, Hashtable 中的引用已经过时了。我还是很好奇如何处理没有 setRetainInstance(true) 的情况。
@siik: "这只是证实了我的假设,Hashtable中的引用在方向改变后是过时的" -- 哦,当然。我以为你已经知道了,抱歉。"我还是很好奇如何在没有setRetainInstance(true)的情况下处理这种情况" -- 给 ListView 的小部件加上唯一的标签,然后使用 findViewWithTag() ViewPager 上找到它们,而不是维持你自己的单独集合。
alijunior
alijunior
发布于 2021-03-07
0 人赞同

我花了一些时间寻找解决这个问题的办法,很多地方都想通了。

  • use FragmentPagerAdapter instead of FragmentStatePagerAdapter

  • use FragmentStatePagerAdapter instead of FragmentPagerAdapter

  • return POSITION_NONE on getItemPosition override of FragmentPagerAdapter

  • 如果你需要动态改变片段,请不要使用FragmentPagerAdapter。

  • 和许多许多其他的人...

  • 在我的应用程序中,像 Eugene 我自己管理创建的片段的实例。我把它保存在一个 HashMap<String,Fragment> 里,在一些专门的类里,所以这些片段永远不会被释放,加快了我的应用程序(但消耗了更多的资源)。

    问题出在我旋转平板电脑(和手机)的时候。那个片段的 getItem(int) 没有再被调用,我也无法改变它。

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

    这个问题的解决方案,虽然寻找起来很辛苦,但却很简单。

    只要在FragmentPagerAdapter扩展的构造函数中保留对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()。
    fabian dario
    fabian dario
    发布于 2021-03-07
    0 人赞同

    你可以清除保存的实例状态

        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");