I grabbed the EU4You sample project demonstrated in The Busy Coder's Guide to Android Development 4.2 (courtesy of Mark Murphy aka commonsware here on StackOverflow). It may be easier to follow along with this sample project, but I will try to post relevant portions of code in the question. You do need to import the ActionBarSherlock project as well. You'll aslo need to set the Java compiler to 1.6 (to allow for the @Override annotation).
NOTE: I've changed the min-sdk to 8 (2.3, Froyo) and the target-sdk to 16 (4.1, Ice Cream Sandwich).
Note: I've changed the countries
framelayout id to left_pane
and the details
framelayout id to right_pane
.
Main layout file (layout-large-land):
<?xml version="1.0" encoding="utf-8"?>
<LinearLayout
xmlns:android="http://schemas.android.com/apk/res/android"
android:orientation="horizontal"
android:layout_width="fill_parent"
android:layout_height="fill_parent">
<FrameLayout
android:id="@+id/left_pane"
android:layout_weight="30"
android:layout_width="0px"
android:layout_height="fill_parent"
/>
<FrameLayout
android:id="@+id/right_pane"
android:layout_weight="70"
android:layout_width="0px"
android:layout_height="fill_parent"
/>
</LinearLayout>
Main layout file:
<?xml version="1.0" encoding="utf-8"?>
<FrameLayout xmlns:android="http://schemas.android.com/apk/res/android"
android:id="@+id/left_pane"
android:layout_width="fill_parent"
android:layout_height="fill_parent"
/>
The original version of the sample application has two activities (SherlockFragmentActivities to be exact).
- EU4You - Loads up the appropriate layout (default or landscape-large). If default, loads a list of countries in a SherlockListFragment. If landscape-large, loads up the list and a details fragment to display the selected country.
- DetailsActivity - Loads up the selected country via the DetailsFragment when not using the landscape-large layout.
If you click on a country in the list fragment, EU4You (the main hosting activity) checks for the existence and visibility of the DetailsFragment. If it exists, it loads up the country website inside that fragment. If not, it fires off the DetailsActivity, which then handles adding the DetailsFragment (which then loads the website for the given country).
This works fine, like a typical application supporting multiple screen layouts on tablets and handsets.
My problem is that I want to remove the DetailsActivity and stick to a single activity. I want to swap out (replace) the list fragment with the details fragment whenever I am not in the large landscape layout (in other words, I am either in a portrait layout on a tablet, or on a handset in any orientation). So what I have done, is changed the following in the EU4You SherlockFragmentActivity:
Original (with slight naming modification on the framelayout names mentioned in the note further up... countries is left_pane, details is right_pane):
@Override
public void onCountrySelected(Country c) {
String url=getString(c.url);
if (details != null && details.isVisible()) {
details.loadUrl(url);
}
else {
Intent i=new Intent(this, DetailsActivity.class);
i.putExtra(DetailsActivity.EXTRA_URL, url);
startActivity(i);
}
}
My modified single activity version:
@Override
public void onCountrySelected(Country c) {
String url=getString(c.url);
if (details != null && details.isVisible()) {
details.loadUrl(url);
}
else {
details = new DetailsFragment();
FragmentTransaction ft = getSupportFragmentManager().beginTransaction();
ft.replace(R.id.left_pane, details);
ft.addToBackStack(null);
ft.commit();
getSupportFragmentManager().executePendingTransactions();
details.loadUrl(url);
}
}
This works as expected. I click on a website on the list (in portrait mode on a tablet, or on a handset in any orientation) and the details show in the details fragment, which replaces the website list fragment. However, if I then click the home button on the device, while the country details are showing, I get a force close error, with the following logcat details (only with the default layout with just the single left_pane framelayout... I don't have this issue in landscape orientation on tablets, when there is a left_pane and right_pane):
11-16 15:04:33.525: E/AndroidRuntime(688): FATAL EXCEPTION: main
11-16 15:04:33.525: E/AndroidRuntime(688): java.lang.RuntimeException: Unable to pause activity {com.commonsware.android.eu4you/com.commonsware.android.eu4you.EU4You}: java.lang.IllegalStateException: Content view not yet created
11-16 15:04:33.525: E/AndroidRuntime(688): at android.app.ActivityThread.performPauseActivity(ActivityThread.java:3348)
11-16 15:04:33.525: E/AndroidRuntime(688): at android.app.ActivityThread.performPauseActivity(ActivityThread.java:3305)
11-16 15:04:33.525: E/AndroidRuntime(688): at android.app.ActivityThread.handlePauseActivity(ActivityThread.java:3288)
11-16 15:04:33.525: E/AndroidRuntime(688): at android.app.ActivityThread.access$2500(ActivityThread.java:125)
11-16 15:04:33.525: E/AndroidRuntime(688): at android.app.ActivityThread$H.handleMessage(ActivityThread.java:2040)
11-16 15:04:33.525: E/AndroidRuntime(688): at android.os.Handler.dispatchMessage(Handler.java:99)
11-16 15:04:33.525: E/AndroidRuntime(688): at android.os.Looper.loop(Looper.java:123)
11-16 15:04:33.525: E/AndroidRuntime(688): at android.app.ActivityThread.main(ActivityThread.java:4627)
11-16 15:04:33.525: E/AndroidRuntime(688): at java.lang.reflect.Method.invokeNative(Native Method)
11-16 15:04:33.525: E/AndroidRuntime(688): at java.lang.reflect.Method.invoke(Method.java:521)
11-16 15:04:33.525: E/AndroidRuntime(688): at com.android.internal.os.ZygoteInit$MethodAndArgsCaller.run(ZygoteInit.java:868)
11-16 15:04:33.525: E/AndroidRuntime(688): at com.android.internal.os.ZygoteInit.main(ZygoteInit.java:626)
11-16 15:04:33.525: E/AndroidRuntime(688): at dalvik.system.NativeStart.main(Native Method)
11-16 15:04:33.525: E/AndroidRuntime(688): Caused by: java.lang.IllegalStateException: Content view not yet created
11-16 15:04:33.525: E/AndroidRuntime(688): at android.support.v4.app.ListFragment.ensureList(ListFragment.java:328)
11-16 15:04:33.525: E/AndroidRuntime(688): at android.support.v4.app.ListFragment.getListView(ListFragment.java:222)
11-16 15:04:33.525: E/AndroidRuntime(688): at com.commonsware.android.eu4you.CountriesFragment.onSaveInstanceState(CountriesFragment.java:64)
11-16 15:04:33.525: E/AndroidRuntime(688): at android.support.v4.app.FragmentManagerImpl.saveFragmentBasicState(FragmentManager.java:1574)
11-16 15:04:33.525: E/AndroidRuntime(688): at android.support.v4.app.FragmentManagerImpl.saveAllState(FragmentManager.java:1644)
11-16 15:04:33.525: E/AndroidRuntime(688): at android.support.v4.app.FragmentActivity.onSaveInstanceState(FragmentActivity.java:499)
11-16 15:04:33.525: E/AndroidRuntime(688): at com.actionbarsherlock.app.SherlockFragmentActivity.onSaveInstanceState(SherlockFragmentActivity.java:127)
11-16 15:04:33.525: E/AndroidRuntime(688): at android.app.Activity.performSaveInstanceState(Activity.java:1036)
11-16 15:04:33.525: E/AndroidRuntime(688): at android.app.Instrumentation.callActivityOnSaveInstanceState(Instrumentation.java:1180)
11-16 15:04:33.525: E/AndroidRuntime(688): at android.app.ActivityThread.performPauseActivity(ActivityThread.java:3330)
11-16 15:04:33.525: E/AndroidRuntime(688): ... 12 more
I can stop this error from occurring, if I remove the following line from the code above:
ft.addToBackStack(null);
I do require to be able to add the replace transaction to the backstack, so I can't remove this line (this sample application is the basis for my real application that has more stuff, but I used it to simplify my issue).
So two questions:
- Why is this happening?
- How can I fix it (perhaps another approach)?
One thing to note is that in my real application, this logic must all fall inside of tabs in an action bar (ActionBarSherlock since I must support back to version 2.3 (Froyo)).
UPDATE (2012-Nov-22 13:27 EST)
I've posted a sample project on GitHub. I'm new to Eclipse and Android development, so I may not have included everything I needed (workspace was not included and I don't think the ActionBarSherlock project is included... it would need to be imported (can be found here)).
commonsware
tag is for questions about my sample code and open source projects, not just general "hey, I'll get that balding guy to comment" stuff -- this question is fine, I'm mentioning this to minimize future tag spam from those seeing the comments here. – Yvette