I have some Fragment with this structure:
<RelativeLayout
android:id="#+id/control_panel"
android:visibility="gone">
<RelativeLayout
android:id="#+id/control_panel_icon">
<ImageView
android:id="#+id/control_panel_icon_1"
android:src="#drawable/ic_control_panel_icon_1" />
<ImageView
android:id="#+id/control_panel_icon_2"
android:src="#drawable/ic_control_panel_icon_2"
android:visibility="gone"/>
</RelativeLayout>
<TextView
android:id="#+id/control_panel_tv"
android:text="#string/not_available" />
</RelativeLayout>
And inside Fragment class i have onSaveInstanceState and onActivityCreated:
#Override
public void onSaveInstanceState(Bundle outState) {
super.onSaveInstanceState(outState);
outState.putString("tv", panel_tv.getText().toString());
}
#Override
public void onActivityCreated(Bundle savedInstanceState) {
super.onActivityCreated(savedInstanceState);
if (savedInstanceState != null)
panel_tv.setText(savedInstanceState.getString("tv"));
}
So it solves saving the state of TextView.
Inside Fragment class I also setup which image to display with some function:
public void setIcon(boolean condition){
if (condition) {
control_panel_icon_1.setVisibility(View.GONE);
control_panel_icon_2.setVisibility(View.VISIBLE);
} else {
control_panel_icon_1.setVisibility(View.VISIBLE);
control_panel_icon_2.setVisibility(View.GONE);
}
}
Is there any way to save and restore which image is currently displayed?
I know, what I'm shouldn't save entire widgets, but if I can save state of RelativeLayout will it restore all it's child's states?
Inside your Fragment class, declare a private variable:
private boolean condition; // rename to something more descriptive
Now in setIcon() store the value:
this.condition = condition;
Finally save this to the bundle in onSaveInstanceState():
outState.putBoolean("condition", this.condition);
and read it in onActivityCreated():
this.condition = savedInstanceState.getBoolean("condition");
this.setIcon(this.condition);
Note
You don't need to save the text from your TextView. The call to super.onSaveInstanceState() already takes care of this for you.
You can either save the condition in outState like below:
outState.putBoolean("condition", condition);
and then read it and update the image views
setIcon(savedInstanceState.getBoolean("condition"))
Or You can save the visibility state of the image views by putInt method:
outState.putInt("ic1", control_panel_icon_1.getVisibility());
outState.putInt("ic2", control_panel_icon_2.getVisibility());
and then restore the state:
control_panel_icon_1.setVisibility(savedInstanceState.getInt("ic1"));
control_panel_icon_2.setVisibility(savedInstanceState.getInt("ic2"));
Related
I have read a lot of things about the data save instance and restore but Unable to implement in my case. What is my case in application .
I am using the Activity (MainActivity) and calling the fragment in it let say ParentFragment
ParentFragment Calls the ChildFragment in it though ParentFragment has its own views such as TextViews which takes First name, Last name and age and below this part I am programatically calling the ChildFragment
So in this way I have 2 fragments in the MainActivity, which is being shown to the user at a time on the screen
**What I want **
I want when User has change the orientation the layout should also change but I also want that the text fields should maintain there data in them .
I want to save some more fragment variables and their data also on configuration changes and retrieve them after the new layout is set after screen orientation changed.
**Problems and Confusions **
I have no idea If i set the Fragment.setRetainInstance(true) then would my fragment still be able to receive the onConfiguration Change call back?
When I rotate my device, the fragment gets re-initialize and also my activity has the Asynctask and that runs again , i want my activity to hold the same data . How can I achieve that?
Please help me and give me some hint.
If you want to change layout on orientation change then,you should not handle configuration change by retaining the activity(by setting android:configChanges="orientation" value for corresponding activity defined in manifest) or by using setRetainInstance() in fragment.
For saving the fragment state on configuration change use
#Override
protected void onSaveInstanceState(Bundle outState) {
outState.putInt(FIRST_NAME_VALUE_KEY, firstNameTextView.getText());
outState.putInt(LAST_NAME_VALUE_KEY, lastNameTextView.getText());
super.onSaveInstanceState(outState);
}
and for writing back state to the views in fragment use
#Override
public View onCreateView(LayoutInflater inflater, ViewGroup container, Bundle savedInstanceState) {
View view = inflater.inflate(R.layout.my_fragment, container, false);
if (savedInstanceState != null) {
String firstName = savedInstanceState.getInt(FIRST_NAME_VALUE_KEY);
String lastName = savedInstanceState.getInt(LAST_NAME_VALUE_KEY);
firstNameTextView.setText(firstName);
lastNameTextView.setText(lastName);
}
return view;
}
You will receive onConfigurationChanged() callback by retaining activity.it is not related to Fragment.setRetainInstance(true).
To avoid running asynctask again,We can store data and retain it using following callbacks inside the activity.
#Override
protected void onSaveInstanceState(Bundle savedInstanceState) {
// Save custom values into the bundle
savedInstanceState.putInt(SOME_VALUE, someIntValue);
savedInstanceState.putString(SOME_OTHER_VALUE, someStringValue);
// Always call the superclass so it can save the view hierarchy state
super.onSaveInstanceState(savedInstanceState);
}
in onCreate(Bundle savedInstanceState) call back you can check for savedInstanceSate , based on that you can call u asynctask or retain values as below
#Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
if (savedInstanceState == null) {
asyncTask.execute();
} else {
someIntValue = savedInstanceState.getInt(SOME_VALUE);
someStringValue = savedInstanceState.getString(SOME_OTHER_VALUE);
}
}
Hi iam trying to save the state of the fragment when one fragment is replaced with other fragment.
Consider in my Activity i have a fragmentA with textview-1 and textview-2. when i click on the textview-1 i am replacing fragmentA with fragmentB which has a list view.
Now when i click on the fragmentB list item iam getting the list value and updating the textview-1. same thing iam doing for textview-2 but when i return back, the textview-1 value is gone. How to save the state of the fragment A and its textview-1 value.
Tried as below
#Override
public void onSaveInstanceState(Bundle outState) {
// TODO Auto-generated method stub
super.onSaveInstanceState(outState);
outState.putString("curChoice", strtext);
}
and in OnActivityCreated()
if (savedInstanceState != null) {
// Restore last state for checked position.
strtext = savedInstanceState.getString("curChoice", "");
//incrementdata(strtext);
}
But always the savedInstanceState is giving null.
When any Fragment is destroyed by the system, everything will just happen exactly the same as in an Activity. It means that every single member variables are also destroyed with the fragment. You will have to manually save and restore those variables through the onSaveInstanceState and onActivityCreated methods respectively.
There is no onRestoreInstanceState method inside a Fragment.
So you need to do something like as below code.
// These variable are destroyed along with Activity
private String var1,var2;
#Override
public void onSaveInstanceState(Bundle outState) {
super.onSaveInstanceState(outState);
outState.putInt("VAR1", var1);
outState.putString("VAR2", var2);
}
#Override
public void onActivityCreated(#Nullable Bundle savedInstanceState) {
super.onActivityCreated(savedInstanceState);
var1 = savedInstanceState.getInt("VAR1");
var2 = savedInstanceState.getString("VAR2");
}
Basically every single standard widget such as EditText, TextView, Checkbox and etc. are all already internally implemented those things. You may need to enable it for some View for example you have to set android:freezeText to true for TextView to use the feature.
I have statically created a fragment (via XML). I'm trying to store the last displayed value in a bundle and display it whenever the app is started next. However I am not able to get it to work. For some reason savedInstanceState is always null.
public class DistanceSetterFragment extends Fragment implements SharedPreferences.OnSharedPreferenceChangeListener {
Distance distance = new Distance();
#Override
public void onCreate(Bundle savedInstanceState)
{
super.onCreate(savedInstanceState);
if (savedInstanceState!=null )
{
Log.d(this.getClass().getName(),"onCreate savedInstanceState is NOT null");
}
else
{
Log.d(this.getClass().getName(),"onCreate savedInstanceState is null");
}
}
#Override
public View onCreateView(LayoutInflater inflater, ViewGroup container, Bundle savedInstanceState)
{
Log.d(this.getClass().getName(),"Distance "+distance);
if (savedInstanceState!=null )
{
Log.d(this.getClass().getName(),"onCreateView savedInstanceState is NOT null");
}
else
{
Log.d(this.getClass().getName(),"onCreateView savedInstanceState is null");
}
return inflater.inflate(R.layout.fragment_distancesetter, container, false);
}
#Override
public void onSaveInstanceState(Bundle outState)
{
super.onSaveInstanceState(outState);
if (distance!=null) {
Log.d(this.getClass().getName(),"Saving DISTANCE_BEAN "+distance);
outState.putSerializable(Constants.DISTANCE_BEAN, distance);
}
else
{
Log.d(this.getClass().getName(),"Distance BEAN IS NULL");
}
outState.putString("", "");
}
}
Below is the fragment XML declared in my main activity XML
<fragment
android:layout_width="fill_parent"
android:layout_height="wrap_content"
android:id="#+id/fragment_distancesetter"
android:layout_below="#id/img_logo_main"
android:name="com.webconfs.xyz.fragments.DistanceSetterFragment"
android:layout_marginLeft="4dp"
android:layout_marginRight="4dp"
/>
As you can see
- I have NOT set setRetainInstance(true) in my Fragment class and
- My fragment XML has an ID associated with it
android:id="#+id/fragment_distancesetter
I had the same problem just now and it was driving me crazy, but as it turns out the fragment was simply being re-added in the activity with every rotation. You didn't add your Activity's code, but you might want to check this is not the case as it's easy to overlook and would explain your problem.
When you put a fragment into an activity statically, the fragment manager will always create a new fragment and attach it to activity. The restoreInstanceState() method will never be called. If you want to do it, you can save state in your activity's restore method, and put save state of your activity to your fragment. Or create fragment dynamically.
I have an aacplayer app and I want to save the state of my activity when orientation changes from portrait to landscape. The TextViews do not appear to be empty, I tried to freeze my textview using this:
android:freezesText="true"
my manifest:
android:configChanges="orientation"
I also tried this:
#Override
public void onConfigurationChanged(Configuration newConfig){
super.onConfigurationChanged(newConfig);
setContentView(R.layout.main2);
So when orientation changes to landscape I can see my layout-land main2.xml, that works but my textview goes out and appears empty. Streaming music works great. I can listen to it when orientation changes, but the text inside textviews are gone each time I change the orientation of my device.
What should I do to fix this so I can save the state?
#Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
....
....
Thank you very much.
When your orientation changes, you don't have to manually change to the landscape layout file. Android does this automatically for you. When orientation changes, Android destroys your current activity and creates a new activity again, this is why you are losing the text.
There are 2 parts you need to do, assuming you want a separate layout for portrait and landscape.
Assuming you have 2 XML layout files for portrait and landscape, put your main.xml layout file in the following folders:
res/layout/main.xml <-- this will be your portrait layout
res/layout-land/main.xml <-- this will be your landscape layout
That's all you need to do, you don't have to touch the manifest file to modify android:configChanges="orientation" or override the onConfigurationChanged(). Actually, it's recommended you do not touch this for what you are trying to achieve.
Now to save your text from the text view =) Lets assume your textview is named as MyTextView in your layout xml file. Your activity will need the following:
private TextView mTextView;
private static final String KEY_TEXT_VALUE = "textValue";
#Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
mTextView = (TextView) findViewById(R.id.main);
if (savedInstanceState != null) {
CharSequence savedText = savedInstanceState.getCharSequence(KEY_TEXT_VALUE);
mTextView.setText(savedText);
}
}
#Override
protected void onSaveInstanceState (Bundle outState) {
super.onSaveInstanceState(outState);
outState.putCharSequence(KEY_TEXT_VALUE, mTextView.getText());
}
Basically, whenever Android destroys and recreates your Activity for orientation change, it calls onSaveInstanceState() before destroying and calls onCreate() after recreating. Whatever you save in the bundle in onSaveInstanceState, you can get back from the onCreate() parameter.
So you want to save the value of the text view in the onSaveInstanceState(), and read it and populate your textview in the onCreate(). If the activity is being created for the first time (not due to rotation change), the savedInstanceState will be null in onCreate(). You also probably don't need the android:freezesText="true"
You can also try saving other variables if you need to, since you'll lose all the variables you stored when the activity is destroyed and recreated.
There are two ways of doing this, the first one is in the AndroidManifest.xml file. You can add this to your activity's tag
android:configChanges="keyboardHidden|orientation|screenSize|screenLayout"
Or you can override two methods that will take care of this. This method requires some more effort, but arguably is much better. onSaveInstanceState saves the state of the activity before it's killed, and onRestoreInstanceState restores that information after onStart() Refer to the official documentation for a more in depth look.
In my sample code below, I am saving 2 int values, the current selection from the spinner as well as a radio button.
#Override
public void onSaveInstanceState(#NonNull Bundle savedInstanceState) {
spinPosition = options.getSelectedItemPosition();
savedInstanceState.putInt(Constants.KEY, spinPosition);
savedInstanceState.putInt(Constants.KEY_RADIO, radioPosition);
super.onSaveInstanceState(savedInstanceState);
}
// And I am restoring those values with `getInt`, then I can pass those stored values into the spinner and radio button group to select the same values that we saved earlier.
#Override
public void onRestoreInstanceState(#NotNull Bundle savedInstanceState) {
spinPosition = savedInstanceState.getInt(Constants.KEY);
radioPosition = savedInstanceState.getInt(Constants.KEY_RADIO);
options.setSelection(spinPosition, true);
type.check(radioPosition);
super.onRestoreInstanceState(savedInstanceState);
}
static CharSequence savedText;
#Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_main);
if(savedText != null) {
TextView mTextView = (TextView) findViewById(R.id.main);
mTextView.setText(savedText);
}
}
// Another function in activity, when you change text
public void actionButton(View view) {
// Change and save text in textView
savedText = "Change text";
mTextView.setText(savedText);
}
Its work for me.
But I think its not good code style and architecture for android.
I use in KOTLIN static var / val :
class MyFragment : Fragment()
{
//all my code
//access to static vars -> MyStaticClass.hello
}
class MyStaticClass
{
companion object {
var hello: String = "Static text"
var number_static: Int = 0
}
}
I have a CustomButton class (extends LinearLayout) where I inflate a layout which contains a ToggleButton (in reality this is more complex, but I simplified here the problem).
public class CustomButton extends LinearLayout {
private ToggleButton toggleOnOffButton;
public CustomButton(Context context, AttributeSet attrs) {
super(context, attrs);
LayoutInflater.from(context).inflate(R.layout.custom_button_layout, this);
}
#Override
protected void onFinishInflate() {
toggleOnOffButton = (ToggleButton) findViewById(R.id.toggle_on_off_button);
super.onFinishInflate();
}
public ToggleButton getToggleOnOffButton() {
return toggleOnOffButton;
}
}
custom_button_layout.xml
<RelativeLayout xmlns:android="http://schemas.android.com/apk/res/android"
android:layout_width="fill_parent"
android:layout_height="wrap_content">
<ToggleButton android:id="#+id/toggle_on_off_button"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:textOff="Off"
android:textOn="On"
android:layout_alignParentRight="true"
/>
</RelativeLayout>
I have an activity where I inflate an layout with 2 CustomButton-s.
The on/off state of the first toggleButton is saved in shared preferences and I load the value from there in onCreate method.
public class FirstActivity extends Activity
{
private CustomButton customButton;
private ToggleButton toggleBut;
#Override
public void onCreate(Bundle savedInstanceState)
{
super.onCreate(savedInstanceState);
setContentView(R.layout.main);
customButton = (CustomButton)findViewById(R.id.toggleButton);
toggleBut = customButton.getToggleOnOffButton();
boolean saved = loadPreferences("toggleBut");
toggleBut.setChecked(saved);
toggleBut.setOnClickListener(new View.OnClickListener() {
#Override
public void onClick(View view) {
boolean checked = toggleBut.isChecked();
savePreferences("toggleBut", checked);
}
});
}
private void savePreferences(String key, boolean value){
SharedPreferences sharedPreferences = getPreferences(MODE_PRIVATE);
SharedPreferences.Editor editor = sharedPreferences.edit();
editor.putBoolean(key, value);
editor.commit();
}
private boolean loadPreferences(String key){
SharedPreferences sharedPreferences = getPreferences(MODE_PRIVATE);
return sharedPreferences.getBoolean(key, true);
}
}
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="fill_parent"
android:layout_height="fill_parent">
<com.example.example.cs.ssd.custom.CustomButton
android:id="#+id/toggleButton"
android:layout_width="fill_parent"
android:layout_height="wrap_content"
/>
<com.example.example.cs.ssd.custom.CustomButton
android:id="#+id/toggleButton2"
android:layout_width="fill_parent"
android:layout_height="wrap_content"
/>
</LinearLayout>
When I start the application the first toggleButton is ON. When I change the orientation of the screen, automatically the first toggleButton become Off, even saved has value true and is called toggleBut.setChecked(saved); and I think this has to do with the CutomButton I've created because if the main.xml layout contains only 1 CustomButton this problem does not reproduce.
I'm not sure what I'm doing wrong...
Here is the archive with the above code (as a project): archive
If you want your CustomButton to retain its current state after an orientation change simply override onSaveInstanceState() and onRestoreInstanceState().
A Solution
I ran through your code and noticed that toggleBut's state was being changed after onActivityCreated() but before onStart(). To avoid having any of these methods override your toggle settings, I simply moved these lines from onViewCreated():
boolean saved = loadPreferences("toggleBut");
toggleBut.setChecked(saved);
and put them in onResume(). Hope that helps!
Better Solution
Your ToggleButton setting are being overwritten when the system tries to restore the default saveInstanceState, probably in Fragment.onActivityCreated().
In CustomButton, override these functions like so:
#Override
protected Parcelable onSaveInstanceState() {
Bundle state = new Bundle();
state.putParcelable("default", super.onSaveInstanceState());
state.putParcelable("toggle", toggleOnOffButton.onSaveInstanceState());
return state;
}
#Override
protected void onRestoreInstanceState(Parcelable state) {
Bundle bundle = (Bundle) state;
super.onRestoreInstanceState(bundle.getParcelable("default"));
toggleOnOffButton.onRestoreInstanceState(bundle.getParcelable("toggle"));
};
Understand that the system will still change the ToggleButton states, without the one more thing. But let me try to explain what;s happening:
onActivityCreated(Bundle savedInstanceState) passes it's savedInstanceState to every layout element by calling 'onRestoreInstanceState(Bundle savedInstanceState)`.
onRestoreInstanceState() begins with the layout's root element first and traverses up the layout's hierarchy (in this case it sets the checked state of each ToggleButton last).
Since the default methods are clearly not working, we need to define our own save / restore method for the ToggleButtons. Otherwise any changes we make before the system calls onRestoreInstanceState() will be changed again by the system...
So, lastly we will exclude the ToggleButtons from this default behavior by adding the following line to CustomButton.onFinishInflate():
toggleOnOffButton.setSaveEnabled(false);
Voila, your CustomButtons automatically retain their state.