2012-02-26 13 views
18

私はActionBarで簡単なAndroidアクティビティを作って2つのフラグメントを切り替えました。 デバイスを回転するまでは問題ありません。事実、私が回転すると、前のアクティブなものと最初のものの2つの断片があります。 なぜですか? 私の活動を破壊して再作成すると、なぜ2つの断片が得られますか?ActionBarでAndroidを回転させた二重フラグメント

サンプルコード:

活動

package rb.rfrag.namespace; 

import android.app.ActionBar; 
import android.app.ActionBar.Tab; 
import android.app.Activity; 
import android.os.Bundle; 

    public class RFragActivity extends Activity { 
    /** Called when the activity is first created. */ 
    @Override 
    protected void onCreate(Bundle savedInstanceState) { 
     super.onCreate(savedInstanceState); 
     // Notice that setContentView() is not used, because we use the root 
     // android.R.id.content as the container for each fragment 

    // setup action bar for tabs 
     final ActionBar actionBar = getActionBar(); 
     actionBar.setNavigationMode(ActionBar.NAVIGATION_MODE_TABS); 
     //actionBar.setDisplayShowTitleEnabled(false); 

     Tab tab; 
     tab = actionBar.newTab() 
       .setText(R.string.VarsTab) 
       .setTabListener(new TabListener<VarValues>(
         this, "VarValues", VarValues.class)); 
     actionBar.addTab(tab); 

     tab = actionBar.newTab() 
       .setText(R.string.SecTab) 
       .setTabListener(new TabListener<SecFrag>(
         this, "SecFrag", SecFrag.class)); 
     actionBar.addTab(tab); 
    } 
} 

TabListener

package rb.rfrag.namespace; 

import android.app.ActionBar; 
import android.app.Activity; 
import android.app.Fragment; 
import android.app.FragmentManager; 
import android.app.FragmentTransaction; 
import android.app.ActionBar.Tab; 

public class TabListener<T extends Fragment> implements ActionBar.TabListener { 
    private Fragment mFragment; 
    private final Activity mActivity; 
    private final String mTag; 
    private final Class<T> mClass; 

    /** Constructor used each time a new tab is created. 
     * @param activity The host Activity, used to instantiate the fragment 
     * @param tag The identifier tag for the fragment 
     * @param clz The fragment's Class, used to instantiate the fragment 
     */ 
    public TabListener(Activity activity, String tag, Class<T> clz) { 
     mActivity = activity; 
     mTag = tag; 
     mClass = clz; 
    } 

    /* The following are each of the ActionBar.TabListener callbacks */ 

    public void onTabSelected(Tab tab, FragmentTransaction ft) {  
     // Check if the fragment is already initialized 
     if (mFragment == null) { 
      // If not, instantiate and add it to the activity 
      mFragment = Fragment.instantiate(mActivity, mClass.getName()); 
      ft.add(android.R.id.content, mFragment, mTag); 
     } else { 
      // If it exists, simply attach it in order to show it 
      ft.attach(mFragment); 
     } 
    } 

    public void onTabUnselected(Tab tab, FragmentTransaction ft) { 
     if (mFragment != null) { 
      // Detach the fragment, because another one is being attached 
      ft.detach(mFragment); 
     } 
    } 
} 

答えて

20

私が選択したタブを維持するための活動にonSaveInstanceStateonRestoreInstanceStateを使用して解決してを修正しました図10に示すように、

最後の変更は、Androidが破棄しないFragmentを再構築しないようにします。しかし、現在のフラグメントナンバーの間にローテーションイベントによってアクティビティが破壊された理由を理解できません。 (あなたはこれについていくつかのアイデアをお持ちですか?)

public void onTabSelected(Tab tab, FragmentTransaction ft) { 
     // previous Fragment management 
     Fragment prevFragment; 
     FragmentManager fm = mActivity.getFragmentManager(); 
     prevFragment = fm.findFragmentByTag(mTag); 
     if (prevFragment != null) { 
      mFragment = prevFragment; 
     } // \previous Fragment management 

     // Check if the fragment is already initialized 
     if (mFragment == null) { 
      // If not, instantiate and add it to the activity 
      mFragment = Fragment.instantiate(mActivity, mClass.getName()); 
      ft.add(android.R.id.content, mFragment, mTag); 
     } else { 
      // If it exists, simply attach it in order to show it 
      ft.attach(mFragment); 
     } 
    } 
+1

これは完全に働きました。これが「正しい」方法であるかどうかについての手掛かりはありますか? –

+3

私がこれを見つけだすまで、それは私にナットをもたらしました...この答えに感謝します! (まだGoogleがその例でそれを修正しないとは信じられない) – Patrick

+0

面白い。私は、ポートレートで 'android.support.v4.view.ViewPager'を使用し、風景のときに両方のフラグメントを表示します。二度回転すると、私は風景の中で二つの断片を得る。 1つは* android:switcher:2131099773:1 *のように呼ばれ、私を馬鹿にする。私はゾンビを取り除くためにホックを見つけなければならない。 – Martin

11

を私はonTabSelectedをオーバーライドandroid.support.v4.view.ViewPagerを使用しているのですることは役に立ちません。しかし、あなたは正しい方向に私を指摘しています。

android.support.v4.app.FragmentManagerは、すべてのフラグメントをandroid.support.v4.app.FragmentActivityonSaveInstanceStateに保存します。 無視するsetRetainInstance - デザインによっては、フラグメントが重複する可能性があります。

最も簡単な解決策は、活動のorCreateに保存されたフラグメントを削除するには、次のとおりです。

@Override 
    public void onCreate (final android.os.Bundle savedInstanceState) 
    { 
     if (savedInstanceState != null) 
     { 
     savedInstanceState.remove ("android:support:fragments"); 
     } // if 

     super.onCreate (savedInstanceState); 
… 
     return; 
    } // onCreate 
+0

+1000これは決して見つかりませんでした。 – Patrick

+1

これは、Fragment2がLandscapeになると、fragment1が表示されることを除いて、うまく機能しました。私がこの問題を抱えている唯一の人かどうかはわかりません。どんな解決策ですか? – Art

+0

あなたのソリューションは、ポートレートとランドスケープでも機能します。タブ間の切り替え後にもはや重なり合わない。 –

3

ソリューションは、実際に画面を回転させたときに、次の手順を確認してください、仕事の全体の多くを必要としません、タブの選択が維持されます。画面の回転時に、最初のタブが選択され、スクリーンを回転する前に選択された2番目のタブではなく、したがって第1のタブが第2のタブの内容と重なっていたので、重なり合ったフラグメントに遭遇しました。

これはあなたの活動は、(私はActionBarSherlockを使用しますが、それは非常に簡単なはず調整しています)どのように見えるべきかです:

public class TabHostActivity extends SherlockFragmentActivity { 

    private static final String SELETED_TAB_INDEX = "tabIndex"; 


    @Override 
    protected void onCreate(Bundle savedInstanceState) { 
    super.onCreate(savedInstanceState); 

    // Setup the action bar 
    ActionBar actionBar = getSupportActionBar(); 
    actionBar.setNavigationMode(ActionBar.NAVIGATION_MODE_TABS); 

    // Create the Tabs you need and add them to the actionBar... 

    if (savedInstanceState != null) { 
     // Select the tab that was selected before orientation change 
     int index = savedInstanceState.getInt(SELETED_TAB_INDEX); 
     actionBar.setSelectedNavigationItem(index); 
    } 
    } 

    @Override 
    protected void onSaveInstanceState(Bundle outState) { 
    super.onSaveInstanceState(outState); 
    // Save the index of the currently selected tab 
    outState.putInt(SELETED_TAB_INDEX, getSupportActionBar().getSelectedTab().getPosition()); 
    } 
} 

そして、これは私のActionBar.TabListenerは、上記で(そのプライベートクラスのように見えるものです上記の実装では、タブ内のフラグメントをタグに基づいて置換することもできます。この目的のために、同じタブ内でフラグメントを切り替えるときに、同じタグ名を使用します。これは、タブに追加された初期フレームワークに使用されました。

0

ありがとうございましたMartinとasclepixのソリューションです。、

<RelativeLayout xmlns:android="http://schemas.android.com/apk/res/android" 
    xmlns:tools="http://schemas.android.com/tools" 
    android:layout_width="fill_parent" 
    android:layout_height="fill_parent" 
    tools:context=".MainActivity" > 

    <FrameLayout 
     android:id="@+id/frActiveTask" 
     android:layout_width="fill_parent" 
     android:layout_height="50dp" 
     android:layout_alignParentBottom="true" 
     /> 

    <FrameLayout 
     android:id="@+id/frTaskList" 
     android:layout_width="fill_parent" 
     android:layout_height="match_parent" 
     android:layout_alignParentTop="true" 
     android:layout_above="@id/frActiveTask" 
     /> 

</RelativeLayout> 

アクティブなタブは最初の1でない場合を除いて、ほぼ正常に動作しonRestoreInstanceStateonSaveInstanceStatesavedInstanceState.remove("android:support:fragments");方法とステートメントを使用して回転させると、最初にクリックしてください:私は3つのタブと最初のタブのように、2つの断片が含まれているを持っていますクリアな表示が表示され、最初のタブの2回目のクリックだけが右側のフラグメント表示になります。 コードをデバッグした後、私は断片add方法で、第一addTabは常にタブリスナーにonTabSelectedイベントを呼び出すことを認識した後、setSelectedNavigationItemは、最初のタブ上で実行されるonRestoreInstanceStatedetachから呼び出されたときに、他方のためadd。 この不必要add呼び出しは私の解決策で修正されました。

私の活動

protected void onCreate(Bundle savedInstanceState) { 
    boolean firstTabIsNotAdded = false; 
    if (savedInstanceState != null) { 
     savedInstanceState.remove("android:support:fragments"); 
     firstTabIsNotAdded = savedInstanceState.getInt(SELETED_TAB_INDEX) != 0; 
    } 
    super.onCreate(savedInstanceState); 

    setContentView(R.layout.activity_main); 

// codes before adding tabs 

    actionBar = getSupportActionBar(); 
    actionBar.setNavigationMode(ActionBar.NAVIGATION_MODE_TABS); 


    tabStartAndStop = actionBar.newTab().setText(getString(R.string.tab_title_start_and_stop)) 
      .setTabListener(
        new FragmentTabListener<StartStopFragment>(this, 
          getString(R.string.tab_title_start_and_stop_id), 
          StartStopFragment.class, 
          firstTabIsNotAdded)); 
    tabHistory = actionBar.newTab().setText(getString(R.string.tab_title_history)) 
      .setTabListener(
        new FragmentTabListener<HistoryFragment>(this, 
          getString(R.string.tab_title_history_id), 
          HistoryFragment.class, 
          false)); 
    tabRiporting = actionBar.newTab().setText(getString(R.string.tab_title_reporting)) 
      .setTabListener(
        new FragmentTabListener<ReportingFragment>(this, 
          getString(R.string.tab_title_reporting_id), 
          ReportingFragment.class, 
          false)); 

    actionBar.addTab(tabStartAndStop); 

     actionBar.addTab(tabHistory); 
     actionBar.addTab(tabRiporting); 

    } 

    @Override 
    protected void onRestoreInstanceState(Bundle savedInstanceState) { 
     if (savedInstanceState != null) { 
      int index = savedInstanceState.getInt(SELETED_TAB_INDEX); 
      actionBar.setSelectedNavigationItem(index); 
     } 
     super.onRestoreInstanceState(savedInstanceState); 
    } 

    @Override 
    protected void onSaveInstanceState(Bundle outState) { 
     super.onSaveInstanceState(outState); 
     // Save the index of the currently selected tab 
     outState.putInt(SELETED_TAB_INDEX, getSupportActionBar().getSelectedTab().getPosition()); 
    } 

、修正タブリスナー

public class FragmentTabListener<T extends SherlockFragment> implements com.actionbarsherlock.app.ActionBar.TabListener { 
    private Fragment mFragment; 
    private final Activity mFragmentActivity; 
    private final String mTag; 
    private final Class<T> mClass; 
    private boolean doNotAdd; 

    /** Constructor used each time a new tab is created. 
     * @param activity The host Activity, used to instantiate the fragment 
     * @param tag The identifier tag for the fragment 
     * @param clz The fragment's Class, used to instantiate the fragment 
     */ 
    public FragmentTabListener(Activity activity, String tag, Class<T> clz, boolean doNotAdd) { 
     mFragmentActivity = activity; 
     mTag = tag; 
     mClass = clz; 
     this.doNotAdd = doNotAdd; 
    } 

    /* The following are each of the ActionBar.TabListener callbacks */ 
    public void onTabSelected(Tab tab, FragmentTransaction ft) { 

     // Check if the fragment is already initialized 
     if (mFragment == null) { 
      // If not, instantiate and add it to the activity 
      if(doNotAdd){ 
       doNotAdd = false; 
      }else{ 
       mFragment = Fragment.instantiate(mFragmentActivity, mClass.getName()); 
       ft.add(android.R.id.content, mFragment, mTag); 
      } 
     } else { 
      // If it exists, simply attach it in order to show it 
      ft.attach(mFragment); 
     } 
    } 

    public void onTabUnselected(Tab tab, FragmentTransaction ft) { 
     if (mFragment != null) { 
      // Detach the fragment, because another one is being attached 
      ft.detach(mFragment); 
     } 
    } 

    public void onTabReselected(Tab tab, FragmentTransaction ft) { 
     // User selected the already selected tab. Usually do nothing. 
    } 
} 
関連する問題