2011-08-28 4 views
14

ここに私の問題があります。私は、タブでActionBar Sherlockを使用しているアプリケーションを持っており、オプションメニュー付きのフラグメントもあります。私がエミュレータを回転させるたびに、隠れた/削除されたフラグメントさえも、すべてのフラグメントのメニューが追加されます(私は両方を試しました)。onCreateOptionsMenuがタブを使用してActionBarで何回も呼び出されています

これは設定です:タブのすべてが同じリスナーを使用

final ActionBar bar = getSupportActionBar(); 

    bar.addTab(bar.newTab() 
     .setText("1") 
     .setTabListener(new MyTabListener(new FragmentList1()))); 

    bar.addTab(bar.newTab() 
     .setText("2") 
     .setTabListener(new MyTabListener(new FragmentList2()))); 

    bar.addTab(bar.newTab() 
     .setText("3") 
     .setTabListener(new MyTabListener(new FragmentList3()))); 

    bar.setNavigationMode(ActionBar.NAVIGATION_MODE_TABS); 
    bar.setDisplayShowHomeEnabled(true); 
    bar.setDisplayShowTitleEnabled(true); 

でアクションバーを持っている一つのFragmentActivityは、:

private class MyTabListener implements ActionBar.TabListener { 
    private final FragmentListBase m_fragment; 


    public MyTabListener(FragmentListBase fragment) { 
    m_fragment = fragment; 
    } 


    public void onTabSelected(ActionBar.Tab tab, FragmentTransaction ft) { 
    FragmentManager fragmentMgr = ActivityList.this.getSupportFragmentManager(); 
    FragmentTransaction transaction = fragmentMgr.beginTransaction(); 

     transaction.add(R.id.frmlyt_list, m_fragment, m_fragment.LIST_TAG); 

    transaction.commit(); 
    } 


    public void onTabUnselected(ActionBar.Tab tab, FragmentTransaction ft) { 
    FragmentManager fragmentMgr = ActivityList.this.getSupportFragmentManager(); 
    FragmentTransaction transaction = fragmentMgr.beginTransaction(); 

    transaction.remove(m_fragment); 
    transaction.commit(); 
    } 


    public void onTabReselected(ActionBar.Tab tab, FragmentTransaction ft) { 
    } 
} 

FragmentListBaseの各サブクラスは、独自のメニューを持っているため、すべての3つのサブクラスは:

setHasOptionsMenu(true); 

および適切な

public void onCreateOptionsMenu(Menu menu, MenuInflater inflater) { 
    Log.d(TAG, "OnCreateOptionsMenu"); 

    inflater.inflate(R.menu.il_options_menu, menu); 
} 

私がアプリケーションを実行すると、onCreateOptionsMenuがすべての異なるフラグメントに対して複数回呼び出されていることがわかります。

私は完全に困惑しています。

できるだけ多くのコードを投稿してみましたが、何かが見つからない場合はアドバイスをお願いします。

[編集] さらにロギングを追加しました。フラグメントがローテーション中に2回(またはそれ以上)添付されていることが判明しました。私が気づくことの一つは、onCreate()メソッドが1回だけ呼び出されていることを除いて、すべてが複数回呼び出されていることです。

06.704:/WindowManager(72): Setting rotation to 0, animFlags=0 
06.926:/ActivityManager(72): Config changed: { scale=1.0 imsi=310/260 loc=en_US touch=3 keys=1/1/2 nav=1/2 orien=L layout=0x10000014 uiMode=0x11 seq=35} 
07.374:/FragmentList1(6880): onAttach 
07.524:/FragmentList1(6880): onCreateView 
07.564:/FragmentList1(6880): onAttach 
07.564:/FragmentListBase(6880): onCreate 
07.564:/FragmentList1(6880): OnCreateOptionsMenu 
07.574:/FragmentList1(6880): OnCreateOptionsMenu 
07.604:/FragmentList1(6880): onCreateView 

[編集2]

[OK]を、私は(私がこの記事を短くするために編集されていること)Androidのコードに遡っ開始し、ここでこの部分を発見しました。

/com_actionbarsherlock/src/android/support/v4/app/FragmentManager.java

public boolean dispatchCreateOptionsMenu(Menu menu, MenuInflater inflater) { 
    if (mActive != null) { 
     for (int i=0; i<mAdded.size(); i++) { 
      Fragment f = mAdded.get(i); 
      if (f != null && !f.mHidden && f.mHasMenu) { 
       f.onCreateOptionsMenu(menu, inflater); 
      } 
     } 
    } 

問題がonCreateOptionsMenu()メソッド」であるのでmAddedが実際に、その中にFragmentList1の複数のインスタンスを持っているということですFragmentList1クラスの異なるインスタンスに対して3回呼び出されます。私が理解できないことは、そのクラスが何度も追加されている理由です...しかし、それは良いリードの地獄です。

答えて

7

私は問題を見つけたようです。私は多くのメニューの上に例外があるので、問題を言います。

1)addTabへ後コールを()onTabSelectedを呼び出すの副作用を(有する)である

bar.setNavigationMode(ActionBar.NAVIGATION_MODE_TABS); 

への呼び出し。私のTabListenerは、FragmentManagerにFragmentList1を追加します

2)デバイスを回転すると、期待どおりにアクティビティが破棄されますが、フラグメントは破棄されません。

  1. はそれがFragmentManagerに追加するフラグメントの別のセットを作成します。新しい活動が回転した後に作成されると、それは2つのことをするだろう。これは、次のコードを実行することになる(setNavigationMode介して())onTabSelected多数

  2. メニュー
  3. の呼び出し引き起こしていたものではない:基本的に

    if (null != fragmentMgr.findFragmentByTag(m_fragment.LIST_TAG)) { 
        transaction.attach(m_fragment); 
        transaction.show(m_fragment); 
    } 
    else { 
        transaction.add(R.id.frmlyt_list, m_fragment, m_fragment.LIST_TAG); 
    } 
    

の断片がFragmentManagerに既にある場合に何がありますそれを追加する必要があります、それを示すだけです。しかし問題があります。それは同じ断片ではありません!これは、アクティビティの以前のインスタンスによって作成されたフラグメントです。そこで、この新しく作成されたフラグメントをアタッチして表示し、例外を発生させようとします。

解決策。

これをすべて修正するには、いくつかのことがあります。

1)addTab()の上にsetNavigationMode()を移動しました。

2)これは私が今、私のタブを作成する方法である:

FragmentListBase fragment = (FragmentListBase)fragmentMgr.findFragmentByTag(FragmentList1.LIST_TAG_STATIC); 
    if (null == fragment) { 
    fragment = new FragmentList1(); 
    } 
    bar.addTab(bar.newTab() 
     .setText("1") 
     .setTabListener(new MyTabListener(fragment))); 

だから私は、フラグメントはFragmentManagerに既にあるかどうかを確認する必要があるアクティビティ作成時。それらのインスタンスを使用している場合、それらのインスタンスを使用する場合は、新しいインスタンスを作成します。これは3つすべてのタブで行われます。

m_fragment.LIST_TAGとFragmentList1.LIST_TAG_STATICという2つの類似したラベルがあることに気がつくかもしれません。

public class FragmentListBase extends Fragment { 
    public String LIST_TAG = null; 
} 

それは内側から割り当てられている:多形Iは、基本クラスでは、以下の非静的変数を宣言している私のTagListenerを使用するordrerで

- ああ、これは...(皮肉<)素敵です私はFragmentListBaseの異なる子孫のためにFragmentManagerを見ることができます。

しかし、作成する前に特定の子孫を検索する必要があるため(作成する必要があるかどうかを知る必要があるため)、次の静的変数も宣言する必要があります。( - もっと皮肉<)

おかげで私のためにこれを見に時間がかかったジェイク・ウォートンにたくさん

public class FragmentList1 extends FragmentListBase { 
    public final static String LIST_TAG_STATIC = "TAG_LIST_1"; 

    public FragmentList1() { 
     LIST_TAG = LIST_TAG_STATIC; 
    }; 
} 

十分では私は誰もが、このシンプルでエレガントな解決策を考え出したていないことdisapointedだと言うこと:)

+1

この回答はあまりにも複雑です。 * ActionBar *にタブを追加する前に 'bar.setNavigationMode(ActionBar.NAVIGATION_MODE_TABS)'に移動するだけで問題は解決しました。 – Phil

6
public FragmentListBase() { 
    setRetainInstance(true); 
    setHasOptionsMenu(true); 
} 

これは、回転時に各フラグメントの個々の状態を保存/復元します。


あなたがしたいかもしれないもう一つの簡単な変更は、タブ選択したコールバックでtransaction.replace(R.id.frmlyt_list, m_fragment, m_fragment.LIST_TAG)を呼び出していると、選択されていないコールバック内のコンテンツを取り除きます。

+0

おかげで、しかし:灘ここ

は、コードスニペットです。私はコードに追加しましたが、変更はありません。私は私の質問に多くの情報を追加しました... – MikeWallaceDev

-4

は少なくともハニカムにSDKの問題は、あなたのアンドレにおける活動宣言に

android:configChanges="orientation" 

を追加することによって解決される関連oidManifest.xmlファイル。 フラグメントの追加と削除は、タブの追加セクション(http://developer.android.com/guide/topics/ui/actionbar.html)にあります。

0

多形タグのフラストレーションにはかなり注意してください。

そうのようなあなたの基本クラスを宣言します。

:インスタンスタグを取得するための多型の方法は、このようなものです今

public class FragmentList1 extends ListFragmentBase { 
    public static final String LIST_TAG = "TAG_LIST_1"; 

    @Override 
    protected String getListTag() { 
     return LIST_TAG; 
    } 
} 

public abstract class ListFragmentBase { 
    protected abstract String getListTag(); 
} 

今すぐあなたのサブクラスのこのような何かを宣言

ListFragmentBase frag = new FragmentList1(); 
frag.getListTag(); 

静的にタグを取得します。

FragmentList1.LIST_TAG; 
+1

静的なLIST_TAG文字列をプライベートに設定して、どのプロパティを使用するのか混同しないようにすることもできます。 –

+0

@DominikvonWeber LIST_TAGをプライベートに設定できましたが、静的にアクセスできませんでした。それはあなたのニーズに左右されます。ソリューションでは、ユーザーが静的にアクセスします – Dave

3

私は回転時にスタック可能なメニューに非常に似た問題がありました。私はタブを使用しませんが、私はFragmentStatePagerAdapterでViewPagerを使用するので、私は実際に私のフラグメントを再利用することはできません。私の頭を2日間叩いた後、私は非常に簡単な解決策を見つけました。確かに問題は複数回呼び出されたonCreateOptionsMenuであるようです。この小さなコードスニペットは、すべての問題の世話(?マスクを)取る:フラグメントが宣言された活動、すなわち、呼び出し元のアクティビティに(真)setHasMenuOptionsを動かした

/** to prevent multiple calls to inflate menu */ 
private boolean menuIsInflated; 

@Override 
public void onCreateOptionsMenu(final Menu menu, final MenuInflater inflater) { 
    if (!menuIsInflated) { 
     inflater.inflate(R.menu.job_details_fragment_menu, menu); 
     menuIsInflated = true; 
    } 
} 
+0

私たちはこれを行う必要があります。私は彼らの側のバグであることを誓います。 – reidisaki

1

何私のために働きました。私は以前にフラグメントのonCreateメソッドでそれを持っていました。答えはジェイクのため

@Override 
    protected void onCreate(Bundle savedInstanceState) { 
     super.onCreate(savedInstanceState); 
     setContentView(R.layout.activity_main); 
     FragmentManager fragmentManager = getFragmentManager(); 
     FragmentTransaction fragmentTransaction = fragmentManager.beginTransaction(); 

     ForecastFragment forecastFragment = new ForecastFragment(); 
     forecastFragment.setHasOptionsMenu(true); 
     fragmentTransaction.add(R.id.fragment, forecastFragment); 
     fragmentTransaction.commit(); 
    } 
+0

上記の回答はどれも私のために働いていませんでしたが、あなたの答えはありました。ありがとう! –

関連する問題