Fragment not re-added after orientation change - android

The Fragments in my app are not re-added to the activity's view when the device is rotated. If i understand the documentation (and similar questions here on SO) correctly, this should be handled be the fragment manager (without having to specify android:configChanges in my manifest).
My Activity looks like:
public class MainActivity extends SherlockFragmentActivity {
public void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_main);
if (savedInstanceState == null){
getSupportFragmentManager().beginTransaction()
.add(R.id.main_fragment_placeholder, new DashboardFragment(),"dashboard")
.commit();
}
}
}
And a Fragment looks like (omitted clicklisteners and so on for clarity):
public class DashboardFragment extends SherlockFragment {
public View onCreateView(LayoutInflater inflater, ViewGroup container,
Bundle savedInstanceState) {
View root = inflater.inflate(R.layout.fragment_dashboard, container, false);
}
}
The layout files look like (activity_main.xml):
<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
android:layout_width="fill_parent"
android:layout_height="fill_parent"
android:id="#+id/main.main">
<FrameLayout android:id="#+id/main_fragment_placeholder"
android:layout_height="fill_parent"
android:layout_weight="1"
android:layout_width="0px"
/>
</LinearLayout>
And fragment_layout.xml:
<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
android:id="#+id/main.wrapper"
android:layout_width="fill_parent"
android:layout_height="fill_parent">
<my.layout.DashboardLayout
android:layout_width="fill_parent"
android:layout_height="fill_parent"
android:id="#+id/main.dashboard">
<!-- some buttons here -->
</my.layout.DashboardLayout>
</LinearLayout>
However, when i rotate my device the screen always becomes blank.

Turns out I did a Stupid Thing. I overrode the onSaveInstanceState(...) method in my activity, without calling super.onSaveInstanceState(), which resulted in the fragments not being recreated. Posting the answer here in hopes that I can save somebody else the time!

Fragments usually get recreated on configuration change. If you don't wish this to happen, use
setRetainInstance(true); in the Fragment's constructor(s)
This will cause fragments to be retained during configuration change.
http://developer.android.com/reference/android/app/Fragment.html#setRetainInstance(boolean)

Related

Display preference fragments in a full view

I am using a preference fragment and replacing the fragment in the id of the root layout of the main xml file. So my problem is that the preferences are displaying alongside with the main activity xml elements.
I have seen in Android apps that the preferences open up separately I am not sure whether they do open up separately or just occupying the entire screen. For opening the preference fragment I am doing a normal fragment transaction(Note: I am not using the support library not sure if this has something to do with my problem but still mentioning it).
Here is what the problem is,
And so here is my code
sampleprefs.xml
<?xml version="1.0" encoding="utf-8"?>
<PreferenceScreen xmlns:android="http://schemas.android.com/apk/res/android">
<CheckBoxPreference
android:defaultValue="false"
android:title="Check box preference"
android:key="check_box_preference_1" />
<EditTextPreference
android:defaultValue="Default value"
android:selectAllOnFocus="true"
android:singleLine="true"
android:title="Edit text preference"
android:key="edit_text_preference_1" />
</PreferenceScreen>
Here is the main layout,
<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:tools="http://schemas.android.com/tools"
android:id="#+id/activity_main"
android:layout_width="match_parent"
android:layout_height="match_parent"
tools:context="com.random.preferenceexample.MainActivity">
<TextView
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:text="Hello World!"
android:id="#+id/textView" />
<Button
android:text="Button"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:id="#+id/button" />
</LinearLayout>
This is my Main Activity code where the fragment displays on the click of a button,
public class MainActivity extends Activity implements View.OnClickListener {
boolean result;
Button button;
#Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_main);
button = (Button)findViewById(R.id.button);
button.setOnClickListener(this);
}
#Override
public void onClick(View v) {
FragmentManager fm = getFragmentManager();
FragmentTransaction ft = fm.beginTransaction();
SettingsFragment sf = new SettingsFragment();
ft.add(R.id.activity_main,sf);
ft.commit();
}
This is the fragment class
public class SettingsFragment extends PreferenceFragment {
#Override
public void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
addPreferencesFromResource(R.xml.sampleprefs);
}
}
It looks the problem seems to be with the background transparency of Fragment. Fragments are transparent backgrounded by default because they can be used to paint only a small portion of the screen but if you want your Fragment to cover fullscreen then just add some background to it, it will work.
Moreover, it's good practice to use a root layout which supports views to be placed one above the other(for ex. FrameLayout) for FragmentTransactions.
Add these in your Fragment :
#Override
public View onCreateView(LayoutInflater inflater, ViewGroup container, Bundle savedInstanceState) {
View view = super.onCreateView(inflater, container, savedInstanceState);
view.setBackgroundColor(getResources().getColor(android.R.color.white));
return view;
}
Here is the working example

Android adding a Fragment in project for Facebook Login

So, I'm following the steps in the Android API to add login to my app. My app is already fairly developed, and of course I'm no to android, so I'm having issues at this point.
the step reads:
Then set up the button in your UI by adding it to a fragment and update your activity to use your fragment.
I've searched for how to add a fragment and anything about that, watched youtube videos, but can't find out exactly what this means. Can anyone dumb this down for me?
https://developers.facebook.com/docs/facebook-login/android
First, you need a hosting activity, extending AppCompatActivity that has a FrameLayout in its related layout file. Then you inflate the layout in the onCreate method and add the needed Fragment:
public class MainActivity extends AppCompatActivity {
#Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_main);
if (findViewById(R.id.fragment_placeholder) != null) {
if (savedInstanceState != null) {
return;
}
// we are extending AppCompatActivity, so we need supportFragmentManager
getSupportFragmentManager().beginTransaction()
.add(R.id.fragment_placeholder, new ContentFragment()).commit();
}
}
}
The according layout file activity_main.xml:
<?xml version="1.0" encoding="utf-8"?>
<FrameLayout xmlns:android="http://schemas.android.com/apk/res/android"
android:id="#+id/fragment_placeholder"
android:layout_width="match_parent"
android:layout_height="match_parent">
</FrameLayout>`
Now you need to define ContentFragment:
public class RecordFragment extends Fragment {
#Override
public View onCreateView(LayoutInflater inflater, ViewGroup container, Bundle savedInstanceState) {
super.onCreateView(inflater, container, savedInstanceState);
return inflater.inflate(R.layout.fragment_content, container, false);
}
}
The last step is the layout file for our fragment, fragment_content, containing a Button:
<?xml version="1.0" encoding="utf-8"?>
<RelativeLayout xmlns:android="http://schemas.android.com/apk/res/android"
android:layout_width="match_parent"
android:layout_height="match_parent">
<Button
android:id="#+id/button"
android:layout_width="match_parent"
android:layout_height="match_parent"/>
</RelativeLayout>
For more information why we use AppCompatActivity, read this answer.

having a condition to fragment adding

for the last 4 hours ive been trying to understand how to do it.
to be clear, i need to add a fragment under a certain condition and for my purpose the condition can be either:
a. if the parent's id matches to what i seek.
b. if the fragment's id matches to what i seek.
i tried to put the condition inside the fragment itself:
public class BlockFrag extends Fragment{
SharedPreferences sp;
#Override
public View onCreateView(LayoutInflater inflater, ViewGroup container, Bundle savedInstanceState)
{
sp = getActivity().getSharedPreferences("myOptions", Context.MODE_PRIVATE);
int id = container.getId();//didn't work(i think the container is null for some reason
switch (id)
{
default: return inflater.inflate(R.layout.nothing, container, false);
case (R.id.redFrame): {
if (sp.getBoolean("redBlock", true)) {
return inflater.inflate(R.layout.block_frag, container, false);
}
}
case (R.id.greenFrame): {
if (sp.getBoolean("greenBlock", true)) {
return inflater.inflate(R.layout.block_frag, container, false);
}
i also tried to get the condition programatically but i cant understand how to prevent the fragment to automatically generate when i call the layout it's placed.
this is the layout it's placed for example
<FrameLayout
android:layout_width="match_parent"
android:layout_height="match_parent"
android:id="#+id/redFrame"
android:layout_weight="0.33333">
<fragment
android:name="com.example.dor.myapplication.BlockFrag"
android:id="#+id/blockRed"
android:layout_width="match_parent"
android:layout_height="match_parent"
tools:layout="#layout/block_frag"/>
this is the fragment's layout if it matters...
<FrameLayout xmlns:android="http://schemas.android.com/apk/res/android"
android:layout_width="match_parent"
android:layout_height="match_parent"
android:layout_alignParentLeft="true"
android:layout_alignParentStart="true">
<ImageView
android:layout_width="118dp"
android:layout_height="match_parent"
android:id="#+id/blockFrag"
android:src="#drawable/block"
android:layout_weight="0.33333"
android:layout_above="#+id/linearLayout"
android:layout_alignParentLeft="true"
android:layout_alignParentStart="true"
android:layout_gravity="left"
android:scaleType="fitXY"/>
thank you for helping, any kind of way to do this will help me.
I did something similar earlier, I had to decide based on a value from PickHeaderFragment which Fragment to show inside my root_frame_body, here's how I achieved it:
My Parent or Host Fragment:
<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
android:orientation="vertical"
android:layout_width="match_parent"
android:layout_height="match_parent">
<FrameLayout xmlns:android="http://schemas.android.com/apk/res/android"
android:layout_width="match_parent"
android:background="#000"
android:layout_height="wrap_content"
android:id="#+id/root_frame_header" />
<FrameLayout xmlns:android="http://schemas.android.com/apk/res/android"
android:layout_width="match_parent"
android:background="#000"
android:layout_weight="1"
android:layout_height="match_parent"
android:id="#+id/root_frame_body" />
</LinearLayout>
Inside my Host Fragment:
#Override
public View onCreateView(LayoutInflater inflater, ViewGroup container, Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
View rootView = inflater.inflate(R.layout.fragment_pick, container, false);
headerFragment = PickHeaderFragment.newInstance("");
getChildFragmentManager().beginTransaction().replace(R.id.root_frame_header, headerFragment).commit();
}
My PickHeaderFragment has an OnPickHeaderFragmentInteractionListener Interface implemented which looks like this:
public interface OnPickHeaderFragmentInteractionListener {
public void onFragmentInteraction(int res);
}
I ensure my Host Fragment will implement this interface by adding it during onAttach (inside PickHeaderFragment) like so:
#Override
public void onAttach(Activity activity) {
super.onAttach(activity);
try {
Fragment f = getParentFragment();
mListener = (OnPickHeaderFragmentInteractionListener) getParentFragment();
} catch (ClassCastException e) {
throw new ClassCastException(activity.toString() + " must implement OnFragmentInteractionListener");
}
}
Now, when the enter Button inside my PickHeaderFragment is clicked I call this event to let my Host Fragment know to change the root_frame_body:
Inside my Host Fragment I have:
#Override
public void onFragmentInteraction(int res) {
if (res == R.id.btnEnter) {
getChildFragmentManager().beginTransaction().replace(R.id.root_frame_body, new PickByItemBodyFragment()).commit();;
}
}
I am now able to change the layout (or which Fragment is displayed) in my Host Fragment after an event occurs inside a child Fragment.
Note: In my example I have nested Fragments, but you could just as easily have several Fragments within an Activity.
In summary I would recommend you follow a similar pattern of having a host Activity or Fragment and ensuring you have a FrameLayout added to your view to allow you to inflate whatever Fragment you need.
An added benefit to doing it this way is that you can segregate your code into separate Fragments also.
Leigh thank you for your answer but i don't know if you didn't understand my question or i didn't understand your answer...
anyway, i solved my problem through programatically adding the fragment in r=the onCreate of the activity and inserting the adding to an "if"
BlockFrag rb = new BlockFrag();
FragmentManager fragmentManager = getFragmentManager ();//declaring Fragment Manager
FragmentTransaction fragmentTransaction = fragmentManager.beginTransaction ();// declaring Fragment Transaction
if(condition)
{
editor.putBoolean("redBlock", true);
fragmentTransaction.add(R.id.redFrame, rb);//adding the fragment
}
needless to say i didn't declare the fragment in my activity layout and didn't perform any changes in the fragment class or fragment xml layout.

Android: FragmentTabHost - java.lang.IllegalArgumentException: you must specify a way to create the tab content

I have the following in a class for creating a FragmentTabHost:
public class TabsActivity extends FragmentActivity {
private FragmentTabHost mTabHost;
#Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.tabs);
mTabHost = (FragmentTabHost)findViewById(android.R.id.tabhost);
mTabHost.setup(this, getSupportFragmentManager(), R.id.realtabcontent);
TabHost.TabSpec exploreSpec = mTabHost.newTabSpec("explore").setIndicator("Explore", getResources().getDrawable(R.drawable.exploreicon));
mTabHost.addTab(exploreSpec);
}
}
I have a separate .xml file for setting up the host as shown in the android support site. This is all in a second activity. My primary activity has a group of images. Clicking one loads this activity. The app runs initially. As soon as I tap an image to load this activity though it crashes. Inside LogCat I found the following error:
java.lang.IllegalArgumentException: you must specify a way to create the tab content
I can't seem to find any thing on this error.
Without being able to see your xml file (R.layout.tabs) I'd say something is not set up quite right.
Instead of:
TabHost.TabSpec exploreSpec = mTabHost.newTabSpec("explore").setIndicator("Explore", getResources().getDrawable(R.drawable.exploreicon));
mTabHost.addTab(exploreSpec);
Try:
mTabHost.addTab(mTabHost.newTabSpec("explore").setIndicator("Explore", getResources().getDrawable(R.drawable.exploreicon)), TheAssociatedFragmentTab.class, null);
R.layout.tabs.xml
<?xml version="1.0" encoding="utf-8"?>
<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
android:layout_width="match_parent"
android:layout_height="match_parent"
android:orientation="vertical"
android:background="#ffffff" >
<android.support.v4.app.FragmentTabHost
android:id="#android:id/tabhost"
android:layout_width="match_parent"
android:layout_height="match_parent"
android:background="#ffffff" >
<FrameLayout
android:id="#+id/tabcontent"
android:layout_width="match_parent"
android:layout_height="match_parent" />
</android.support.v4.app.FragmentTabHost>
</LinearLayout>
Beyond that you will just need an ExplorerFragment class (extends fragment) which Overrides onCreateView and inflates the layout for your explorer tab like so..
ExplorerFragment.java
#Override
public View onCreateView(LayoutInflater inflater, ViewGroup container, Bundle savedInstanceState)
{
super.onCreateView(inflater, container, savedInstanceState);
return inflater.inflate(R.layout.explorer_fragment, container, false);
}
I didn't try to incorporate an image on my tabs, but this code works for me. Let me know if that helps.

"no view found for id for fragment" error before onCreateView() called

This is related to several posts, but I don't see anything that explains the order of view creation in the view hierarchy (static - xml and dynamic - in code).
I have a FragmentActivity hosting an Activity and a Fragment. I'm getting a runtime error from a view not being created for the fragment before the fragment's onCreateView is called. It's difficult to determine what the calling method is that can't find the view as the debugger can't seem to find the right line-numbers during step-through.
I can't get the source to attach correctly to see inside FragmentManager as there seems to be a mismatch between the dl'd source and .jar
I placed a Log.i entry in StreamingActivity.onCreateView and the error occurs before that.
Here is the relevant LogCat:
07-19 10:13:36.091: I/StreamingActivity(9886): onCreate
07-19 10:13:42.271: W/dalvikvm(9886): threadid=1: thread exiting with uncaught exception (group=0x40020560)
07-19 10:13:42.281: E/AndroidRuntime(9886): FATAL EXCEPTION: main
07-19 10:13:42.281: E/AndroidRuntime(9886): java.lang.RuntimeException: Unable to start activity
ComponentInfo{kesten.fragmentstestbed/kesten.fragmentstestbed.FragmentsMainActivity}:
java.lang.IllegalArgumentException: No view found for id 0x7f05002e for fragment
StreamingActivity{4052f810 #0 id=0x7f05002e}
caused by
java.lang.IllegalArgumentException: No view found for id 0x7f05002e for fragment
StreamingActivity{40530948 #0 id=0x7f05002e}
at android.support.v4.app.FragmentManagerImpl.moveToState(FragmentManager.java:865)
my java file snippets:
public class FragmentsMainActivity extends FragmentActivity {
public final static int STARTUP_ACTIVITY_RESULT=0;
#Override
public void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_fragments_main);
if(savedInstanceState == null) {
Intent intentStartupActivity = new Intent(this, StartupActivity.class);
if(intentStartupActivity != null)
startActivityForResult(intentStartupActivity, STARTUP_ACTIVITY_RESULT);
// get an instance of FragmentTransaction from your Activity
FragmentTransaction fragmentTransaction =
getSupportFragmentManager().beginTransaction();
//add a fragment
StreamingActivity streamingActivity = new StreamingActivity();
if (streamingActivity != null) {
fragmentTransaction.add(R.id.streamingactivity, streamingActivity);
fragmentTransaction.commit();
}
}
}
my Fragment:
public class StreamingActivity extends Fragment {
public void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
Log.i("StreamingActivity","onCreate")
}
my layout files:
"res/layout/main.xml"
<?xml version="1.0" encoding="utf-8"?>
<RelativeLayout xmlns:android="http://schemas.android.com/apk/res/android"
android:layout_width="fill_parent"
android:layout_height="fill_parent"
>
<FrameLayout
android:id="#+id/streamingactivity"
android:layout_width="0px"
android:layout_height="match_parent">
</FrameLayout>
<!-- IMU -->
<TextView
android:id="#+id/imu_label"
style="#style/Label.Title"
android:text="#string/imu"
/>
<kesten.fragmentstestbed.ImuView
android:id="#+id/imu_values"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_below="#+id/imu_label"
android:text=""
/>
<!-- Accelerometer -->
<TextView
android:id="#+id/accelerometer_label"
style="#style/Label.Title"
android:text="#string/accelerometer"
android:layout_below="#+id/imu_values"
/>
<!-- Filtered -->
<kesten.fragmentstestbed.CoordinateView
android:id="#+id/accelerometer_filtered_coordinates"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_below="#+id/accelerometer_label"
android:text="#string/filtered"
/>
</RelativeLayout>
and "activity_fragments_main.xml" for my FragmentActivity
<RelativeLayout xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:tools="http://schemas.android.com/tools"
android:layout_width="match_parent"
android:layout_height="match_parent" >
<TextView
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_centerHorizontal="true"
android:layout_centerVertical="true"
android:padding="#dimen/padding_medium"
android:text="#string/hello_world"
tools:context=".FragmentsMainActivity" />
</RelativeLayout>
Possible causes of id not found error gleaned from other Stackoverflow threads:
wrong layout in setContentView() of the onCreate() method of the FragmentActivity.
I checked and my xml files are all there. Maybe there's a syntax error or missing linked resource, but i can't see it. I understand the HierarchyViewer would be a useful tool to debug the UI, but i can't get it working.
the id passed into FragmentTransaction.add must be a child of the layout specified in setContentView.
my FragmentActivity's onCreate
#Override
public void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_fragments_main);
my Fragment's onCreateView
#Override
public View onCreateView(LayoutInflater inflater, ViewGroup container,
Bundle savedInstanceState) {
Log.i("StreamingActivity","onCreateView");
View view = inflater.inflate(R.layout.main, container, false);
I presume that the LayoutManager passes the correct value for container, i.e., the one set in FragmentActivity's setContentView(). But we never make it there anyway. The error in the logCat occurs before we enter onCreateView().
The only thing i can think of atm is that the startupActivity (which is closed third party) called after setContentView and before StreamingActivity sets the content view to something besides R.layout.activity_fragments_main which is not a parent of my fragment's view.
The problem seems to stem from the fact that while Activity has setContentView which can be called whenever, Fragment only has onCreateView() which gets called after fragment transactions that start the fragment.
Why and what is trying to access the fragment's view before onCreateView() is called?
darKoram
The TextView in activty_fragments_main has no id. I ran into a similar error, and added an id, and all of a sudden things started working...
the following FrameLayout should be in activty_fragments_main
<FrameLayout
android:id="#+id/streamingactivity"
android:layout_width="0px"
android:layout_height="match_parent">
</FrameLayout>
the below function adds a fragment inside the layout provided in the first aurgument:
fragmentTransaction.add(R.id.streamingactivity, streamingActivity);
So try changing:
<RelativeLayout xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:tools="http://schemas.android.com/tools"
android:layout_width="match_parent"
android:layout_height="match_parent" >
<TextView
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_centerHorizontal="true"
android:layout_centerVertical="true"
android:padding="#dimen/padding_medium"
android:text="#string/hello_world"
tools:context=".FragmentsMainActivity" />
</RelativeLayout>
to:
<RelativeLayout xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:tools="http://schemas.android.com/tools"
android:layout_width="match_parent"
android:layout_height="match_parent" >
<FrameLayout
android:id="#+id/streamingactivity"
android:layout_width="match_parent"
android:layout_height="match_parent">
</FrameLayout>
</RelativeLayout>
In FragmentMainActivity you are adding fragment to to id streamingactivity
fragmentTransaction.add(R.id.streamingactivity, streamingActivity);
and "activity_fragments_main.xml" for my FragmentMainActivity
<RelativeLayout xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:tools="http://schemas.android.com/tools"
android:layout_width="match_parent"
android:layout_height="match_parent" >
<TextView
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_centerHorizontal="true"
android:layout_centerVertical="true"
android:padding="#dimen/padding_medium"
android:text="#string/hello_world"
tools:context=".FragmentsMainActivity" />
</RelativeLayout>
//your layout must contains a FrameLayout with id streamingactivity .you Activity must contains
<FrameLayout
android:id="#+id/streamingactivity"
....
..>
And your StreamingActivity which is a Fragment must import android.support.v4.Fragment. And must implement onCreateView
#Override
public View onCreateView(LayoutInflater inflater, ViewGroup container,
Bundle savedInstanceState) {
return inflater.inflate(R.layout.main, container, false);
}
Or One more problem may be . Your have interchanged the LayoutId of Fragment and Activity. As your names look like

Categories

Resources