Double fragment rotating Android with ActionBar

后端 未结 4 683
轻奢々
轻奢々 2020-12-24 08:33

I\'ve made a simple Android Activity with an ActionBar to switch between 2 fragments. It\'s all ok until I rotate the device. In facts, when I rotate I\'ve got 2 fragment on

相关标签:
4条回答
  • 2020-12-24 08:42

    Thanks Martin and asclepix for theirs solutions. I have 3 tabs and first tab contains 2 fragments, like this:

    <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>
    

    Using onRestoreInstanceState, onSaveInstanceState and savedInstanceState.remove("android:support:fragments"); methods and statement working almost fine except if your active tab is not the first one and rotate and click on first, a clear display appears and only for the second click on the first tab came the right fragment display. After debugging code I recognized that the first addTab always calls an onTabSelected event in the tab listener, with a fragment add method and then when the setSelectedNavigationItem is called from onRestoreInstanceState a detach is executed on the first tab and an add for other one. This unecessary add calling is fixed in my solution.

    My activity

    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());
        }
    

    And the modified tab listener

    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.
        }
    }
    
    0 讨论(0)
  • 2020-12-24 08:43

    The solution does not actually require a whole lot of work, the following steps ensure, that when rotating the screen, the tab selection is maintained. I ran into overlapping Fragments, because upon screen rotation my first tab was selected, not the second one that was selected before rotating the screen and hence the first tab was overlapping the content of the second tab.

    This is how your Activity should look (I am using ActionBarSherlock but adjusting it should be very easy):

    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());
       }
    }
    

    And this is what my ActionBar.TabListener looks like (its a private class in the above Activity):

      private class MyTabsListener<T extends Fragment> implements ActionBar.TabListener {
         private Fragment fragment;
         private final SherlockFragmentActivity host;
         private final Class<Fragment> type;
         private String tag;
    
         public MyTabsListener(SherlockFragmentActivity parent, String tag, Class type) {
            this.host = parent;
            this.tag = tag;
            this.type = type;
         }
    
         @Override
         public void onTabSelected(Tab tab, FragmentTransaction transaction) {
            /*
             * The fragment which has been added to this listener may have been
             * replaced (can be the case for lists when drilling down), but if the
             * tag has been retained, we should find the actual fragment that was
             * showing in this tab before the user switched to another.
             */
            Fragment currentlyShowing = host.getSupportFragmentManager().findFragmentByTag(tag);
    
            // Check if the fragment is already initialised
            if (currentlyShowing == null) {
              // If not, instantiate and add it to the activity
              fragment = SherlockFragment.instantiate(host, type.getName());
              transaction.add(android.R.id.content, fragment, tag);
            } else {
              // If it exists, simply attach it in order to show it
              transaction.attach(currentlyShowing);
            }
         }
    
         public void onTabUnselected(Tab tab, FragmentTransaction fragmentTransaction) {
            /*
             * The fragment which has been added to this listener may have been
             * replaced (can be the case for lists when drilling down), but if the
             * tag has been retained, we should find the actual fragment that's
             * currently active.
             */
            Fragment currentlyShowing = host.getSupportFragmentManager().findFragmentByTag(tag);
            if (currentlyShowing != null) {
              // Detach the fragment, another tab has been selected
              fragmentTransaction.detach(currentlyShowing);
            } else if (this.fragment != null) {
              fragmentTransaction.detach(fragment);
            }
         }
    
         public void onTabReselected(Tab tab, FragmentTransaction fragmentTransaction) {
            // This tab is already selected
         }
    

    The above implementation also allows replacement of Fragments within a tab, based on their tags. For this purpose when switching fragments within the same tab I use the same Tag name, that was used for the initial framework that's been added to the tab.

    0 讨论(0)
  • 2020-12-24 08:49

    I've resolved using onSaveInstanceState and onRestoreInstanceState in the Activity to maintain the selected tab and modifying onTabSelected as follows.

    The last modify avoids that Android rebuild the Fragment it doesn't destoy. However I don't understand why the Activity is destroyed by the rotation event while the current Fragment no. (Have you some idea about this?)

    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);
            }
        }
    
    0 讨论(0)
  • 2020-12-24 08:57

    Since I use a android.support.v4.view.ViewPager overriding onTabSelected would not help. But still you hint pointed me in the right direction.

    The android.support.v4.app.FragmentManager saves all fragments in the onSaveInstanceState of the android.support.v4.app.FragmentActivity. Ignoring setRetainInstance — Depending on your design this might lead to duplicate fragments.

    The simplest solution is to delete the saved fragment in the orCreate of the activity:

       @Override
       public void onCreate (final android.os.Bundle savedInstanceState)
       {
          if (savedInstanceState != null)
          {
             savedInstanceState.remove ("android:support:fragments");
          } // if
    
          super.onCreate (savedInstanceState);
    …
          return;
       } // onCreate
    
    0 讨论(0)
提交回复
热议问题