2013-04-28 7 views
10

Ho questo strano problema in cui il mio frammento di lista viene creato due volte, una volta quando il super.oncreate viene chiamato sull'attività padre e una volta quando viene chiamato setContentView sulla stessa attività principale. È una semplice applicazione in cui utilizzo layout diversi per orientamento verticale e orizzontale.frammento Android creato due volte sul cambiamento di orientamento

Qui è l'attività principale:

private HeadlinesFragment headlines; 

@Override 
public void onCreate(Bundle savedInstanceState) { 
    Log.w("MainActivity", "Before super.onCreate: " + this.toString()); 
    super.onCreate(savedInstanceState); 
    Log.w("MainActivity", "Before setContentView: " + this.toString()); 
    setContentView(R.layout.news_articles); 

    //check to see if its portrait 
    if (findViewById(R.id.fragment_container) != null) { 
     if(getSupportFragmentManager().findFragmentById(R.id.fragment_container) == null) { 
      headlines = new HeadlinesFragment(); 
      getSupportFragmentManager().beginTransaction().replace(R.id.fragment_container, headlines).commit(); 
     } 
    } 
} 

Ecco l'news_articles nella cartella di layout-terra:

<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android" 
android:orientation="horizontal" 
android:layout_width="match_parent" 
android:layout_height="match_parent"> 

<fragment android:name="com.example.android.fragments.HeadlinesFragment" 
      android:id="@+id/headlines_fragment" 
      android:layout_weight="1" 
      android:layout_width="0dp" 
      android:layout_height="match_parent" /> 

<fragment android:name="com.example.android.fragments.ArticleFragment" 
      android:id="@+id/article_fragment" 
      android:layout_weight="2" 
      android:layout_width="0dp" 
      android:layout_height="match_parent" /> 

Ecco l'news_articles nella cartella di layout (per l'orientamento verticale)

<FrameLayout xmlns:android="http://schemas.android.com/apk/res/android" 
android:id="@+id/fragment_container" 
android:layout_width="match_parent" 
android:layout_height="match_parent" /> 

qui è il thats headlinesfragment state create due volte

public class HeadlinesFragment extends ListFragment { 
OnHeadlineSelectedListener mCallback; 

// The container Activity must implement this interface so the frag can deliver messages 
public interface OnHeadlineSelectedListener { 
    /** Called by HeadlinesFragment when a list item is selected */ 
    public void onArticleSelected(int position); 
} 

@Override 
public void onCreate(Bundle savedInstanceState) { 
    super.onCreate(savedInstanceState); 
    Log.w("HeadlinesFragment", "inside onCreate: " + this.toString()); 

    // We need to use a different list item layout for devices older than Honeycomb 
    int layout = Build.VERSION.SDK_INT >= Build.VERSION_CODES.HONEYCOMB ? 
      android.R.layout.simple_list_item_activated_1 : android.R.layout.simple_list_item_1; 

    // Create an array adapter for the list view, using the Ipsum headlines array 
    setListAdapter(new ArrayAdapter<String>(getActivity(), layout, Ipsum.Headlines)); 
} 

@Override 
public void onStart() { 
    super.onStart(); 

    // When in landscape layout, set the listview to highlight the selected list item 
    // (We do this during onStart because at the point the listview is available.) 
    if (getFragmentManager().findFragmentById(R.id.article_fragment) != null) { 
     getListView().setChoiceMode(ListView.CHOICE_MODE_SINGLE); 
    } 
} 

@Override 
public void onAttach(Activity activity) { 
    super.onAttach(activity); 

    // This makes sure that the container activity has implemented 
    // the callback interface. If not, it throws an exception. 
    try { 
     mCallback = (OnHeadlineSelectedListener) activity; 
    } catch (ClassCastException e) { 
     throw new ClassCastException(activity.toString() 
       + " must implement OnHeadlineSelectedListener"); 
    } 
} 


@Override 
public void onCreateOptionsMenu(Menu menu, MenuInflater inflater) { 
    // TODO Auto-generated method stub 
    super.onCreateOptionsMenu(menu, inflater); 
} 

@Override 
public void onDestroy() { 
    Log.w("HeadlinesFragment", "inside onDestroy: " + this.toString()); 
    super.onDestroy(); 
} 
} 

Ecco l'articlefragment

public class ArticleFragment extends Fragment { 
final static String ARG_POSITION = "position"; 
int mCurrentPosition = 0; 

@Override 
public View onCreateView(LayoutInflater inflater, ViewGroup container, 
    Bundle savedInstanceState) { 
    Log.w("ArticleFragment", "inside onCreateView: " + this.toString()); 

    if (savedInstanceState != null) { 
     mCurrentPosition = savedInstanceState.getInt(ARG_POSITION); 
    } 

    // Inflate the layout for this fragment 
    View view = inflater.inflate(R.layout.article_view, container, false); 
    return view; 
} 

@Override 
public void onStart() { 
    super.onStart(); 
    Bundle args = getArguments(); 
    if (args != null) { 
     // Set article based on argument passed in 
     updateArticleView(args.getInt(ARG_POSITION)); 
    } else if (mCurrentPosition != -1) { 
     // Set article based on saved instance state defined during onCreateView 
     updateArticleView(mCurrentPosition); 
    } 
} 

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

    // Save the current article selection in case we need to recreate the fragment 
    outState.putInt(ARG_POSITION, mCurrentPosition); 
} 

@Override 
public void onDestroy() { 
    Log.w("ArticleFragment", "inside onDestroy: " + this.toString()); 
    super.onDestroy(); 
} 

}

Il problema in dettaglio è questo:

1) avviare l'applicazione in orientamento verticale 2) viene chiamato setContentView e caricato news_articles ma è quello con il frag ment_container. Viene creato 3) headlinesfragment // finora comportamento normale 4) modifica orientamento orizzontale 5) mainActivity viene distrutto -> headlinefragment viene distrutto 6) super.onCreate su mainactivity è chiamato 7) Headlinefragment viene creato 8) setContentView su mainactivity è chiamato 9) viene creato un altro headlinefragment // problema

ho inserito i registri come si può vedere nel codice sopra e qui è l'uscita quando avvio l'app in modalità verticale e passo in orizzontale.

W/MainActivity(6925): Before super.onCreate: [email protected]8 
W/MainActivity(6925): Before setContentView: [email protected] 
W/HeadlinesFragment(6925): inside onCreate: HeadlinesFragment{41d8d4d8 #0 id=0x7f050001} 
W/MainActivity(6925): inside onDestroy: [email protected] 
W/HeadlinesFragment(6925): inside onDestroy: HeadlinesFragment{41d8d4d8 # 0id=0x7f050001} 
W/MainActivity(6925): Before super.onCreate: [email protected] 
W/HeadlinesFragment(6925): inside onCreate: HeadlinesFragment{41ea7290 #0 id=0x7f050001} 
W/MainActivity(6925): Before setContentView: [email protected] 
W/HeadlinesFragment(6925): inside onCreate: HeadlinesFragment{41eb1f30 #1 id=0x7f050002} 
W/ArticleFragment(6925): inside onCreateView: ArticleFragment{41eb5f20 #2 id=0x7f050003} 

Spero di essere stato chiaro con il mio codice e tronchi, mi sembra super.onCreate e setContentView sia creare un un headlinesfragment ciascuno; almeno penso.

la mia domanda è perché vengono create 2 istanze di frammenti di intestazioni e come posso evitare una situazione del genere.

molte grazie per qualsiasi aiuto per quanto riguarda questo

risposta

15

Nel onCreate della vostra attività, è possibile controllare lo stato della vostra savedInstanceState fascio. Se non è nullo, significa che si è verificata una modifica alla configurazione (nel tuo caso, una modifica dell'orientamento dello schermo) e che non è necessario ricreare il tuo Fragment.

Un altro errore che stavi facendo era che stavi cercando di recuperare il tuo Fragment con lo findFragmentById. Invece di passare l'id del frammento, gli stai dando l'id della vista allegata al frammento, che è diverso (e questo è il motivo per cui suppongo che questo restituisca sempre null).

Una corretta applicazione sarebbe più simile a questo (questo è il tuo Activity):

//check to see if its portrait 
    if (findViewById(R.id.fragment_container) != null) { 
     if(savedInstanceState == null) { 
      headlines = new HeadlinesFragment(); 
      getSupportFragmentManager().beginTransaction().replace(R.id.fragment_container, headlines, FRAGMENT_TAG_STRING).commit(); // Use tags, it's simpler to deal with 
     } else { 
      headlines = getSupportFragmentManager().findFragmentByTag(FRAGMENT_TAG_STRING); 
     } 
    } 
+0

Grazie per la risposta, tuttavia, il problema è quando l'applicazione si trasforma in orientamento orizzontale, quindi il codice che hai dato non viene comunque eseguito. Riguardo anche al codice che hai dato, quando il primato dell'applicazione inizia in modalità verticale, l'istanza savedState è nullo e il blocco else viene eseguito così come possiamo trovare un HeadlinesFragment per tag se non dovessimo mai crearlo in primo luogo? –

+3

Penso che la condizione dell'istruzione if sia indietro qui. Se c'è uno stato di istanza salvato, dovresti trovare il frammento esistente. Se lo stato dell'istanza salvato è nullo, è necessario creare un nuovo frammento. – Razz

+0

@Razz è corretto. NOTA: se savedInstanceState è null, si crea un nuovo frammento, altrimenti il ​​frammento esiste già! Quindi, basta cambiare l'istruzione if interna a if (savedInstanceState == null) –

2

Override onSavedInstanceState senza chiamare è super.

+3

Puoi espandere la tua risposta, spiegare il tuo ragionamento e ci sono anche insidie ​​nel non chiamare il super in questo caso. – CurlyPaul

+0

Genius, l'unica soluzione funzionante per me. :) – Galya