2011-12-12 19 views
101

Usando el paquete de compatibilidad para apuntar 2.2 usando Fragmentos.Fragmento de Android ciclo de vida sobre cambios de orientación

Después de recodificar una actividad para usar fragmentos en una aplicación no pude obtener los cambios de orientación/administración del estado funcionando, así que creé una pequeña aplicación de prueba con una sola FragmentActivity y un solo Fragment.

Los registros de los cambios de orientación son raros, con varias llamadas a los fragmentos OnCreateView.

Obviamente me falta algo, como separar el fragmento y volver a colocarlo en lugar de crear una nueva instancia, pero no puedo ver ninguna documentación que indique dónde me estoy equivocando.

¿Alguien puede arrojar algo de luz sobre lo que estoy haciendo mal aquí por favor? Gracias

El registro es el siguiente después de cambios de orientación.

Initial creation 
12-04 11:57:15.808: D/FragmentTest.FragmentTestActivity(3143): onCreate 
12-04 11:57:15.945: D/FragmentTest.FragmentOne(3143): OnCreateView 
12-04 11:57:16.081: D/FragmentTest.FragmentOne(3143): OnCreateView->SavedInstanceState null 


Orientation Change 1 
12-04 11:57:39.031: D/FragmentTest.FragmentOne(3143): onSaveInstanceState 
12-04 11:57:39.031: D/FragmentTest.FragmentTestActivity(3143): onCreate 
12-04 11:57:39.031: D/FragmentTest.FragmentOne(3143): OnCreateView 
12-04 11:57:39.031: D/FragmentTest.FragmentOne(3143): OnCreateView->SavedInstanceState not null 
12-04 11:57:39.031: D/FragmentTest.FragmentOne(3143): OnCreateView 
12-04 11:57:39.167: D/FragmentTest.FragmentOne(3143): OnCreateView->SavedInstanceState null 


Orientation Change 2 
12-04 11:58:32.162: D/FragmentTest.FragmentOne(3143): onSaveInstanceState 
12-04 11:58:32.162: D/FragmentTest.FragmentOne(3143): onSaveInstanceState 
12-04 11:58:32.361: D/FragmentTest.FragmentTestActivity(3143): onCreate 
12-04 11:58:32.361: D/FragmentTest.FragmentOne(3143): OnCreateView 
12-04 11:58:32.361: D/FragmentTest.FragmentOne(3143): OnCreateView->SavedInstanceState not null 
12-04 11:58:32.361: D/FragmentTest.FragmentOne(3143): OnCreateView 
12-04 11:58:32.361: D/FragmentTest.FragmentOne(3143): OnCreateView->SavedInstanceState not null 
12-04 11:58:32.498: D/FragmentTest.FragmentOne(3143): OnCreateView 
12-04 11:58:32.498: D/FragmentTest.FragmentOne(3143): OnCreateView->SavedInstanceState null 

Actividad Principal (FragmentActivity)

public class FragmentTestActivity extends FragmentActivity { 
/** Called when the activity is first created. */ 

private static final String TAG = "FragmentTest.FragmentTestActivity"; 


FragmentManager mFragmentManager; 

@Override 
public void onCreate(Bundle savedInstanceState) { 
    super.onCreate(savedInstanceState); 
    setContentView(R.layout.main); 

    Log.d(TAG, "onCreate"); 

    mFragmentManager = getSupportFragmentManager(); 
    FragmentTransaction fragmentTransaction = mFragmentManager.beginTransaction(); 

    FragmentOne fragment = new FragmentOne(); 

    fragmentTransaction.add(R.id.fragment_container, fragment); 
    fragmentTransaction.commit(); 
} 

y el fragmento

public class FragmentOne extends Fragment { 

private static final String TAG = "FragmentTest.FragmentOne"; 

EditText mEditText; 

@Override 
public View onCreateView(LayoutInflater inflater, ViewGroup container, 
     Bundle savedInstanceState) { 

    Log.d(TAG, "OnCreateView"); 

    View v = inflater.inflate(R.layout.fragmentonelayout, container, false); 

    // Retrieve the text editor, and restore the last saved state if needed. 
    mEditText = (EditText)v.findViewById(R.id.editText1); 

    if (savedInstanceState != null) { 

     Log.d(TAG, "OnCreateView->SavedInstanceState not null"); 

     mEditText.setText(savedInstanceState.getCharSequence("text")); 
    } 
    else { 
     Log.d(TAG,"OnCreateView->SavedInstanceState null"); 
    } 
    return v; 
} 

@Override 
public void onSaveInstanceState(Bundle outState) { 
    super.onSaveInstanceState(outState); 

    Log.d(TAG, "FragmentOne.onSaveInstanceState"); 

    // Remember the current text, to restore if we later restart. 
    outState.putCharSequence("text", mEditText.getText()); 
} 

Manifiesto

<uses-sdk android:minSdkVersion="8" /> 

<application 
    android:icon="@drawable/ic_launcher" 
    android:label="@string/app_name" > 
    <activity 
     android:label="@string/app_name" 
     android:name=".activities.FragmentTestActivity" 
     android:configChanges="orientation"> 
     <intent-filter > 
      <action android:name="android.intent.action.MAIN" /> 

      <category android:name="android.intent.category.LAUNCHER" /> 
     </intent-filter> 
    </activity> 
</application> 
+0

No sé si es la respuesta correcta, pero intente usar una etiqueta al agregar el fragmento, agregue (R.id.fragment_container, fragment, "MYTAG") o, en su defecto, reemplace (R.id.fragment_container, fragment , "MYTAG") – Jason

+2

haciendo algunas investigaciones. Cuando la actividad principal (FragmentTestActivity) se reinicia en el cambio de orientación y obtengo una nueva instancia de FragmentManager, realizo un FindFragmentByTag para ubicar el fragmento que aún existe, por lo que el fragmento se retiene durante la recreación de la actividad principal. Si encuentro el fragmento y no hago nada, se vuelve a mostrar con MainActivity de todos modos. – MartinS

Respuesta

165

Estas capas de sus Fragmentos una encima de la otra.

Cuando se produce un cambio de configuración, el Fragmento anterior se agrega a la nueva Actividad cuando se vuelve a crear. Este es un dolor masivo en la parte posterior la mayor parte del tiempo.

Puede detener la aparición de errores utilizando el mismo Fragmento en lugar de volver a crear uno nuevo. Basta con añadir este código:

if (savedInstanceState == null) { 
    // only create fragment if activity is started for the first time 
    mFragmentManager = getSupportFragmentManager(); 
    FragmentTransaction fragmentTransaction = mFragmentManager.beginTransaction(); 

    FragmentOne fragment = new FragmentOne(); 

    fragmentTransaction.add(R.id.fragment_container, fragment); 
    fragmentTransaction.commit(); 
} else {   
    // do nothing - fragment is recreated automatically 
} 

Se advierte sin embargo: se producirán problemas si intenta obtener acceso a vistas actividad desde el interior del fragmento como los ciclos de vida va a cambiar sutilmente. (Obtener vistas de una Actividad principal desde un Fragmento no es fácil).

+36

"Esto es un dolor masivo en la parte posterior la mayor parte del tiempo" (pulgares arriba) – rushinge

+1

¿Cómo se puede manejar el mismo escenario en caso de uso de ViewPage con FragmentStatePagerAdapter ... alguna sugerencia? – CoDe

+4

¿Hay alguna aseveración similar en la documentación oficial?¿No es esto una contradicción con lo que se afirma en la guía: '' cuando se destruye la actividad, también lo son todos los fragmentos ''? Dado que '" Cuando la orientación de la pantalla cambia, el sistema destruye y recrea la actividad [...] "'. – cYrus

5

Puede @Override FragmentActivity usando onSaveInstanceState(). Asegúrese de no llamar al super.onSaveInstanceState() en el método.

+1

Esto muy probablemente romperá el ciclo de vida de las actividades, presentando más problemas potenciales en este proceso que ya es bastante complicado. Mire en el código fuente de FragmentActivity: guarda los estados de todos los fragmentos allí. – Brian

+0

Tuve el problema de que tengo un conteo de adaptador diferente para una orientación diferente. Así que siempre tuve una situación extraña después de encender el dispositivo y deslizar algunas páginas, obtuve la anterior y la incorrecta. con el giro de SavedInstance funciona mejor sin pérdidas de memoria (utilicé setSavedEnabled (false) antes y terminé con grandes fugas de memoria en cada cambio de orientación) – Informatic0re

0

Siempre debemos tratar de evitar la excepción de nullpointer, por lo que debemos verificar primero en el método de la instancia de guardado para obtener información del paquete. por breve explicación para comprobar este blog link

public static class DetailsActivity extends Activity { 

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

     if (getResources().getConfiguration().orientation 
      == Configuration.ORIENTATION_LANDSCAPE) { 
      // If the screen is now in landscape mode, we can show the 
      // dialog in-line with the list so we don't need this activity. 
      finish(); 
      return; 
     } 

     if (savedInstanceState == null) { 
      // During initial setup, plug in the details fragment. 
      DetailsFragment details = new DetailsFragment(); 
      details.setArguments(getIntent().getExtras()); 
      getFragmentManager().beginTransaction().add(android.R.id.content, details).commit(); 
     } 
    } 
} 
78

Para citar this book, "para garantizar una experiencia de usuario consistente , Android persiste la disposición Fragmento y se asocia volver pila cuando un La actividad se reinicia debido a un cambio de configuración." (pag.124)

Y la manera de abordar esto es comprobar primero si la pila Fragmento de vuelta haya sido rellenada, y crear la nueva instancia fragmento sólo si no tiene:

@Override 
public void onCreate(Bundle savedInstanceState) { 

     ...  

    FragmentOne fragment = (FragmentOne) mFragmentManager.findFragmentById(R.id.fragment_container); 

    if (fragment == null) { 
     FragmentTransaction fragmentTransaction = mFragmentManager.beginTransaction(); 
     fragmentTransaction.add(R.id.fragment_container, new FragmentOne()); 
     fragmentTransaction.commit(); 
    } 
} 
+2

Probablemente me ha ahorrado mucho tiempo con este ... muchas gracias. Puede combinar esta respuesta con la de Graeme para obtener una solución perfecta para manejar cambios de configuración y fragmentos. – azpublic

+10

Esta es realmente la respuesta correcta, no la marcada. ¡Muchas gracias! –

+0

cómo se puede manejar el mismo escenario en el caso de la implementación del Fragmento de ViewPager. – CoDe

7

El onCreate() El método de su actividad se llama después de que la orientación cambie como lo ha visto. Por lo tanto, no ejecutes FragmentTransaction que agrega el Fragment después de que la orientación cambie en tu actividad.

@Override 
protected void onCreate(Bundle savedInstanceState) { 
    super.onCreate(savedInstanceState); 
    if (savedInstanceState==null) { 
     //do your stuff 
    } 
} 

Los fragmentos deben y no deben modificarse.

0

Si usted acaba de hacer un proyecto, entonces el director del proyecto dice que necesita para lograr la conmutación pantalla de función, pero que no quieren pantalla de cambio de carga diferente diseño (puede crear un diseño y un sistema de diseño de puertos.

Determinará automáticamente el estado de la pantalla, cargará el diseño correspondiente), debido a la necesidad de reiniciar la actividad o fragmento, la experiencia del usuario no es buena, no directamente en el cambio de pantalla, me refiero al ? Url = YgNfP-VHY-Nuldi7YHTfNet3AtLdN-w__O3z1wLOnzr3wDjYo7X7PYdNyhw8R24ZE22xiKnydni7R0r35s2fOLcHOiLGYT9Qh_fjqtytJki & wd = & eqid = f258719e0001f24000000004585a1082

La premisa es que su diseño utilizando el peso de la forma en que el diseño de la layout_weight, de la siguiente forma:

<LinearLayout 
Android:id= "@+id/toplayout" 
Android:layout_width= "match_parent" 
Android:layout_height= "match_parent" 
Android:layout_weight= "2" 
Android:orientation= "horizontal" > 

Así que mi el enfoque es, cuando se cambia de pantalla, no es necesario cargar un nuevo diseño del archivo de vista, modificar el diseño en onConfigurationChanged pesos dinámicos, los siguientes pasos: 1 primer conjunto: AndroidManifest.xml en el atributo de actividad: android: configChanges = "keyboardHidden | orientation | screenSize" Para evitar el cambio de pantalla, evite volver a cargar, para poder supervisar en onConfigurationChanged 2 reescribir la actividad o el fragmento en el método onConfigurationChanged.

@Override 
Public void onConfigurationChanged (Configuration newConfig) { 
    Super.onConfigurationChanged (newConfig); 
    SetContentView (R.layout.activity_main); 
    if (newConfig.orientation == Configuration.ORIENTATION_LANDSCAPE) { 
     //On the layout// weight adjustment 
     LinearLayout toplayout = (LinearLayout) findViewById (R.id.toplayout); 
     LinearLayout.LayoutParams LP = new LayoutParams(LinearLayout.LayoutParams.MATCH_PARENT, 0, 2.0f); 
     Toplayout.setLayoutParams (LP); 
     LinearLayout tradespace_layout = (LinearLayout) findViewById(R.id.tradespace_layout); 
     LinearLayout.LayoutParams LP3 = new LayoutParams(LinearLayout.LayoutParams.MATCH_PARENT, 0, 2.8f); 
     Tradespace_layout.setLayoutParams (LP3); 
    } 
    else if (newConfig.orientation == Configuration.ORIENTATION_PORTRAIT) 
    { 
     //On the layout// weight adjustment 
     LinearLayout toplayout = (LinearLayout) findViewById (R.id.toplayout); 
     LinearLayout.LayoutParams LP = new LayoutParams (LinearLayout.LayoutParams.MATCH_PARENT, 0, 2.8f); 
     Toplayout.setLayoutParams (LP); 
     LinearLayout tradespace_layout = (LinearLayout) findViewById (R.id.tradespace_layout); 
     LinearLayout.LayoutParams LP3 = new LayoutParams (LinearLayout.LayoutParams.MATCH_PARENT, 0, 2.0f); 
     Tradespace_layout.setLayoutParams (LP3); 
    } 
} 
0

El cambio de configuración, el marco creará una nueva instancia del fragmento para usted y añadirlo a la actividad. Así que en lugar de esto:

FragmentOne fragment = new FragmentOne(); 

fragmentTransaction.add(R.id.fragment_container, fragment); 

hacer esto:

if (mFragmentManager.findFragmentByTag(FRAG1_TAG) == null) { 
    FragmentOne fragment = new FragmentOne(); 

    fragmentTransaction.add(R.id.fragment_container, fragment, FRAG1_TAG); 
} 

Tenga en cuenta que el marco añade una nueva instancia de FragmentOne sobre el cambio de orientación a menos que llame setRetainInstance (verdadero), en cuyo caso se añadirá la vieja instancia de FragmentOne.

Cuestiones relacionadas