2013-04-28 4 views
10

У меня возникла эта странная проблема, когда фрагмент списка создается дважды, один раз, когда super.oncreate вызывается в родительской активности и один раз, когда setContentView вызывается с тем же родительским действием. Это простое приложение, в котором я использую разные макеты для портретной и альбомной ориентации.андроидный фрагмент, созданный дважды при изменении ориентации

Вот основной вид деятельности:

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

здесь является news_articles в папке раскладка-суша:

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

здесь является news_articles в папке макета (для портретной ориентации)

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

здесь является headlinesfragment то будет создан дважды

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

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

}

Проблема в деталях заключается в следующем:

1) запустить приложение в портретная ориентация 2) вызывается setContentView и загружается news_articles, а его - с фрагментом ment_container. 3) headlinesfragment создается // до сих пор нормальное поведение ориентации 4) изменения в ландшафт 5) mainActivity разрушается -> headlinefragment разрушается 6) super.oncreate на mainactivity называется 7) Headlinefragment создается 8) setcontentview на mainactivity называется 9) создается другой headlinefragment // проблема

Я разместил журналы, как показано в приведенном выше коде, и вот результат, когда я запускаю приложение в портретном режиме, и я перехожу к пейзажу.

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} 

Я надеюсь, что я был ясно с моим кодом и журналы, мне кажется super.oncreate и setcontentview как создать в headlinesfragment каждый; по крайней мере, я думаю.

мой вопрос, почему 2 экземпляра headlinesфрагментов создаются и как я могу избежать такой ситуации.

большое спасибо за любую помощь в отношении этого

ответ

15

В onCreate вашей деятельности, вы можете проверить состояние вашего savedInstanceState расслоения. Если это не null, это означает, что произошло изменение конфигурации (в вашем случае, изменение ориентации экрана), и вам не нужно воссоздавать Fragment.

Другая ошибка, которую вы делали, заключалась в том, что вы пытались восстановить Fragment с помощью findFragmentById. Вместо того, чтобы передавать идентификатор фрагмента, вы указываете ему идентификатор представления, прикрепленного к фрагменту, который отличается (и именно поэтому я предполагаю, что это всегда возвращало значение null).

Правильная реализация будет больше, как это (это ваш 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

Спасибо за ваш ответ, однако проблема в том, что приложение обращается к альбомной ориентации, поэтому код, который вы указали, не выполнен в любом случае. Также в отношении кода, который вы указали, когда приложение запускается в портретном режиме, файл savedInstanceState равен null, а блок else выполняется так, как мы можем найти тег HeadlinesFragment по тегам, если мы никогда не создадим его в первую очередь? –

+3

Я думаю, что условие if if здесь назад. Если есть состояние сохраненного экземпляра, вы должны найти существующий фрагмент. Если сохраненное состояние экземпляра равно NULL, вы должны создать новый фрагмент. – Razz

+0

@Razz правильный. ПРИМЕЧАНИЕ: если savedInstanceState IS null, то вы создаете новый фрагмент, иначе фрагмент уже существует! Итак, просто измените внутренний оператор if на if (savedInstanceState == null) –

2

Override onSavedInstanceState без вызова это супер.

+3

Можете ли вы расширить свой ответ, объяснить свои рассуждения, и есть также подводные камни, не вызывающие супер в этом случае. – CurlyPaul

+0

Genius, единственное работающее решение для меня. :) – Galya

Смежные вопросы