Reputation: 397
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.
minimum code to reproduce: 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:
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:
<?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:
<?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:
<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. unchecking causes the word to dissapear.
Actual behavour: checking the box causes the word "open" to appear below. if the screen is then rotated, the word "open" becomes darker and unchecking the box causes the word to become its normal shade.
Upvotes: 0
Views: 656
Reputation: 39191
The Fragment
you're adding initially is being restored automatically when the Activity
is recreated. That's standard behavior for Fragment
s. 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
. If you were to continue to change the orientation with the CheckBox
checked, more and more Fragment
instances would just keep piling up. You need to check if a Fragment
instance already exists before adding one.
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. It would probably be simpler to just hide()
and show()
it as needed, after ensuring that it's instantiated and added.
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 fragment;
Upvotes: 2