I'm using a ScrollView, called fragmentView below, to host a custom Fragment inside of it, and it generally works fine. However, when I rotate my device into landscape mode and back, the app crashes saying
java.lang.IllegalStateException: ScrollView can host only one direct child
I add the Fragment when a Spinner item is clicked like this:
fragmentView.removeAllViews();
FragmentTransaction fragmentTransaction = getSupportFragmentManager().beginTransaction();
ConfigureFragment fragment = new ConfigureFragment();
fragment.setReferences(MainActivity.this, (Controller) spinner.getSelectedItem());
fragmentTransaction.add(fragmentView.getId(), fragment, "");
fragmentTransaction.commit();
I used removeAllViews to make sure that the ScrollView is empty before I add anything new. This is the layout file for my fragment:
<?xml version="1.0" encoding="utf-8"?>
<android.support.constraint.ConstraintLayout xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:app="http://schemas.android.com/apk/res-auto"
xmlns:tools="http://schemas.android.com/tools"
android:layout_width="match_parent"
android:layout_height="match_parent"
tools:context=".ConfigureFragment">
<TableLayout
android:layout_width="match_parent"
android:layout_height="match_parent"
android:stretchColumns="0">
<TableRow
android:layout_width="match_parent"
android:layout_height="match_parent">
<EditText
android:id="@+id/editTextName"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_weight="1"
android:ems="10"
android:gravity="center"
android:inputType="textPersonName"
android:padding="10dp" />
</TableRow>
<TableRow
android:layout_width="match_parent"
android:layout_height="match_parent">
<TextView
android:id="@+id/textView"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_weight="1"
android:gravity="center"
android:text="@string/esp_channel" />
</TableRow>
<TableRow
android:layout_width="match_parent"
android:layout_height="match_parent">
<SeekBar
android:id="@+id/seekBarRed"
android:layout_width="match_parent"
android:layout_height="match_parent"
android:max="@integer/esp_value_max"
android:progress="@integer/esp_value_min" />
<EditText
android:id="@+id/editTextRed"
android:layout_width="wrap_content"
android:layout_height="match_parent"
android:ems="10"
android:inputType="number"
android:padding="10dp" />
</TableRow>
<TableRow
android:layout_width="match_parent"
android:layout_height="match_parent">
<SeekBar
android:id="@+id/seekBarGreen"
android:layout_width="match_parent"
android:layout_height="match_parent"
android:max="@integer/esp_value_max"
android:progress="@integer/esp_value_min" />
<EditText
android:id="@+id/editTextGreen"
android:layout_width="wrap_content"
android:layout_height="match_parent"
android:ems="10"
android:inputType="number"
android:padding="10dp" />
</TableRow>
<TableRow
android:layout_width="match_parent"
android:layout_height="match_parent">
<SeekBar
android:id="@+id/seekBarBlue"
android:layout_width="wrap_content"
android:layout_height="match_parent"
android:max="@integer/esp_value_max"
android:progress="@integer/esp_value_min" />
<EditText
android:id="@+id/editTextBlue"
android:layout_width="wrap_content"
android:layout_height="match_parent"
android:ems="10"
android:inputType="number"
android:padding="10dp" />
</TableRow>
<TableRow
android:layout_width="match_parent"
android:layout_height="match_parent">
<Button
android:id="@+id/buttonRefresh"
android:layout_width="match_parent"
android:layout_height="match_parent"
android:layout_weight="1"
android:text="@string/esp_retrieve" />
</TableRow>
<TableRow
android:layout_width="match_parent"
android:layout_height="match_parent">
<TextView
android:id="@+id/textView2"
android:layout_width="wrap_content"
android:layout_height="match_parent"
android:layout_weight="1"
android:gravity="center"
android:text="@string/esp_connection" />
</TableRow>
<TableRow
android:layout_width="match_parent"
android:layout_height="match_parent">
<TextView
android:id="@+id/textView3"
android:layout_width="wrap_content"
android:layout_height="match_parent"
android:gravity="fill_vertical"
android:padding="10dp"
android:text="@string/esp_ip" />
<EditText
android:id="@+id/editTextIP"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:ems="10"
android:inputType="date"
android:padding="10dp" />
</TableRow>
<TableRow
android:layout_width="match_parent"
android:layout_height="match_parent">
<TextView
android:id="@+id/textView4"
android:layout_width="wrap_content"
android:layout_height="match_parent"
android:gravity="fill_vertical"
android:padding="10dp"
android:text="@string/esp_port" />
<EditText
android:id="@+id/editTextPort"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:ems="10"
android:inputType="number"
android:padding="10dp" />
</TableRow>
</TableLayout>
</android.support.constraint.ConstraintLayout>
Solution 1
Use replace()
instead of add()
for adding the fragment to container
When Activity is restarted due to orientation change, Activity is created once yet ConfigureFragment is created twice.
The reason is because system restores the activity and fragment automatically if app got killed by system. The scene happens in super.onCreate(). Therefore, the add fragment code above will add an extra fragment on top of the restored fragment which lead to the current situation. ScrollView cannot have more than one child!
Solution 2
If you are adding the fragment in onCreate() method of your activity, then check for savedInstanceState variable (Bundle object). It's not null when activity is recreated, then only add your fragment
if (savedInstanceState == null) {
FragmentTransaction fragmentTransaction = getSupportFragmentManager().beginTransaction();
ConfigureFragment fragment = new ConfigureFragment();
fragment.setReferences(MainActivity.this, (Controller) spinner.getSelectedItem());
fragmentTransaction.add(fragmentView.getId(), fragment, "");
fragmentTransaction.commit();
}