i have an activity with a fragment in it.
I would like to handle the orientation change myself, so i updated the manifest to look like this:
<activity android:name="com.test.app" android:configChanges="orientation|keyboardHidden"/>
Then i updated the activity to look like this:
#Override
public void onConfigurationChanged(Configuration newConfig) {
super.onConfigurationChanged(newConfig);
updateLayout();
}
and
#Override
public void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
updateLayout();
}
private void updateLayout() {
setContentView(R.layout.my_layout);
}
I also do this with the fragment:
fragment.setRetainInstance(true);
The issue I have is that when I do the screen orientation, it fails on the setContentView() saying that there is a duplicate id for my fragment. Not sure how to make it so that doesn't happen -- ideas?
tia.
I believe it's because you told it not to throw away your previous layout, so when you rotate it, you still have your old view (which in this case, is the same as the new view, thus the ID conflicts).
Also, I'm not sure, but I think this:
fragment.setRetainInstance(true);
is unnecessary? Because here you tell it NOT to recreate your activity on configuration changes:
android:configChanges="orientation|keyboardHidden"
In my experience, the configChanges setting in XML is enough to prevent recreation.
EDIT :
Mmmm just looking again, how exactly are you using Fragments? If the code posted here is from your FragmentActivity, then I would expect something like this for inflating your Fragment and adding it to the Activity:
class SomeActivity extends FragmentActivity
{
...
#Override
public void onCreate( Bundle savedInstance )
{
...
LayoutInflater inflater = getLayoutInflater();
inflater.inflate( R.layout.some_fragment, root );
...
}
}
With that XML looking something like:
some_fragment.xml
<?xml version="1.0" encoding="utf-8"?>
<fragment
xmlns:android="http://schemas.android.com/apk/res/android"
android:orientation="vertical"
android:layout_width="match_parent"
android:layout_height="match_parent"
android:name="com.someapp.fragments.SomeFragment">
</fragment>
So I guess I'm not clear on how you are using Fragments. But using them like this, with the XML config setting, successfully disables recreation on rotation for me.
Related
I'm trying to learn how to develop Android apps. I followed a video tutorial on YouTube, and it ended by adding a simple App Settings screen to the application.
However, there's one point that bothers me: when I press the back button on my phone's navigation bar, the changed settings aren't applied.
I have tried searching on Google, but none of the solutions I found have worked. The fact that I don't yet understand 100% of what's happening on the proposed solutions may also contribute to my difficulty on solving this one problem.
The behavior I expect from the app is that when I press the back button on the navigation bar, the changed settings should be applied.
For instance, I have a setting for dark background, which is controlled by a checkbox. The current behavior is: I check the setting for dark background. When I press the back button on the navigation bar, the setting isn't applied (I do have a method that loads the preferences on my MainActivity). What I want to happen is when I press the back button, the dark background is applied in this case.
From what I understand, I believe that overriding onBackPressed should do the trick, but I don't know what should be executed in order to properly apply the settings.
Here are the class and layout of my PreferenceScreen. Regarding the strings on the XML, they aren't actually hard-coded. I just copied the English values here to show the text that should appear on the interface.
public class AppPreferences extends AppCompatActivity
{
#Override
protected void onCreate(Bundle savedInstanceState)
{
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_note_detail);
FragmentManager fragmentManager = getFragmentManager();
FragmentTransaction fragmentTransaction = fragmentManager.beginTransaction();
SettingsFragment settingsFragment = new SettingsFragment();
fragmentTransaction.add(android.R.id.content, settingsFragment, "SETTINGS_FRAGMENT");
fragmentTransaction.commit();
}
public static class SettingsFragment extends PreferenceFragment
{
#Override
public void onCreate(Bundle savedInstanceState)
{
super.onCreate(savedInstanceState);
addPreferencesFromResource(R.xml.app_preferences);
}
}
}
<?xml version="1.0" encoding="utf-8"?>
<PreferenceScreen xmlns:android="http://schemas.android.com/apk/res/android">
<PreferenceCategory
android:title="General">
<EditTextPreference
android:title="Notebook"
android:summary="The title that will be used on the main action bar."
android:key="title"
android:defaultValue="Notebook" />
</PreferenceCategory>
<PreferenceCategory
android:title="Color">
<CheckBoxPreference
android:title="Dark Background"
android:summary="Is the main background color dark?"
android:key="background_color"
android:defaultValue="false" />
</PreferenceCategory>
</PreferenceScreen>
You will need to use
public class AppPreferences extends AppCompatActivity
{
#Override
protected void onCreate(Bundle savedInstanceState)
{
super.onCreate(savedInstanceState);
//setContentView(R.layout.activity_note_detail);
FragmentManager fragmentManager = getFragmentManager();
FragmentTransaction fragmentTransaction = fragmentManager.beginTransaction();
SettingsFragment settingsFragment = new SettingsFragment();
fragmentTransaction.add(android.R.id.content, settingsFragment, "SETTINGS_FRAGMENT");
fragmentTransaction.commit();
}
public static class SettingsFragment extends PreferenceFragment
{
#Override
public void onCreate(Bundle savedInstanceState)
{
super.onCreate(savedInstanceState);
addPreferencesFromResource(R.xml.app_preferences);
Preference preference = findPreference("background_color");
preference.setOnPreferenceChangeListener(new Preference.OnPreferenceChangeListener() {
#Override
public boolean onPreferenceChange(Preference preference, Object newValue) {
//do your action here
return false;
}
});
}
}
}
Or from other activity:
PreferenceManager.setDefaultValues(this, R.xml.your_setting_xml, false);
SharedPreferences settings = PreferenceManager.getDefaultSharedPreferences(this);
if (settings.getBoolean("background_color", true)) {
//do your action here
.........
Refer to this question also, (it has similar use case):
Checkbox Preference and Checking if its enabled or disable
Like #Chisko said, there isn't enough code in your question for us to be able to figure out your end goal - although I'm guessing you are wanting some form of persistent storage for your app to be able to save your app preferences. For this, you will want to use something in Android called SharedPreferences. This allows you to save simple data types to be accessed later.
Give that link a read, and then try saving/loading one simple piece of data. You'll want to load it from SharedPreferences on starting the activity (you can specify a default value if it hasn't been saved yet) and then you'll want to save the data in onBackPressed() as you said.
Best of luck and if you run into any issues, just comment here.
#Abdulhamid Dhaiban correctly points it out.
I'll add to your suggestion about overriding the onBackPressed() method.
If your "Up" button (top left <-) provides the correct result, then you can set the Back button to behave like the Up button by just adding the following code:
#Override
public void onBackPressed() {
super.onBackPressed();
NavUtils.navigateUpFromSameTask(this);
}
Hope it helps!
I have a problem with the display of the layout when I rotate the screen from landscape to portrait o viceversa.
I've created a layout land (layout-sw720dp-land/example.xml ) and port (layout- sw720dp-port/example.xml ).
Until everything is right.
At the layout port, I've given the precise dimensions and the layout land other precise dimensions...
Previously I had a problem with the switch of the two layouts.
The transition from horizontal to vertical layout passed successfully, but the activity was destroyed and rebuilt.
I solved the problem by inserting in AndroidManifest.xml this code:
android:configChanges="orientation|screenSize"
Now the problem is that the transition from horizontal to vertical is not automatic.
For example, I open the app horizontally and the app displays the correct layout (the layout-sw720dp-land / example.xml because I use a tablet) but once the app open and i turn the tablet vertically , the vertical layout is not displayed but remains in the horizontal version .. ( for example in the horizontal layout I have a button that its text is " Start " while in the vertical I have the same button that its text is " Finish " and when the app is open and i rotate the tablet from horizontal to vertical the the layout turn making me see , however, the horizontal version , so with the " start " button).
How can I do to show the correct layout WITHOUT re-create and destroy the activity ??
N.B : Using the fragment ... in short, I have a activity_main.xml file that references to activity "MainActivity.java" that inside it has this code:
<FrameLayout
android:layout_below="#+id/toolbar"
android:id="#+id/fragment_container"
android:layout_width="fill_parent"
android:layout_height="fill_parent"
android:foreground="#drawable/shadow_toolbar"/>
inside of this FrameLayout I view the two layouts in this way :
FragmentExample fragment = new FragmentExample();
android.support.v4.app.FragmentTransaction fragmentTransaction =
getSupportFragmentManager().beginTransaction();
fragmentTransaction.replace(R.id.fragment_container, fragment);
fragmentTransaction.commit();
In FragmentExample.java there are the two famous layout:
public class FragmentExample extends Fragment {
public FragmentExample() {
// Required empty public constructor
}
#Override
public View onCreateView(LayoutInflater inflater, ViewGroup container, Bundle savedInstanceState) {
View rootView = inflater.inflate(R.layout.example, container, false);
// Inflate the layout for this fragment
return rootView;
}
}
How can I do so to make the automatic switch of the two layout without closing and reopening the app and re-create it without ??
Sorry for my english =)
You cannot use two different layouts for landscape and portrait mode WITHOUT recreating activity. Parameter android:configChanges="orientation|screenSize" says activity to not recreate itself, but it doesn't work properly because in this case another layout file is not set.
If you want to use two different layouts for landscape and portrait mode then remove android:configChanges="orientation|screenSize" from your activity in AndroidManifest.xml and handle saving and restoring activity state through methods onSaveInstanceState(Bundle outState) and onRestoreInstanceState(Bundle savedInstanceState).
Example (see full article):
public class MainActivity extends AppCompatActivity {
// These variable are destroyed along with Activity
private int someVarA;
private String someVarB;
...
#Override
protected void onSaveInstanceState(Bundle outState) {
super.onSaveInstanceState(outState);
outState.putInt("someVarA", someVarA);
outState.putString("someVarB", someVarB);
}
#Override
protected void onRestoreInstanceState(Bundle savedInstanceState) {
super.onRestoreInstanceState(savedInstanceState);
someVarA = savedInstanceState.getInt("someVarA");
someVarB = savedInstanceState.getString("someVarB");
}
}
See this solution to save and restore fragments.
See also Handling Runtime Changes Documentation.
I have a Fragment, and I want to set that whole fragment as root view of my activity. I have everything ready, and I'm instantiating my fragment programatically. I've tried (in my activity):
#Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
FeedFragment fragment = [...];
setContentView(fragment.getView());
}
But I've got a null pointer exception. In other words, how can I make my fragment act like an activity? I only target ICS+, I don't need to support older versions, if it makes any difference.
Try this
#Override
public View onCreateView(LayoutInflater inflater, ViewGroup container,
Bundle savedInstanceState) {
View rootView = inflater.inflate(R.layout.all_lecturer_frag, container, false);
......
return rootView;
}
A Fragment, by design, is intended to be a tool to help you reuse screen space and as such, fragments have to be present inside a container. So while a fragment cannot technically be a root view, you can have a fragment be the only view inside the Activity. For this, you should inflate the view for your fragment programmatically inside the onCreateView() method of the fragment. then you could have something like this in your activity's layout xml:
<FrameLayout xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:tools="http://schemas.android.com/tools"
android:id="#+id/frame_layout"
android:layout_width="match_parent"
android:layout_height="match_parent">
<com.package.fragment_name
android:layout_width="match_parent"
android:layout_height="match_parent"
/>
</FrameLayout>
And then, within your activity, all you have to do is:
setContentView(R.layout.main);
Since, the fragment is defined in the layout xml, it cannot be removed from the activity's layout (although the layout itself can be changed) and is tied to it.
Also, on a side note, notice that the root view is a FrameLayout and not the fragment itself. But in this manner, your fragment can be tied to the activity. But don't forget that the Fragment will still retain it's lifecycle separate from the activity's.
EDIT: If you need to create your fragment instance programmatically, you have to do:
getFragmentManager().beginTransaction().add(R.id.frame_layout, your_fragment).commit();
This is the only way to add your fragment programmatically. But also keep in mind that the Fragment's layout is not tied to the activity's layout. But you can use the Fragment's lifecycle to behave similarly as an Activity.
#Override
protected void onCreate(Bundle savedInstanceState)
{
super.onCreate(savedInstanceState);
setContentView(R.layout.xxx);
//initializations...
if (savedInstanceState == null) {
// During initial setup, plug in the fragment.
YourFragment details = new YourFragment();
getFragmentManager().beginTransaction().add(R.id.your_root_frame_layout, details).commit();
}
}
Problem:
I add a fragment to a LinearLayout, programmatically. It shows up in my activity, great.
I turn the device —> configuration changes: everything is destroyed to be recreated. But, before onDestroy() is called, onSaveInstanceState() should be called. It is the case for the parent activity, but not for the fragment I've added. Why ?
Code:
The XML:
<?xml version="1.0" encoding="utf-8"?>
<LinearLayout
xmlns:android="http://schemas.android.com/apk/res/android"
android:id="#id/parent_LL"
android:stuff="myStuff"
>
<LinearLayout
android:id="#id/categories_activity_LL1"
android:stuff="myStuff" />
<LinearLayout
android:id="#id/categories_activity_LL2"
android:stuff="myStuff" />
</LinearLayout>
I add the fragment to the UI in the parent activity:
ft.add(container1, categories, CatFragIds.CATEGORIES.toString()).commit();
I override the onSaveInstanceState() of my fragment:
#Override
public void onSaveInstanceState(Bundle outState) {
// Récupère extensible
boolean extensible = ((CategoryAppsListView) this.getListView())
.isExtensible();
mState.setExtensible(extensible);
// Transmet l'état de CategoriesListElems
FragmentManager fm = getFragmentManager();
#SuppressWarnings("unchecked")
FragRetainObject<CategoriesListElemsState> retainedState =
(FragRetainObject<CategoriesListElemsState>)
fm.findFragmentByTag(CATEGORIESLISTELEMS_STATE+"_"+this.getTag());
if( retainedState == null) {
retainedState =
FragRetainObject.<CategoriesListElemsState>newInstance(mState);
fm.beginTransaction()
.add(retainedState, CATEGORIESLISTELEMS_STATE+"_"+this.getTag()).commit();
}
else retainedState.setRetainObj(mState);
super.onSaveInstanceState(outState);
}
Thank you for your time!! :-)
I had the same problem. There are two possible answers:
Add fragment in xml file, like "fragment android:name="faragmentClass" etc."
Call onSaveInstanceState method of fragment manually. And in onCreate method restore fragment's state manually, when you add it.
You need to put the config change as orientation and keyboard in the AndroidManifest.xml
I have an activity which handles configuration changes. But now i have to change
layout.I tried in onConfigurationChanged callback just to set again the layout and hoping will get the correct layout(land) but it's still showing
the first layout for portrait view ( there two layout(same name) are placed in res/
layout and res/layout-land :)
if I delete android:configChanges="orientation", it works should be, but ı need to handle onConfigurationChanged. What should I do??
If you have your portrait layout main.xml in /res/layout and your landscape layout main.xml in /res/layout-land, and your onConfigurationChanged() looks like this:
public void onConfigurationChanged(Configuration newConfig) {
super.onConfigurationChanged(newConfig);
setContentView(R.layout.main);
...
...
...
}
And in your Manifest you should have android:configChanges="keyboardHidden|orientation"
Then it should work fine, as this does in my app. Is this what you are doing?
// In your activity code.
int mCurrentOrientation;
#Override
public void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_mfo_offers);
........
mCurrentOrientation = getCurrentOrientation();
}
#Override
public void onConfigurationChanged(Configuration newConfig) {
super.onConfigurationChanged(newConfig);
// You may handle different configuration changes in your activity which configured in your mainfest.xml, you only need to recreate layout when orientation changes.
if(mCurrentOrientation != newConfig.orientation) {
recreate(); // This recreate the activity if you can properly restore your activity state.
}
......
}
See Activity.recreate() here: http://developer.android.com/reference/android/app/Activity.html#recreate()