android片段在方向更改时创建了两次

Posted

技术标签:

【中文标题】android片段在方向更改时创建了两次【英文标题】:android fragment created twice on orientation change 【发布时间】:2013-04-22 08:12:46 【问题描述】:

我遇到了这个奇怪的问题,我的列表片段被创建了两次,一次是在父活动上调用 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();
        
    

这是 layout-land 文件夹中的 news_articles:

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

<fragment android:name="com.example.android.fragments.HeadlinesFragment"
          android:id="@+id/headlines_fragment"
          android:layout_weight="1"
          android:layout_
          android:layout_ />

<fragment android:name="com.example.android.fragments.ArticleFragment"
          android:id="@+id/article_fragment"
          android:layout_weight="2"
          android:layout_
          android:layout_ />

这是布局文件夹中的 news_articles(用于纵向)

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

这是两次创建的头条新闻片段

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) 头条新闻片段被创建 // 到目前为止的正常行为 4)将方向更改为横向 5)mainActivity被销毁->headlinefragment被销毁 6) 调用 mainactivity 上的 super.oncreate 7) 标题片段被创建 8) 调用 mainactivity 上的 setcontentview 9) 另一个标题片段被创建 //problem

我已将日志放在上面的代码中,这是当我以纵向模式启动应用程序并更改为横向时的输出。

W/MainActivity(6925): Before super.onCreate: MainActivity@41d81238
W/MainActivity(6925): Before setContentView: MainActivity@41d81238
W/HeadlinesFragment(6925): inside onCreate: HeadlinesFragment41d8d4d8 #0 id=0x7f050001
W/MainActivity(6925): inside onDestroy: MainActivity@41d81238
W/HeadlinesFragment(6925): inside onDestroy: HeadlinesFragment41d8d4d8 # 0id=0x7f050001
W/MainActivity(6925): Before super.onCreate: MainActivity@41ea6258
W/HeadlinesFragment(6925): inside onCreate: HeadlinesFragment41ea7290 #0 id=0x7f050001
W/MainActivity(6925): Before setContentView: MainActivity@41ea6258
W/HeadlinesFragment(6925): inside onCreate: HeadlinesFragment41eb1f30 #1 id=0x7f050002
W/ArticleFragment(6925): inside onCreateView: ArticleFragment41eb5f20 #2 id=0x7f050003

我希望我已经清楚我的代码和日志,在我看来 super.oncreate 和 setcontentview 都创建了一个标题片段;至少我认为。

我的问题是为什么要创建 2 个标题片段实例以及如何避免这种情况。

非常感谢您对此提供的任何帮助

【问题讨论】:

【参考方案1】:

在您的 Activity 的 onCreate 中,您可以检查您的 savedInstanceState 捆绑包的状态。如果它不为空,则表示发生了配置更改(在您的情况下,屏幕方向更改)并且您不需要重新创建 Fragment

您犯的另一个错误是您试图用findFragmentById 检索您的Fragment。而不是传递 Fragment id,而是给它附加到 Fragment 的 View 的 id,这是不同的(这就是我猜测它总是返回 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);
         
   

【讨论】:

感谢您的回复,但问题是当应用程序变为横向时,您提供的代码无论如何都不会执行。另外关于您给出的代码,当应用程序第一次以纵向模式启动时,savedInstanceState 为 null 并且执行 else 块,那么如果我们一开始就没有创建 HeadlinesFragment,我们如何通过标签找到它?跨度> 我认为这里的 if 语句条件是倒退的。如果有保存的实例状态,您应该找到现有的片段。如果保存的实例状态为空,你应该创建一个新的片段。 @Razz 是正确的。注意:如果 savedInstanceState 为 null 那么你创建一个新的片段,否则片段已经存在!因此,只需将内部 if 语句更改为 if(savedInstanceState == null)【参考方案2】:

覆盖 onSavedInstanceState 而不调用它是超级的。

【讨论】:

您能否扩展您的答案,解释您的推理,并且在这种情况下不调用 super 也存在缺陷。

以上是关于android片段在方向更改时创建了两次的主要内容,如果未能解决你的问题,请参考以下文章

Android片段生命周期:onResume调用了两次

Android Fragment 和 Activity 在方向更改时的行为

避免Android在方向更改时自动重新添加我的片段

Android:为啥 DialogFragment 在方向更改时返回空指针

如何防止在方向更改时重新创建片段寻呼机中的片段?

在片段中在运行时更改方向时更改布局而不重新创建视图