简体   繁体   English

Android Fragment的视图在“活动娱乐”中重复

[英]Android Fragment's view is duplicated on Activity Recreation

When an activity spawns a fragment and is later recreated (eg. by rotating the screen), the view associated with the fragment is duplicated with only one being destroyed when the fragment is later destroyed. 当一个活动产生一个片段并随后重新创建(例如,通过旋转屏幕)时,与片段相关联的视图将被复制,而当片段后来被销毁时,只有一个被销毁。

This happens if and only if the activity calls super.onSaveInstanceState either directly in its override of onSaveInstanceState or by simply not overriding the callback. 当且仅当该活动直接在其onSaveInstanceState的覆盖中调用super.onSaveInstanceState或仅不覆盖该回调时,才发生这种情况。

minimum code to reproduce: MainActivity.java: 要复制的最小代码:MainActivity.java:

package com.example.trevor.test;
import android.app.Activity;
import android.os.Bundle;
import android.util.Log;
import android.widget.CheckBox;
import android.widget.CompoundButton;

/**
 * Created by trevor on 11/11/16.
 */

public class MainActivity extends Activity {
    MainFragment fragment = new MainFragment();
    @Override
    public void onCreate(Bundle savedInstanceState)
    {
        super.onCreate(savedInstanceState);
        setContentView(R.layout.activity_main);
        CheckBox checkbox = (CheckBox)findViewById(R.id.checkBox);
        checkbox.setOnCheckedChangeListener(new CompoundButton.OnCheckedChangeListener() {
            @Override
            public void onCheckedChanged(CompoundButton compoundButton, boolean b) {
                if(b)
                {
                    getFragmentManager().beginTransaction().add(R.id.container,fragment).commit();
                }
                else
                {
                    getFragmentManager().beginTransaction().remove(fragment).commit();
                }
            }
        });
    }
}

MainFragment.java: MainFragment.java:

package com.example.trevor.test;

import android.app.Fragment;
import android.os.Bundle;
import android.view.LayoutInflater;
import android.view.View;
import android.view.ViewGroup;

/**
 * Created by trevor on 11/11/16.
 */

public class MainFragment extends Fragment {
    @Override
    public View onCreateView(LayoutInflater inflater, ViewGroup container, Bundle savedInstanceState)
    {
        return inflater.inflate(R.layout.fragment_main,container,false);
    }
}

activity_main.xml: activity_main.xml:

<?xml version="1.0" encoding="utf-8"?>
<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
    android:orientation="vertical" android:layout_width="match_parent"
    android:layout_height="match_parent">

    <CheckBox
        android:text="CheckBox"
        android:layout_width="match_parent"
        android:layout_height="wrap_content"
        android:id="@+id/checkBox" />

    <FrameLayout
        android:layout_width="match_parent"
        android:layout_height="match_parent"
        android:id="@+id/container">

    </FrameLayout>
</LinearLayout>

fragment_main.xml: fragment_main.xml:

<?xml version="1.0" encoding="utf-8"?>
<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
    android:orientation="vertical" android:layout_width="match_parent"
    android:layout_height="match_parent">

    <TextView
        android:text="Open"
        android:layout_width="match_parent"
        android:layout_height="wrap_content"
        android:id="@+id/textView" />
</LinearLayout>

AndroidManifest.xml: AndroidManifest.xml:

<manifest xmlns:android="http://schemas.android.com/apk/res/android"
    package="com.example.trevor.test">

    <application
        android:allowBackup="true"
        android:icon="@mipmap/ic_launcher"
        android:label="@string/app_name"
        android:supportsRtl="true"
        android:theme="@style/AppTheme">
        <activity android:name=".MainActivity">
            <intent-filter>
                <action android:name="android.intent.action.MAIN"/>
                <category android:name="android.intent.category.LAUNCHER"/>
            </intent-filter>
        </activity>

    </application>

</manifest>

Expected behavior: checking the box causes the word "open" to appear below. 预期的行为:选中该框会使单词“ open”出现在下面。 unchecking causes the word to dissapear. 取消选中会导致单词消失。

Actual behavour: checking the box causes the word "open" to appear below. 实际行为:选中此框会使单词“ open”出现在下面。 if the screen is then rotated, the word "open" becomes darker and unchecking the box causes the word to become its normal shade. 如果随后旋转屏幕,则“打开”一词会变暗,并且取消选中该框将使该词变成其常规阴影。

The Fragment you're adding initially is being restored automatically when the Activity is recreated. 重新创建Activity时,将自动还原最初添加的Fragment That's standard behavior for Fragment s. 这是Fragment的标准行为。 Additionally, the CheckBox 's checked state is being restored after the Activity recreation, so its onCheckedChanged() method is firing again, and loading another instance of the Fragment . 此外,在Activity重新创建后,将恢复CheckBox的选中状态,因此其onCheckedChanged()方法将再次触发,并加载Fragment另一个实例。 If you were to continue to change the orientation with the CheckBox checked, more and more Fragment instances would just keep piling up. 如果要在选中CheckBox情况下继续更改方向,则越来越多的Fragment实例将不断堆积。 You need to check if a Fragment instance already exists before adding one. 您需要在添加实例之前检查Fragment实例是否已经存在。

Since the Fragment is going to be re-added automatically, adding and removing it in the OnCheckedChangeListener is going to be cumbersome, as you'd first need to check if it's attached to the FragmentManager , and then determine if it's showing. 由于Fragment将自动重新添加,因此在OnCheckedChangeListener添加和删​​除它会很麻烦,因为您首先需要检查它是否已连接到FragmentManager ,然后确定它是否显示。 It would probably be simpler to just hide() and show() it as needed, after ensuring that it's instantiated and added. 在确保实例化和添加之后,仅根据需要hide()show()可能会更简单。

For example: 例如:

fragment = (MainFragment) getFragmentManager().findFragmentById(R.id.container);

checkbox.setOnCheckedChangeListener(new CompoundButton.OnCheckedChangeListener() {
    @Override
    public void onCheckedChanged(CompoundButton compoundButton, boolean b) {
        if(b)
        {
            if(fragment == null) {
                fragment = new MainFragment();
                getFragmentManager().beginTransaction().add(R.id.container, fragment).commit();
            }
            else {
                getFragmentManager().beginTransaction().show(fragment).commit();
            }
        }
        else
        {
            if (fragment != null) {
                getFragmentManager().beginTransaction().hide(fragment).commit();
            }
        }
    }
});

You can then remove the initialization from MainFragment 's declaration. 然后,您可以从MainFragment的声明中删除初始化。

MainFragment fragment;

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM