Xamarin.Android AlertDialog pops up twice inside a fragment - android

I'm having a problem with an AlertDialog , It appears twice,
when i click on one of the buttons it re-appears again, here is my code.
var builder = new Android.App.AlertDialog.Builder(Activity);
var view = Activity.LayoutInflater.Inflate(Resource.Layout.dialog, null);
Android.App.AlertDialog dialog = null;
builder.SetView(view);
dialog = builder.Create();
dialog.SetButton2("CANCEL", delegate { dialog.Dismiss(); });
dialog.Show();
any idea what i might be doing wrong?

Thanks so much. I was using an anonymous method/delegate btnOne.clicked += (o, e) => ...) , and it was still calling the fragment twice, had to change to a named delegate then followed your instructions, I first unsubscribed then I resubscribed. Thanks
I found the root cause in my application, I had a method called SetUp which has my event handlers included. I call it in the OnCreate method, and also I call the method from the OnResume method. Im a noob so learning as I go, might have been obvious to others, but its not so obvious when learning. Also remember OnResume is called after the OnPause, and since your main activity is paused because of the fragment, when you dismiss the fragment, OnResume is called and the event handlers called again

Related

DialogFragment disappears after device rotation despite applying common fixes

I have a DialogFragment that I launch when a user taps a menu item on the ActionBar. Everything about the Dialog functions properly - it launches just fine and it does everything I've set it up to do. Unfortunately, as soon as I rotate my device, the DialogFragment disappears.
This seemed to be a common problem back in 2012 - I've scoured StackOverflow and tried all of the common fixes that have been posted in the last couple of years. This SO post in particular summarizes all of the potential fixes that have been proposed:
Set up the DialogFragment to use the newInstance() paradigm
Add setRetainInstance(true) to the DialogFragment's onCreate()
Add a workaround to onDestroyView() to address a potential bug in the support library
Despite implementing everything above, the DialogFragment refuses to stick around after device rotation.
Here's how I launch the DialogFragment from the Activity:
DialogKanjiLookup dialog = DialogKanjiLookup.newInstance(gSearchView.getQuery());
dialog.show(getSupportFragmentManager(), "dialogKanjiLookup");
Here is the DialogFragment's newInstance():
public DialogKanjiLookup() {}
public static DialogKanjiLookup newInstance(CharSequence searchTerm)
{
DialogKanjiLookup dialog = new DialogKanjiLookup();
Bundle args = new Bundle();
args.putCharSequence(BUNDLE_SEARCH, searchTerm);
dialog.setArguments(args);
return dialog;
}
Here's the dialog's `onCreateDialog():
#Override
public Dialog onCreateDialog(Bundle savedInstanceState)
{
// Specify a layout for the dialog
LayoutInflater inflater = getActivity().getLayoutInflater();
View layout = inflater.inflate(R.layout.dialog_kanjilookup, null);
// SNIP
// ...Handle savedInstanceState, set up various Listeners and adapters...
// SNIP
// Create the actual dialog
AlertDialog.Builder builder = new AlertDialog.Builder(getActivity());
// Customize the dialog
builder.setTitle(R.string.dialog_kanji_lookup_title);
builder.setIcon(R.drawable.kanji_lookup);
builder.setPositiveButton(R.string.menu_search, btnSearchListener);
builder.setNegativeButton(R.string.cancel, null);
builder.setView(layout);
// Force the dialog to take up as much space as it can
Dialog dialog = builder.create();
dialog.show();
dialog.getWindow().setLayout(WindowManager.LayoutParams.MATCH_PARENT, WindowManager.LayoutParams.MATCH_PARENT);
// Display the dialog
return dialog;
}
There's only one instance of DialogFragment.dismiss() called from within the fragment but that's only triggered when the user taps one of the dialog's buttons, so I've ruled that out. Why does my DialogFragment still disappear after rotation? I'm all but pulling my hair out over this, it worked fine until sometime after I implemented a Navigation Drawer. Could that be a part of the problem?
EDIT: False alarm, I discovered that my answer wasn't the solution! The problem reappeared after I finished moving all of my Fragments and Activities away from using the Support Libraries.
I did discover that this problem only exists in Activities in which the content fragment has not been declared statically in the Activity's layout. That is, if I have a <FrameLayout> defined in XML and use fragmentManager.beginTransaction().replace(R.id.content_frame, frag, tag).commit(); to load a fragment, any DialogFragments launched in that activity fail to reload when the device has been rotated.
Here's a screen recording that demonstrates the issue:
https://www.youtube.com/watch?v=psK0pzMn6oc
After some experimentation I discovered a solution. The Activity that launches the dialog needs to extend android.support.v4.app.FragmentActivity, and the DialogFragment needs to extend android.support.v4.app.DialogFragment.
Then, getSupportFragmentManager() must be called when launching the DialogFragment:
CustomDialog dialog = CustomDialog.newInstance();
dialog.show(getSupportFragmentManager(), "customDialog");
This should retain the dialog during rotation. There was no need to use setRetainInstance(true) in the dialog itself.
Mind you, this only works in instances in which an FragmentActivity calls a DialogFragment. I'm still trying to suss out a way to preserve a dialog that gets called via a Fragment instead.

Understanding the AlertDialog creation on android

I'm new to Android, only three days mostly studying the basics. I ended up with this code while studying the creation of an android alert dialog:
OnClickListener oclBtnOk = new OnClickListener() {
#Override
public void onClick(View v) {
AlertDialog alertDialog = new AlertDialog.Builder(MainActivity.this).create();
alertDialog.setTitle("Title");
alertDialog.setMessage("Message");
alertDialog.setButton("OK", new DialogInterface.OnClickListener() {
public void onClick(DialogInterface dialog, int which) {
// TODO Add your code for the button here.
}
});
alertDialog.show();
}
};
But I still have somethings to get known:
1- First time I made the code I was using AlertDialog alertDialog = new AlertDialog.Builder(this).create(); instead AlertDialog alertDialog = new AlertDialog.Builder(MainActivity.this).create();. The first code wans't compiling, so, what exactly is the difference? That means the dialog builder is son or has some dependency with the MaiActivity? That means the listener from this dialog is inside the main activity?
2- Is this really the correct way to create a simple alert dialog? 'cause in my console I saw one red line saying "ActivityManager: Warning: Activity not started, its current task has been brought to the front" and also studying through http://developer.android.com/guide/topics/ui/dialogs.html what they recommend is to use AlertDialogFragment as container of my dialog..
And my last question is a little bit more complicated but a simple answer as "Yes" to lead me I a deeper research would help- android has some sort of EDT (Event Dispatch Thread) since is based on java? I mean, to process graphics (like progress bars) should I separate them into another thread?
AlertDialog.Builder requires a Context. The code is in an OnClickListener anonymous subclass and it is not a Context. this refers to the subclass instance. To refer to the this of the parent activity class, it's scoped as MainActivity.this, and activities are Contexts.
It's all right at this point of your learning curve. The "Warning: Activity not started, its current task has been brought to the front" is nothing to worry about - the app was already running and was just brought to front, not re-launched.
Android does not run AWT and there's no EDT by that name. However, the main thread (also called the UI thread) does something similar.
To answer your first question:
AlertDialog alertDialog = new AlertDialog.Builder(this).create();
The code is running in an anonymous class, so this does not reference an object derived from Context. Adding the MainActivity. makes the argument reference an Activity which is derived from Context
Your second question: Is this really the correct way....?
It is certainly one acceptable way. There are others.
The message you are seeing about bringing the activity to the front is not related to dialogs. You may eliminate it by exiting from your application before starting a new debug session.

Proper way of dismissing DialogFragment while application is in background

I started using DialogFragment, because they are working nicely through orientation changes, and stuff. But there is nasty problem I encountered.
I have AsyncTask that shows progress DialogFragment and dismisses it onPostExecute. Everything works fine, except when onPostExecute happens while application is in background (after pressing Home button, for example). Then I got this error on DialogFragment dismissing - "Can not perform this action after onSaveInstanceState". Doh. Regular dialogs works just fine. But not FragmentDialog.
So I wonder, what is the proper way of dismissing DialogFragment while application is in background? I haven't really worked with Fragments a lot, so I think that I'm just missing something.
DialogFragment has a method called dismissAllowingStateLoss()
This is what I did (df == dialogFragment):
Make sure that you call the dialog this way:
df.show(getFragmentManager(), "DialogFragment_FLAG");
When you want to dismis the dialog make this check:
if (df.isResumed()){
df.dismiss();
}
return;
Make sure that you have the following in the onResume() method of your fragment (not df)
#Override
public void onResume(){
Fragment f = getFragmentManager().findFragmentByTag("DialogFragment_FLAG");
if (f != null) {
DialogFragment df = (DialogFragment) f;
df.dismiss();
}
super.onResume();
}
This way, the dialog will be dismissed if it's visible.. if not visible the dialog is going to be dismisded next the fragment becomes visible (onResume)...
This is what I had to do to achieve what you want:
I have a Fragment activity on which i was showing a dialog fragment named fragment_RedemptionPayment which is globally declared at the top. The following code dismisses the DialogFragment if it was showing before the activity goes in background and comes back in foreground.
#Override
public void onResume() {
super.onResume();
if(fragment_RedemptionPayment.isVisible()){
fragment_RedemptionPayment.dismiss();
}
}
Another new way of checking the state before calling dismiss is this:
if(!dialog.isStateSaved){
dialog.dismiss()
} else {
//Change the UI to suit your functionality
}
In this way its is checked that state is saved or not, basically on pause and onSaveInstanceState has been called.
For Java you can use isStateSaved()
A solution that might work is setting Fragment.setRetainInstance(true) in your dialogfragment, but that's not the prettiest of fixes.
Sometimes I have noticed that I have to queue up my dialog actions to let the framework restore the state first. If you can get hold of the current Looper (Activity.getMainLooper()) and wrap that in a Handler you could try passing your dismissal to the back of the queue by posting a runnable on that queue.
I often end up using a separate fragment that it retaininstance(true) that has a ResultReceiver. So i pass on that result receiver to my jobs and handle callbacks in its onReceive (often as a router for other receivers). But that might be a bit more work than it is worth if you are using async tasks.

Android dialog dismiss() doesn't close

The custom dialog does dismiss at certain points in my program, for example when they press an "Edit" button, but the dialog will not dismiss if I select something from a list view and press an "Add" button. Both buttons end up using this same code below, but the if statements decide which will execute. Either way, the problem is that pcDialog.dismiss() is outside of the if statements, so it should dismiss always...but it doesn't.
Any ideas on what the problem might be? My dialog is declared outside of any methods as a member.
createDoneBtn.setOnClickListener(
new View.OnClickListener()
{
#Override
public void onClick(View v)
{
if ( !editingPC )
{
...
}
else if ( editingPC )
{
...
}
adapter.notifyDataSetChanged();
pcDialog.dismiss();
}
});
Maybe this one dismisses ok, but your code makes a second one to immediately appear?
Or maybe an exception is thrown but gets silently caught so you never step into dismiss() ?
Why not first debug?
And I think it's best practice to put it in try - catch - finally. Then you can call dismiss in the finally.

Dismiss dialog after screen orientation change

1) I launch a background task (via AsyncTask)
new FindJourneyTask().execute(); // FindJourneyTask extends AsyncTask
2) Still in the main thread (just before new thread is launched) I create a dialog with showDialog(dialogId)
// this method is in FindJourneyTask
protected void onPreExecute() {
showDialog(DIALOG_FINDING_JOURNEY);
}
3) Screen orientation changes and the Activity is recreated
4) How can I now dismiss the dialog from the FindJourneyTask? Calling dismissDialog(dialogId) does nothing.
// this method is in FindJourneyTask
protected void onPostExecute(FindJourneyResult result) {
dismissDialog(DIALOG_FINDING_JOURNEY); // does nothing
}
This is a common problem, and there are no real good solutions. The issue is that on screen orientation change, the entire Activity is destroyed and recreated. At the same time, the Dialog you previously had is re-created in the new Activity, but the old background task still refers to the old Activity when it tries to dismiss the dialog. The result is that it dismisses a dialog which was long ago destroyed, rather than dismissing the dialog the new orientation created.
There are three basic solutions:
Override the default orientation-handling code so that your Activity is not destroyed upon rotation. This is probably the least satisfactory answer, as it blocks a lot of code that is automatically run upon orientation changes.
Create a static member variable of your Activity that references the Activity itself, so you can call STATIC_ACTIVITY_VARIABLE.dismissDialog().
Code a solution in which the background task keeps track of the current Activity and updates itself as necessary.
These three solutions are discussed at length here: http://groups.google.com/group/android-developers/browse_thread/thread/bf046b95cf38832d/
There is a better solution to this problem now which involves using fragments.
If you create a dialog using DialogFragment, then this fragment will be responsible for maintaining your dialog's lifecycle. When you show a dialog, you supply a tag for your fragment (DialogFragment.show()). When you need to access your dialog, you just look for the necessary DialogFragment using FragmentManager.findFragmentByTag instead of having a reference to the dialog itself.
This way if device changes orientation, you will get a new fragment instead of the old one, and everything will work.
Here's some code based also in #peresisUser answer:
public void onSaveInstanceState(Bundle outState) {
AppCompatActivity activity = (AppCompatActivity) context;
FragmentManager fragmentManager = activity.getSupportFragmentManager();
DialogFragment dialogFragment = (DialogFragment) fragmentManager.findFragmentByTag("your_dialog_tag");
if(dialogFragment!=null) {
Dialog dialog = dialogFragment.getDialog();
if(dialog!=null && dialog.isShowing()) {
dialogFragment.dismiss();
}
}
}
This is long after the question was asked and answered, but i stumbled upon this problem also and wanted to share my solution...
I check in onSavedInstance() which runs on orientation change, whether the dialog is showing or not with dialog.isShowing(), and pass it into outState variable. Then in your onCreate(), you check this var if it's true. If it is, you simply dismiss your dialog with dialog.dismiss()
Hope this helps others :()
I tried adding setRetainInstance(true); on OnCreate function of DialogFragment. This will cause dialog to dismiss on rotation.
Just add this line to specific activity in your Manifest to solve this problem android:configChanges="orientation|screenSize|smallestScreenSize"
like this,
<activity
android:name=".PDFTools"
android:exported="false"
android:configChanges="orientation|screenSize|smallestScreenSize"
android:theme="#style/Theme.DocScanner.NoActionBar" />

Categories

Resources