2013-04-28 29 views
10

Mam ten dziwny problem, w którym mój fragment listy jest tworzony dwa razy, jeden raz, gdy super.oncreate jest wywoływany w macierzystej aktywności i raz, gdy setContentView jest wywoływana na tej samej nadrzędnej aktywności. Jest to prosta aplikacja, w której używam innego układu do orientacji pionowej i poziomej.Fragment android utworzony dwa razy na zmianę orientacji

Oto Główna działalność:

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

tutaj jest news_articles w folderze layout-land:

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

tutaj jest news_articles w folderze układu (dla orientacji pionowej)

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

tutaj jest ów headlinesfragment zostały utworzone dwa

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

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

}

Problem szczegółowo to:

1) rozpoczyna się na stosowanie w orientacja portretowa 2) wywoływana jest funkcja setContentView, a element news_articles jest załadowany, ale jest to element zawierający fragment ment_container. 3) headlinesfragment jest tworzony // dotąd normalne zachowanie orientację 4) do zmiany krajobrazu 5) główną działalność jest zniszczona -> headlinefragment jest zniszczona 6) super.oncreate na główną działalność nazywa 7) Headlinefragment jest tworzony 8) setcontentview na główną działalność nazywa 9) inny headlinefragment jest tworzony // problem

umieściłem dzienniki jak widać w powyższym kodzie i tu jest wyjście kiedy uruchomić aplikację w trybie pionowym i zmieni się krajobraz.

W/MainActivity(6925): Before super.onCreate: [email protected] 
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} 

Mam nadzieję, że było jasne, z mojego kodu i dzienników, wydaje mi super.oncreate i setcontentview zarówno tworzenia się headlinesfragment każda; przynajmniej myślę.

Moje pytanie brzmi: dlaczego powstają 2 egzemplarze nagłówków i jak mogę uniknąć takiej sytuacji.

wielkie dzięki za wszelką pomoc dotyczącą tego

Odpowiedz

15

W onCreate swojej działalności, można sprawdzić stan swojego savedInstanceState wiązki. Jeśli nie jest pusta, oznacza to zmianę konfiguracji (w twoim przypadku zmiana orientacji ekranu) i nie musisz odtwarzać swojego Fragment.

Innym błędem, który robisz, było to, że próbujesz odzyskać swoją Fragment z findFragmentById. Zamiast podania identyfikatora Fragment podajesz mu identyfikator widoku dołączonego do Fragmentu, który jest inny (i to jest powód, dla którego zgaduję, że zawsze zwracał wartość null).

Prawidłowa realizacja będzie więcej tak (jest to Twoja 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

Dziękuję za odpowiedź, ale problem pojawia się, gdy aplikacja zmienia orientację poziomą, tak więc kod, który podałeś, nie jest wykonywany. Również w odniesieniu do kodu, który podałeś, kiedy aplikacja uruchamia się najpierw w trybie pionowym, zapisana wartośćInstanceState ma wartość null, a blok else jest wykonywany, więc w jaki sposób możemy znaleźć nagłówek HeadlinesFragment po tagu, jeśli nigdy nie stworzyliśmy go w pierwszej kolejności? –

+3

Myślę, że warunek instrukcji if jest tutaj wstecz. Jeśli istnieje zapisany stan wystąpienia, powinieneś znaleźć istniejący fragment. Jeśli zapisany stan wystąpienia ma wartość NULL, powinieneś utworzyć nowy fragment. – Razz

+0

@Razz jest poprawny. UWAGA: jeśli savedInstanceState IS jest null, tworzysz nowy fragment, w przeciwnym razie fragment już istnieje! Więc, zmień wewnętrzną instrukcję if na if (savedInstanceState == null) –

2

Zastąp onSavedInstanceState bez zawijania to jest super.

+3

Możesz rozwinąć swoją odpowiedź, wyjaśnij swoje rozumowanie i są też pułapki, nie wołając super w tym przypadku. – CurlyPaul

+0

Geniusz, jedyne działające rozwiązanie dla mnie. :) – Galya

Powiązane problemy