Андроидный фрагмент, созданный дважды при изменении ориентации

У меня есть эта странная проблема, когда мой фрагмент списка создается дважды, один раз, когда 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 в папке 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" />

здесь находится 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" />

вот заголовок, который был создан дважды

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

здесь находится фрагмент статьи

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, но тот, у которого есть файл fragment_container. 3) создается заголовок fragment//до сих пор нормальное поведение 4) изменить ориентацию на пейзаж 5) mainActivity уничтожен → разбит заголовок 6) суперконвертироваться на mainactivity называется 7) Создан Headlinefragment 8) setcontentview on mainactivity называется 9) создается другой заголовок fragment//проблема

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

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 создают как один заголовок, так и каждый из них; по крайней мере, я думаю.

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

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

Ответ 1

В 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 simpler to deal with
        } else {
            headlines = getSupportFragmentManager().findFragmentByTag(FRAGMENT_TAG_STRING);
        } 
   }

Ответ 2

Переопределить onSavedInstanceState, не вызывая его супер.