web-dev-qa-db-ja.com

IllegalStateException:<MyFragment>は現在FragmentManagerにありません

FragmentStatePagerAdapter IllegalStateException:<MyFragment>は現在FragmentManagerにありません の複製のように聞こえますが、彼の解決策は私のケースには関係ありません。

まれに次のクラッシュが発生します。

Java.lang.RuntimeException:アクティビティ{MyActivity}を一時停止できません:

...

原因:Java.lang.IllegalStateException:Fragment MyFragment {40648258 id = 0x7f070051}は現在、Android.support.v4.app.FragmentStatePagerAdapterのAndroid.support.v4.app.FragmentManagerImpl.putFragment(MT:516)のFragmentManagerにありません.saveState(MT:185)at Android.support.v4.view.ViewPager.onSaveInstanceState(MT:881)

...

android.view.View.saveHierarchyState(View.Java:6238)at com.Android.internal.policy.impl.PhoneWindow.saveHierarchyState(PhoneWindow.Java:1522)at Android.app.Activity.onSaveInstanceState(Activity.Java:1138 )Android.support.v4.app.FragmentActivity.onSaveInstanceState(MT:480)at MyActivity.onSaveInstanceState(MT:336)

これは私がFragmentStatePagerAdapterから理解できない奇妙なコードのようです:

for (int i=0; i<mFragments.size(); i++) {
    Fragment f = mFragments.get(i);
    if (f != null) {
        if (state == null) {
            state = new Bundle();
        }
        String key = "f" + i;
        mFragmentManager.putFragment(state, key, f);
    }
}

アダプターはFragmentからmFragmentsを取得しているようですが、FragmentManagerに状態を追加できません。

テストデバイスでこれを再現する方法が見つからず、一部のユーザーからしか受け取っていません。

サポートパッケージv4を使用しています。

何か助けは?ありがとう。

34
marmor

FragmentStatePagerAdapterは、Googleによって確認された、または確認されていないバグでおびき寄せられる恐ろしいコードの一部であるため、この特定のクラッシュを修正するためにこのコードを使用します。

    @Override
    public void destroyItem(ViewGroup container, int position, Object object) {
        // Yet another bug in FragmentStatePagerAdapter that destroyItem is called on fragment that hasnt been added. Need to catch
        try {
            super.destroyItem(container, position, object);
        } catch (IllegalStateException ex) {
            ex.printStackTrace();
        }
    }
16
Greg Ennis

これは、ViewPagerでFragmentStatePagerAdapterが設定され、Fragmentsが設定されているが、ViewPagerのレイアウトが発生する前にアクティビティが一時停止しているため、まだFragmentManagerに追加されていない場合に発生する可能性があります。これは、アダプタもonCreateで設定された状態で、onCreateから別のアクティビティを開始することで確実にヒットします。そのような状況では、アダプタの設定を保留し、代わりにonActivityResultを設定するのが最善です。この問題を参照してください: https://code.google.com/p/Android/issues/detail?id=77285

状態を保存しようとする前に、フラグメントが追加されていることを確認するパッチも送信しました。

10
piusvelte

正確な例外が発生しました。

私の場合、FragmentPagerStateAdapterによって管理されているいくつかのフラグメントがありますが、フラグメントが適切な位置に切り替わることがあります。

GetItemPosition()をオーバーライドして新しい位置を返し、notifyDataSetChanged()を呼び出してViewPagerを更新します。すべて正常に動作しますが、ViewPagerを離れるとクラッシュが発生することがあります。

数時間調べたところ、FragmentPagerStateAdapterにバグがあることがわかりました。

アダプターは状態をキャッシュするだけでなく、「アクティブ」と見なすフラグメントもキャッシュします

private ArrayList<Fragment.SavedState> mSavedState = new ArrayList<Fragment.SavedState>();
private ArrayList<Fragment> mFragments = new ArrayList<Fragment>();

ただし、フラグメントの位置が無効になったか移動したかどうかはチェックしません。

それがこの例外を取得する方法です:

1.I、ViewPagerおよびAdapterにA、B、Cの3つのフラグメントがあります。

2.アダプターの位置をB、A、Cに切り替え、notifyDataSetChanged()を呼び出しました。

3.ViewPagerは、フラグメントを新しい順序で再レイアウトします。しかし、mFragmentsキャッシュはまだ{A、B、C}です

4.数ページをスワイプすると、ViewPagerはアダプターに最初のフラグメントを破棄するように要求します。 その後、AではなくフラグメントBがキャッシュでnullに設定されます。 mFragmentsは{null、A、C}です。

5.ViewPagerを離れると、onSaveInstanceStateがトリガーされます。 mFragments内のすべてのフラグメントはアクティブであると見なされ、FragmentManagerImplがその中にないAを見つけて例外をスローするまでputFragment()が呼び出されます。

だからここに私のそれほど穏やかな解決策があります:

1. FragmentPagerStateAdapterソースコード全体をコピーします。

2.notifyDataSetChanged()メソッドをオーバーライドして、以下のようにキャッシュを再配置します。

@Override
public void notifyDataSetChanged() {
  List<Fragment> oldFragments = new ArrayList<>(mFragments);
  List<Fragment.SavedState> oldStates = new ArrayList<>(mSavedState);
  for (int i = 0; i < getCount(); i++) {
    if (i < mFragments.size()) {
      Fragment f = mFragments.get(i);
      if (f != null) {
        int newPosition = getItemPosition(f);
        if (newPosition == POSITION_UNCHANGED || newPosition == i) {
        } else if (newPosition == POSITION_NONE) {
          if (i < mSavedState.size()) {
            mSavedState.set(i, null);
          }
          mFragments.set(i, null);
        } else {
          while (i >= mFragments.size()) {
            mFragments.add(null);
          }
          if (oldStates.size() > i) {
            mSavedState.set(newPosition, oldStates.get(i));
          }
          mFragments.set(newPosition, oldFragments.get(i));
        }
      } else {
        /*
         * No Fragment but that's possible there's savedState and position has
         * changed.
         */
      }
    }
  }
  super.notifyDataSetChanged();
}

あなたの一部でうまくいくことを願っています!

7
Joseph

フラグメント階層がある場合は、getChildFragmentManager()ではなくgetFragmentManager()を使用します。例えば。フラグメントページャーがある場合。

7
ViliusK

以下を使用

final FragmentTransaction ft = getSupportFragmentManager().beginTransaction();
ft.add(f, key);

の代わりに

mFragmentManager.putFragment(state, key, f);

バンドルを明示的に渡します。

参考のために、

http://developer.Android.com/reference/Android/app/FragmentTransaction.html#add%28Android.app.Fragment,%20Java.lang.String%29

2
Sripathi

ViewPagerが(アクティビティではなく)フラグメント内にレイアウトされている場合:

mViewPager.setAdapter(new MyFragmentStatePagerAdapter(getChildFragmentManager()));

2
Thomas G.

Activity onCreate()メソッドに書き込みます。

pager = (ViewPager) findViewById(R.id.pager);
    adapter = new SwipePagerAdapter(getSupportFragmentManager());
    pageOneFragment = new PageOneFragment();
    adapter.addFragment(pageOneFragment);

アダプターコード:

public class SwipePagerAdapter extends FragmentStatePagerAdapter
{
private final ArrayList<Fragment> mFragments = new ArrayList<Fragment>();

public SwipePagerAdapter(FragmentManager fm)
{
    super(fm);
}

@Override
public Fragment getItem(int position)
{
    return mFragments.get(position);
}

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

public void addFragment(Fragment fragment)
{
    mFragments.add(fragment);
    notifyDataSetChanged();
}

@Override
public void destroyItem(ViewGroup container, int position, Object object)
{
    super.destroyItem(container, position, object);
}

@Override
public CharSequence getPageTitle(int position)
{
    return super.getPageTitle(position);
}}
1
Abdul Rehman

あなた自身のコードからFragmentStatePagerAdapter.instantiateItem()を呼び出していますか?通常、ViewPagerがこのメソッドを呼び出す唯一のクライアントである必要がありますが、このメソッドに依存する他の制限の回避策に出くわすことは簡単です。

FragmentStatePagerAdapterの動作方法が原因で、アダプターがFragmentをインスタンス化し、それをFragmentの内部コレクションに追加し、さらにそのFragmentをFragmentManagerに追加するトランザクションを開始したと思いますが、そのフラグメントトランザクションはコミットまたは実行されていません。これは、リクエストされたフラグメントをまだインスタンス化していない場合のinstantiateItem()の動作とまったく同じです。

別のメソッドであるFragmentStatePagerAdapter.finishUpdate()は、トランザクションのコミットと保留中のトランザクションの実行を担当します。 finishUpdate()は、instantiateItem()の後に呼び出す必要があります。そうしないと、フラグメントがFragmentManagerに追加されない場合があります。

次のようなものを試してください:

// Get the current Fragment presented by the ViewPager.
pagerAdapter.startUpdate(viewPager);
Fragment fragment = pagerAdapter.instantiateItem(viewPager, viewPager.getCurrentItem());
pagerAdapter.finishUpdate(viewPager);
return fragment;

startUpdate()には、APIレベル19以降、空の実装があります。

1
James Wald

あなたはmFragmentManager.add();を使ってみることができます

0
superuser

使ってみてください

use fragmentTransaction.add()
0
Bedimindtricks

アクティビティにonSaveInstanceStateonRestoreInstanceStateを実装しているかどうかを再確認し、フラグメントの状態が正しく保存およびロードされていることを確認します。

0
Hubert

ViewPagerのフラグメントが修正されました。アダプターのフラグメントを置き換えたり、別のフラグメントのセットを提供してnotifyDataSetを変更したりするか、FrameLayoutを利用してビューページャータブの現在のフラグメントに別のフラグメントを表示してください。

うまくいく私の解決策があります:

スワイプジェスチャーは、ViewPagerとともにフラグメントレベルで適用され、デフォルトのスワイプは無効になっています

0
Abhinav Saxena