Weird problem when using onSaveInstanceState and onRestoreInstanceState - java

I have an activity where I have elements like, ImageView, Switch, EditText, TextView, when changing screen orientation some elements returned me to their initial value, to solve it use onSaveInstanceState and onRestoreInstanceState to store and retrieve the information when changing the orientation of screen. It seems to work fine except for one detail with a Toast.
I have a TextView called dateOfNotification that shows a date that the user has selected, it starts with visibility = "gone", I also have a Switch with the following code:
switch1.setOnCheckedChangeListener(new CompoundButton.OnCheckedChangeListener() {
#Override
public void onCheckedChanged(CompoundButton buttonView, boolean isChecked) {
if(switch1.isChecked()){
if(dateOfNotification.getVisibility() == View.VISIBLE){
hour.setText("08:00");
}
else{
Toast.makeText(tareas.this, "First enter a date", Toast.LENGTH_SHORT).show();
switch1.setChecked(false);
}
}
}
});
The important thing to note here is that switch1 cannot be activated if dateOfNotification is gone.
Now for the change of screen orientation without losing data I have:
protected void onSaveInstanceState( final Bundle outState ) {
super.onSaveInstanceState(outState);
outState.putInt("visDate", dateOfNotification.getVisibility());
outState.putBoolean("statusSW1", switch1.isChecked());
}
#Override
protected void onRestoreInstanceState(#NonNull Bundle savedInstanceState) {
super.onRestoreInstanceState(savedInstanceState);
dateOfNotification.setVisibility(savedInstanceState.getInt("visDate"));
switch1.setChecked(savedInstanceState.getBoolean("statusSW1"));
}
The problem is:
When dateOfNotification is visible and switch1 is Checked and I change orientation, values remain dateOfNotification is visible and switch1 is Checked, but I don't know why the Toast "First enter a date" shows me, It is assumed that when dateOfNotification is visible, the Toast should not be shown, the rarer is that to show the Toast, the dateOfNotification is considered to be hidden, but then why doesn't the switch disable me?
else{
Toast.makeText(tareas.this, "First enter a date", Toast.LENGTH_SHORT).show();
switch1.setChecked(false);
}
It does not seem logical to me that only the Toast.makeText(tareas.this, "First enter a date", Toast.LENGTH_SHORT).show(); is executed and that the switch1.setChecked(false); is not executed
EDIT
By testing I understood what is happening, the state of switch1 is always saved with or without onSaveInstanceState, then when rotating the screen dateOfNotification is Gone, but the state of switch1 is retrieved and activated, when setOnCheckedChangeListener sees that switch1 has been activated dateOfNotification is hidden then it shows the Toast and the switch1 is turned off, then with onRestoreInstanceState the visibility of the dateOfNotification and the state of switch1 are recovered, then now it is activated again.
So I would like to know if there is some way to make the state of switch1 not load automatically when rotating the screen, which can only be loaded with onRestoreInstanceState

Related

Android toasts sometimes not showing when the app was started for the first time

I understand that the title might sound a little bit misleading, but it will make sense in a second.
When does the problem occur?
The toasts are shown always, but not at the first time when the user launches the app. When the app is fired for the first time an App Intro is being displayed, In my MainActivity i check my SharedPreferencesand start an Intent which holds the AppIntro when the user visits the app the first time.
I show a quick Intro and also ask the User for the Storage Permission Then my SearchFragment in the corresponding ViewPager is shown.
When and where are the Toasts being fired?
When the user clicks on a Button, I call the method showInfoToast() where I pass the Context and the String as Argument and then my Toasts are being shown. I use the Library Toasty from GrenderG but it doesn't make any difference with the default Toast.
public static void showInfoToast(String infoMessage, Context context) {
Toasty.info(context, infoMessage, Toast.LENGTH_SHORT, true).show();
}
What have I tried / what do I think the problem is?
The Context passed to the method. As said I call the method showInfoToast in my onClickListener in the SearchFragment I pass getContext() But it seems not to be the problem, as I have seen when I stepped over it with the Debugger, Context is from the MainActivity.
The Library Toasty but the same error occures in the default Toasts
That's why in my opinion somehow the App Intro Intent messes up the Context/The SearchFragment the problem has to be there as it only happens on the first launch of the app.
Code snippet onClickListener in Search Fragment:
goViralButton.setOnClickListener(new View.OnClickListener() {
#Override
public void onClick(View view) {
synchronized (this) {
showInfoToast("Please add users", getContext());
}
}
});
Code snippet App Intro launched by the MainActivity:
// Initialize SharedPreferences
SharedPreferences getPrefs = PreferenceManager
.getDefaultSharedPreferences(getBaseContext());
// Create a new boolean and preference and set it to true
boolean isFirstStart = getPrefs.getBoolean("firstStart", true);
// If the activity has never started before...
if (isFirstStart) {
// Launch app intro
final Intent i = new Intent(MainActivity.this, IntroActivity.class);
runOnUiThread(new Runnable() {
#Override
public void run() {
startActivity(i);
}
});
// Make a new preferences editor
SharedPreferences.Editor e = getPrefs.edit();
// Edit preference to make it false because we don't want this to run again
e.putBoolean("firstStart", false);
// Apply changes
e.apply();
}
Logcat output when I press the goViral Button:
Unable to start animation, surface is null or no children.
Toast already killed
...

Detect dismissal of the keyboard (by back button) in EditText

I have a set of EditText fields generated by a for loop. Each time that field is edited, I need to run some calculations and update other text fields (think excel spreadsheet sums).
My logic is working, and I even have it set up to replace the last field's down focus change to the first field (to avoid the "Done" button showing on the keyboard).
However, if the user presses the Back button to dismiss the keyboard, the focus is not changed and the calculations are not done. The entered number is different but now the totals are wrong. I can't find where to detect when the keyboard is dismissed so I can work around this.
What I have now:
final EditText etWeight = new EditText(this);
etWeight.setText("initWt");
etWeight.setSelectAllOnFocus(true);
etWeight.setOnFocusChangeListener(new View.OnFocusChangeListener() {
#Override
public void onFocusChange(View view, boolean b) {
//code to update after weight is changed
}
));
llWeightRow.addView(etWeight);
I have also tried putting this code in setOnEditorActionListener, setOnClickListener, and setOnKeyListener.
Aside from some of these not working as expected, none of them appear to trigger if the back button is pressed to dismiss the keyboard. I have searched online but only came up with suggestions on how to manually hide the keyboard with my own button; I can't seem to find anything concerning the back button on the tablet itself.
How can I detect that the keyboard has been dismissed so I can force a focus change (or just re-run the calculation code)?
Update:
Not marking this as answered because this doesn't answer this specific question, but I currently am producing my desired results (other fields always updated) by switching to a onTextChanged method. Now the values are updated whenever any text inside the EditText is changed.
etWeight.addTextChangedListener(new TextWatcher() {
#Override
public void beforeTextChanged(CharSequence cs, int i, int i1, int i2) {
}
#Override
public void onTextChanged(CharSequence cs, int i, int i1, int i2) {
//calc and update code
}
#Override
public void afterTextChanged(Editable editable) {
}
});
Have you tried to override onBackPressed? You can make your manipulations with focus there.
#Override
public void onBackPressed() {
// Make needed preparations here
super.onBackPressed();
}
try this
InputMethodManager imm = (InputMethodManager) ActivityName.this.getSystemService(Context.INPUT_METHOD_SERVICE);
if (imm.isAcceptingText()) {
Log.d(TAG,"Software Keyboard was shown");
} else {
Log.d(TAG,"Software Keyboard was not shown");
}

how to keep my popup window on orientation change?

I have a popup window in my activity.
Whenever I change the screen orientation to landscape, the popup disappears.
Why is that, and how can I keep the popup visible?
try below code:-
#Override
public void onConfigurationChanged(Configuration newConfig) {
super.onConfigurationChanged(newConfig);
if(newConfig.orientation == Configuration.ORIENTATION_LANDSCAPE)
Log.i("orientation", "Orientation changed to: Landscape");
else
Log.i("orientation", "Orientation changed to: Portrait");
}
see below link for more info:-
How to keep Popup window opened when orientation changes at run time in Android?
When orientation changes the activity will restart.. So normally the popup window calls again.. In any case if it gone try to call it within onCreate.
Or check the orientation change and take necessary recalls.
if(getResources().getConfiguration().orientation == getResources()
.getConfiguration().ORIENTATION_LANDSCAPE){
// put some flag
}else if(getResources().getConfiguration().orientation != getResources()
.getConfiguration().ORIENTATION_PORTRAIT) {
// change the flag
}
If you put your code fragments may I can help you
You need to use managed Dialogs. Rather than
AlertDialog.Builder builder = new AlertDialog.Builder(this);
builder.setTitle(getString(R.string.rule_edit_choose_action));
builder.setAdapter(new ArrayAdapter(this, R.array.dummyValues), null);
builder.show();
you should use something like
myActivity.showDialog(0);
and then implement onCreateDialog() in your Activity. Your activity will then manage the dialog and re-show it when you re-orientate and it's closed. If you need to change your dialog every time it is shown, implement onPrepareDialog() also - the Activity will give you access to the Dialog just before it is shown so you can update it (with a custom message, for instance).
There's lots of info here:
http://developer.android.com/guide/topics/ui/dialogs.html
As #Ciril said, your issue is that your Activity is restarted when you re-orientate. You could always fix your activity orientation to portrait or landscape if that is suitable for your app. That would prevent it from restarting.
most likely you use AlertDialog for your popups, something along the lines of:
AlertDialog.Builder builder = new AlertDialog.Builder(activity);
builder.setTitle(R.string.popup_title);
builder.setMessage(R.string.popup_message);
builder.setPositiveButton(R.string.yes, new OnClickListener() {
#Override
public void onClick(DialogInterface dialog, int which) {
// do something
}
});
builder.show();
this is bad, because your Activity has no idea there's a popup dialog, and when you change screen orientation, the Activity is restarted with the new parameters, and your popup is gone.
to avoid this you'd better use ShowDialog() to display your popups. to make it work, you need to override onCreateDialog() :
// Called to create a dialog to be shown.
#Override
protected Dialog onCreateDialog(int id, Bundle bundle) {
switch (id) {
case NEW_DIALOG :
return new AlertDialog.Builder(this)
.setTitle(R.string.popup_title)
.setMessage(R.string.popup_message)
.setPositiveButton(android.R.string.ok, null)
.create();
default:
return null;
}
}
then you'd better override onPrepareDialog() (this is not required, actually):
// If a dialog has already been created, this is called
// to reset the dialog before showing it a 2nd time. Optional.
#Override
protected void onPrepareDialog(int id, Dialog dialog, final Bundle bundle) {
AlertDialog dlg = (AlertDialog) dialog;
switch (id) {
case NEW_DIALOG :
dlg.SetTitle("popup title");
// and maybe something else
}
}
after all preparations you may call ShowDialog(NEW_DIALOG) and you Activity will remember it has a popup laid over on the top, and will recreate it after the orientation change.

Android Changing CheckBox Checked Color

I'm coding an application that deals with 2 different CheckBoxes. When one CheckBox gets clicked, the color of the tick should be blue (instead of green), whereas the color of the other CheckBox remains green.
This is my code...
CheckBox green = (CheckBox) findViewById(R.id.greenButton);
CheckBox blue = (CheckBox) findViewById(R.id.blueButton);
blue.setOnCheckedChangeListener(new OnCheckedChangeListener(){
public void onCheckedChanged(CompoundButton arg0, boolean arg1) {
if(arg1){
blue.setHighlightColor(Color.BLUE);
Toast.makeText(getBaseContext(), "Question Marked As Partial", 4000).show();
}
}
});
green.setOnCheckedChangeListener(new OnCheckedChangeListener(){
public void onCheckedChanged(CompoundButton arg0, boolean arg1) {
if(arg1){
blue.setHighlightColor(Color.GREEN);
Toast.makeText(getBaseContext(), "Question Marked As Fully Understood!", 4000).show();
}
}
});
However, both the CheckBoxes tick colors remains green, and the Toast message doesn't get displayed, so I am guessing that the OnCheckedChangeListener is never being called.
Could someone offer any advice?
If the Toast isn't appearing, it is possible that your listeners aren't actually being set on the CheckBoxes. In other words, maybe this whole piece of code isn't even being called.
If your code is in a method, make sure you're calling the method to set the listeners on the CheckBoxes, or ensure this code is in one of your main methods such as onCreate().
If you don't know already, it would be a great time to learn how to debug your code - it makes it really quick and easy to determine whether your code is being called or not.

How do I handle screen orientation changes when a dialog is open?

I have an android app which is already handling changes for orientation, i.e. there is a android:configChanges="orientation" in the manifest and an onConfigurationChange() handler in the activity that switches to the appropriate layout and preps it. I have a landscape / portrait version of the layout.
The problem I face is that the activity has a dialog which could be open when the user rotates the device orientation. I also have a landscape / portrait version of the dialog.
Should I go about changing the layout of the dialog on the fly or perhaps locking the activity's rotation until the user dismisses the dialog.
The latter option of locking the app appeals to me since it saves having to do anything special in the dialog. I am supposing that I might disable the orientation when a dialog opens, such as
setRequestedOrientation(ActivityInfo.SCREEN_ORIENTATION_NOSENSOR);
and then when it dismisses
setRequestedOrientation(ActivityInfo.SCREEN_ORIENTATION_SENSOR);
Would that be a sensible thing to do? If the screen orientation did change while it was locked, would it immediately sense the orientation change when it was unlocked?
Are there alternatives?
I would recommend not turning off the screen rotation, instead of this handle the configuration changes for the Dialog. You could use one of these two approach for this:
The first one is using a flag variable in onSaveInstanceState(outState) method, and restore the dialog onCreate(bundle) method:
in this example my flag variable is called 'isShowing Dialog', when the onCreate method is called by the android System for first time, the bundle argument will be null and nothing happens. However when the activity it's recreated by a configuration change (screen rotation), the bundle will have the boolean value isShowing Dialog, previously saved by the inSaveInstanceState(...) method, so if the variable gets true the dialog is created again, the trick here is set the flag in true when the dialog get showing, and false when it's not, is a little but simple trick.
Class MyClass extends Activity {
Boolean isShowingDialog = false;
AlertDialog myDialog;
#Override
protected void onCreate(Bundle savedInstanceState) {
if(savedInstanceState!=null){
isShowingDialog = savedInstanceState.getBoolean("IS_SHOWING_DIALOG", false);
if(isShowingDialog){
createDialog();
}
}
}
#Override
protected void onSaveInstanceState(Bundle outState) {
outState.putBoolean("IS_SHOWING_DIALOG", isShowingDialog);
super.onSaveInstanceState(outState);
}
#Override
protected void onPause() {
if(myDialog!=null && myDialog.isShowing()) {
myDialog.dismiss();
}
}
private void createDialog() {
AlertDialog.Builder dialog_builder = new AlertDialog.Builder(this);
dialog_builder.setTitle("Some Title"):
... more dialog settings ...
myDialog = dialog_builder.create();
myDialog.show();
isShowingDialog = true;
}
private void hideDialog(){
myDialog.dismiss();
isShowingDialog = false;
}
}
The second approach is to use the ability of the fragments components to retain its states, the main idea is create the dialog inside a fragment, there is the problem about detach and reattach the fragment during the configuration changes (because you need dismiss and show the dialog correctly), but the solution is very similar to the first approach. The advantage of this approach is that if you have an AlertDialog with a couple of configurations, when the fragment is recreated there is not needed to create and setting up the dialog again, only make it show() and the AlertDialog state is maintained by the fragment.
I hope this helps.
I suggest your Dialog should override onSaveInstanceState() and onRestoreInstanceState(Bundle) to save its state into a Bundle.
You then override those methods in your Activity, checking if the Dialog is shown and if so - calling the dialog's methods to save and restore it's state.
If you are displaying this dialog from a fragment, you will want to override OnActivityCreated(Bundle) instead of OnRestoreInstanceState.
For a source example see the built-in clock app provided with Android, where the SetAlarm Activity handles the TimePickerDialog this way.
If you are handling orientation changes yourself, then here is an approach.
I won't claim that this is an elegant solution, but it works:
You can keep track of whether the dialog has an active instance inside the dialog class itself, by using a static variable activeInstance, and overriding onStart() to set activeInstance = this and onCancel() to set activeInstance = null.
Provide a static method updateConfigurationForAnyCurrentInstance() that tests that activeInstance variable and, if non-null, invokes a method activeInstance.reInitializeDialog(), which is a method that you will write to contain the setContentView() call plus the code that wires the handlers for the dialog controls (button onClick handlers, etc. - this is code that would normally appear in onCreate()). Following that, you would restore any displayed data to those controls (from member variables in your dialog object). So, for example, if you had a list of items to be viewed, and the user were viewing item three of that list before the orientation change, you would re-display that same item three at the end of updateConfigurationForAnyCurrentInstance(), right after re-loading the controls from the dialog resource and re-wiring the control handlers.
You would then call that same reInitializeDialog() method from onCreate(), right after super.onCreate(), and place your onCreate()-specific initialization code (e.g., setting up the list of items from which the user could choose, as described above) after that call.
This will cause the appropriate resource (portrait or landscape) for the dialog's new orientation to be loaded (provided that you have two resources defined having the same name, one in the layout folder and the other in the layout-land folder, as usual).
Here's some code that would be in a class called YourDialog:
ArrayList<String> listOfPossibleChoices = null;
int currentUserChoice = 0;
static private YourDialog activeInstance = null;
#Override
protected void onStart() {
super.onStart();
activeInstance = this;
}
#Override
public void cancel() {
super.cancel();
activeInstance = null;
}
static public void updateConfigurationForAnyCurrentInstance() {
if(activeInstance != null) {
activeInstance.reInitializeDialog();
displayCurrentUserChoice();
}
}
private void reInitializeDialog() {
setContentView(R.layout.your_dialog);
btnClose = (Button) findViewById(R.id.btnClose);
btnClose.setOnClickListener(this);
btnNextChoice = (Button) findViewById(R.id.btnNextChoice);
btnNextChoice.setOnClickListener(this);
btnPriorChoice = (Button) findViewById(R.id.btnPriorChoice);
btnPriorChoice.setOnClickListener(this);
tvCurrentChoice = (TextView) findViewById(R.id.tvCurrentChoice);
}
private void displayCurrentUserChoice() {
tvCurrentChoice.setText(listOfPossibleChoices.get(currentUserChoice));
}
#Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
reInitializeDialog();
listOfPossibleChoices = new ArrayList<String>();
listOfPossibleChoices.add("One");
listOfPossibleChoices.add("Two");
listOfPossibleChoices.add("Three");
currentUserChoice = 0;
displayCurrentUserChoice();
}
#Override
public void onClick(View v) {
int viewID = v.getId();
if(viewID == R.id.btnNextChoice) {
if(currentUserChoice < (listOfPossibleChoices.size() - 1))
currentUserChoice++;
displayCurrentUserChoice();
}
}
else if(viewID == R.id.btnPriorChoice) {
if(currentUserChoice > 0) {
currentUserChoice--;
displayCurrentUserChoice();
}
}
Etc.
Then, in your main activity's onConfigurationChanged() method, you would just invoke YourDialog.updateConfigurationForAnyCurrentInstance() whenever onConfigurationChanged() is called by the OS.
Doesn't seem the title was ever resolved (Google Necro Direct).
Here is the solution, matching the request.
When your activity is created, log the screen orientation value.
when onConfiguration change is called on your activity, compare the orientation values. if the values don't match, fire off all of your orientation change listeners, THEN record the new orientation value.
Here is some constructive code to put in your activity (or any object that can handle configuration change events)
int orientation; // TODO: record orientation here in your on create using Activity.this.getRequestedOrientation() to initialize!
public int getOrientation(){return orientation;}
public interface OrientationChangeListener {
void onOrientationChange();
}
Stack<OrientationChangeListener> orientationChangeListeners = new Stack<>();
public void addOrientationChangeListener(OrientationChangeListener ocl){ ... }
public void removeOrientationChangeListener(OrientationChangeListener ocl){ ... }
That's the basic environment. Here's your executive:
public void onConfigurationChanged(Configuration newConfig) {
super.onConfigurationChanged(newConfig);
if (orientation != newConfig.orientation)
for (OrientationChangeListener ocl:orientationChangeListeners) ocl.onOrientationChange();
orientation = newConfig.orientation;
}
In YOUR code model, you may need to send the new configuration, with the event, or the two orientation values with the event. However, Activity.this.getOrientation() != Activity.this.getRequestedOrientation() during event handling (because we are in a logical state of change between two logical values).
In review of my post, i have determined that there could be some synchronization issues, with multiple events! This is not a fault of this code, but a fault of "Android Platform" for not having defacto orientation sense handlers on every window, thusly trashing the polymorphic benefits of using java in the first place..
See the answer from Viktor Valencia above. That will work perfectly with the slight adjustment that you move the createDialog() to onResume.
#Override
protected void onResume() {
super.onResume();
if(isShowingDialog){
createDialog();
}
}
Fetch the boolean isShowingDialog value at onCreate, as suggested, but wait for onResume to display the dialog.

Categories

Resources