Domanda

Ho fatto un semplice Android di Attività con un ActionBar per passare tra i 2 frammenti.Tutto ok fino a quando ho ruotare il dispositivo.In fatti, quando ruoto ho 2 frammento di uno sopra l'altro:il precedente attiva e la prima.Perché?Se la rotazione distruggere e ricreare la mia attività, perché ottenere 2 frammenti?

Codice di esempio:

Attività

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);
        }
    }
}
È stato utile?

Soluzione

Ho risolto utilizzando onSaveInstanceState e onRestoreInstanceState nell'attività per mantenere la scheda selezionata e modificando onTabSelected come segue.

L'ultima modifica evita che Android ricostruisci il frammento che non destoy.Tuttavia non capisco perché l'attività è distrutta dall'evento di rotazione mentre il frammento corrente no.(Hai qualche idea di questo?)

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

Altri suggerimenti

Poiché uso un android.support.v4.view.ViewPagerTagCoDetagCodeTagCode onTabSelected non aiuterà.Ma ancora ti suggerisci nella giusta direzione.

Il android.support.v4.app.FragmentManager consente di risparmiare tutti i frammenti nel onSaveInstanceState del android.support.v4.app.FragmentActivity. Ignorando setRetainInstance - A seconda del disegno Questo potrebbe portare a frammenti duplicati.

La soluzione più semplice è di eliminare il frammento salvato nel orCreate dell'attività:

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

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

La soluzione non richiede davvero un sacco di lavoro, i seguenti passaggi assicurano che quando ruotando lo schermo, la selezione della scheda viene mantenuta.Mi sono imbattuto in frammenti sovrapposti, perché sullo schermo è stata selezionata la rotazione della mia prima scheda, non il secondo che è stato selezionato prima di ruotare lo schermo e quindi la prima scheda si è sovraplicata del contenuto della seconda scheda.

È così che la tua attività dovrebbe guardare (sto usando ActionBarsherlock ma la regolazione dovrebbe essere molto facile):

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

E questo è ciò che il mio actionbar.tablisener sembra (è una classe privata nell'attività di cui sopra):

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

L'implementazione di cui sopra consente anche la sostituzione dei frammenti all'interno di una scheda, in base ai loro tag.A tal fine quando si cambiano frammenti all'interno della stessa scheda utilizzo lo stesso nome tag, utilizzato per il quadro iniziale che è stato aggiunto alla scheda.

Grazie Martin e asclepix per le loro soluzioni.Ho 3 schede e la prima scheda contiene 2 frammenti, come questo:

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

Utilizzando onRestoreInstanceState, onSaveInstanceState e savedInstanceState.remove("android:support:fragments"); metodi e istruzione lavoro quasi bene, tranne se la tua scheda non è il primo e ruotare e fare clic sul primo, un chiaro display appare e solo per il secondo clic sulla prima scheda è venuto il diritto frammento di visualizzazione.Dopo il debug di codice che ho riconosciuto che il primo addTab chiama sempre un onTabSelected evento nella scheda ascoltatore, con un frammento add metodo e poi, quando il setSelectedNavigationItem viene chiamato da onRestoreInstanceState un detach viene eseguita sulla prima scheda e un add per altri una.Questo superflui add calling è fisso nella mia soluzione.

La mia attività

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

E la modifica scheda ascoltatore

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.
    }
}
Autorizzato sotto: CC-BY-SA insieme a attribuzione
Non affiliato a StackOverflow
scroll top