I have defined a method for my DialogFragment which will pop another (alert) dialog, where I confirm that the user wants to dismiss the DialogFragment. If that is the case, then I call DialogFragment.dismiss(). If not, the dismissal of the DialogFragment should simply be ignored and the user should return to it as was before.
This method (say, confirmCancel()) is used for the 'Cancel' button at the bottom of the DialogFragment. Since I also want this to appear when the user presses the back button, or when they touch outside the DialogFragment, I have set confirmCancel as its onCancelListener (of course, I have also used getDialog().setCanceledOnTouchOutside(true) too).
This is the code for confirmCancel():
public void confirmCancel()
{
(new AlertDialog.Builder(getActivity())
.setIcon(R.drawable.ic_baseline_warning_24)
.setTitle("Discard changes")
.setMessage("Are you sure you want to discard changes and go back?")
.setPositiveButton("Yes", ( dialogInterface, i ) -> dismiss())
.setNegativeButton("No", ( dialogInterface, i ) -> {})
.show()).setCanceledOnTouchOutside(true);
}
This works almost perfectly, except for the fact that by the time the AlertDialog is shown on screen, the DialogFragment is already dismissed, and the actions taken in the AlertDialog are of no use at all.
So what I need now is a way to 'cancel' the dismissal of the DialogFragment, or a method that is called before the its dismissal. How do I solve this?
P.S.: getDialog().setCancelable(false) is not helpful to me since I do want the dialog to be cancelled; it's just that I want it cancelled conditionally.
Neither DialogFragment nor Dialog offer a condition that is checked before a cancel/dismiss event, that you can set.
They only offer listeners that are notified after said events have been fired, when the damage has already been done.
I have combed through the source code and have determined their is no way to force it into a state to ignore the first cancel/dismiss call but still allow the listeners to fire so you can catch the event, I have considered reflection to mess with fields, but it got to messy. I also tried forcing exceptions, to create invalid states, but there are no invalid states that would allow the functionality you want.
Here are the two sources.
https://android.googlesource.com/platform/frameworks/base/+/master/core/java/android/app/Dialog.java
https://android.googlesource.com/platform/frameworks/support/+/refs/heads/androidx-activity-release/fragment/fragment/src/main/java/androidx/fragment/app/DialogFragment.java
You could play with the containing Activity's Window to handle touch events and send them back to the DialogFragment using a FragmentResultListener, ie the DialogFragment is set to non-cancelable and when the user touches the outside the
Activity picks it up and calls setFragmentResult to send it back to the DialogFragment. This may or may not work depending on how dialogs detect/receive outside touch events, does it consume them or let them fall through, when its non-cancalable.
The easiest solution by far is to copy the source code above, probably just the DialogFragment and make your own to solve this problem.
OR OR OR
I'm totally missing the most obvious solution.
Related
In my AppCompatActivity, I am showing an AlertDialog at a specific event.
Once the AlertDialog is shown, the user should not be able to click on anything in my AppCompatActivity anymore.
I tried to disable the activity in onPause(): getWindow().setFlags(WindowManager.LayoutParams.FLAG_NOT_TOUCHABLE, WindowManager.LayoutParams.FLAG_NOT_TOUCHABLE);
And to check whether it is not paused: getLifecycle().getCurrentState().isAtLeast(Lifecycle.State.RESUMED)
But it seems that onPause() is not called quickly enough for that.
I could set a static boolean to true whenever I pause the AppCompatActivity, set it on false in onResume() and check it in every single onClickListener/onTouchListener etc., but is there a better way to do it?
Every help is appreciated!
Edit:
Thanks for your comments! I found out, that when I am clicking on the screen while the UI thread is started (for opening the AlertDialog), the click is put in a queue. That's why the click event is executed while the AlertDialog is running. Do I need to save the AlertDialog and check if it is running or is there a better way?
I believe that you should let us know more about your code. However, I think that I can guess what you're saying. There is a setEnabled(false) method for every view component in android, and if you call it on a view component java object, it'll be disabled and no longer clickable. So you can simply disable your components whenever you want to show the AlertDialog and then enable them again by calling setEnabled(true) if you wish. I'm pretty sure pausing the activity is not what you should do. Because doing it manually, is not best practice anyway, at all.
I ended up saving an instance and writing a method to check if the Activity should not be active:
private void canRun() {
return (dialog == null || !dialog.isShowing()) && getLifecycle().getCurrentState().isAtLeast(Lifecycle.State.RESUMED);
}
I have this ImageView which on clicked should Show toast and also making api on click of the image View, so I decided to use Debounce operator in Rx
but now thost is not visible on every click. below is the code
cartSubscriptionDecrement = RxView.clicks(holder.databinding
.cartQuantityLayout.ivDecreaseQuantity)
.debounce(1000, TimeUnit.MILLISECONDS)
.subscribe { next ->
cartQuantityEvent.onCartQuantityDecremented(cartSubscriptionDecrement!!)
}
I need to show toast on every single click of imageView.
I tried to have a separate onclick method for Image View but during debugging I found that the control is never coming to onclick method this is my another onclick method
How do I achieve this toast on every click while not making an api call in every click (make use of Debounce operator).
There are multiple ways to do what you're asking. I've included the simplest below, but will need to think more about potential side effects from using doOnNext.
The code for this example lives on Github here if you're interested in running the app. Just realized the package name is wrong, but the example will run just fine.
The most relevant code to the example is below. The key is that you'll need to debounce after you show the Toast rather than before.
RxView.clicks(image1)
.doOnNext(imageClickedEvent -> {
// Show toast every time onNext is called (this will also log to LogCat)
showToast();
})
.debounce(1000, TimeUnit.MILLISECONDS)
.subscribe(clickEvent -> {
makeApiCall();
});
I tried to have a separate onclick method for Image View but during debugging I found that the control is never coming to onclick method this is my another onclick method
I believe this is the case because RxClicks sets a listener on the ImageView, and that occurs after the framework sets the onClick listener and you can only have one OnClickListener on a View if I recall correctly. So what's happening is the RxView.clicks(imageView) is receiving the click event rather than the method you created separately.
You have to apply ".subscribeOn(AndroidSchedulers.mainThread())" to all RxBinding events, such as "RxView.clicks()"
Working with libGDX, and in this particular project we are using Dialog to have a box popup when the user clicks a certain button.
What I want is to be able to dismiss the Dialog by clicking outside of it.
At other times, I have used two tables, a background table and a menu table, and added a transparent background to the background table that when clicked will remove both of those tables from the Stage.
I have tried making a class that has a both a Dialog and a background table like the one mentioned above, but the background table never receives any actions.
I have also tried simply adding this background table to the stage before creating the dialog box, but this does not work either.
Finally, I have also tried to subclass Dialog, the idea being to override the show(stage) method to change its behavior, but I don't know how to do this one, and I'm not sure if it would work, anyway.
I believe the problem is that dialog.show(stage) changes the situation in the stage to only accept clicks inside the Window of the dialog box. I have seen this question about adding a close button to a dialog box, but playing with the clipping settings is not working to fix this problem.
There is also the possibility that when show() calls the pack() method and does its layout thing that something is happening that is making what I am trying to do impossible. I think that the solution will be overriding show() or overriding pack(), or both, but I don't know how to do this.
I can post code if need be, but this should be a pretty complete description of what I have tried and what I need to accomplish.
I know this is an old question but for those like me that searched the entire web for an answer only to find it inside libGDX code, the answer to .close() a libGDX dialog by code is simply to call the method
dialog.hide();
EDIT (added from the comments below):
so all he needs to do is register a global touch down event and see if
the touch has happened inside the Rectangle of his dialog, if not,
close it
I have an application containing multiple activities.
At the moment the whole application contains about 8 activities. First I show a splash screen for a few seconds where all the preferences are loaded and set up (from sharedPreferences) - these are saved in a "Setting" class I made for this - this class basicly just have a bunch of static variables, so all activities in the app can read these and modify them as they need to.
EDIT: More data is getting stored in this class as the app runs, some of this is from a webservice - the data is parsed into obejcts and references to these obejcts are saved in the Settings class too (or a list of the objects).
My problem is then, that when users press the HOME key, the current activity is put in the background. If the activity is in the background for a long time (a lot of users "close" apps by pressing home instead of back), and then reopened it shows the activity that was running before HOME was pressed.
As an example - lets say the user starts the app, sees the Splash screen for a few seconds. The splash screen then starts a new activity and calls finish on itself.
This means that now the Activity stack is just the MainActivity (main menu). In the MainActivity I supply all the buttons with listeners in the onCreate method, and most buttons require some info from the Settings class mentioned above.
When I then press HOME and reopens the app a few hours later, none of the buttons works anymore - seems the graphic is reacting etc, but something still goes wrong. One of the buttons that should work even with all settings wiped will just open a dialog with some text in it.
The listener:
Button b = (Button)v.findViewById(R.id.id_b1);
b.setOnClickListener(new OnClickListener() {
public void onClick(View v) {
Dialog dialog = createDialog(MainActivity.this, DIALOG_CONST1);
dialog.show();
}
});
I have some constantst for dialog types, as I'm using some custom designs for the dialogs I have created a class to created the dialogs for me.
From the activity containing the button (simplified a bit):
public static Dialog createDialog(final Context c, int dialogId) {
Dialog dialog = null;
CustomDialog.Builder customBuilder;
switch (dialogId) {
...
case d1:
customBuilder = new CustomDialog.Builder(c, DIALOG_CONST1);
//Sets up the parapters to create the dialog afterwards
customBuilder.setTitle("Header").setMessage("Content")
.setPositiveButton("", new DialogInterface.OnClickListener() {
public void onClick(DialogInterface dialog, int which) {
dialog.dismiss();
}
});
dialog = customBuilder.create(); //Creates the dialog from the above settings
dialog.setCanceledOnTouchOutside(true);
break;
...
}
... //Set the width of the dialog
return dialog;
}
The CustomDialog class extends the Dialog class and then depending on the const it
s supplied with it inflates one of several dialog layouts and adds content etc. Works great normally, but after the app have been paused by HOME for a while, and things go wrong no dialog is shown when I press the button. I do se a flash of a loading dialog on the other buttons, but nothing happens afterwards - no dialog is shown. Strange thing is that if I press on the button again in the middle of the screen, I can't press it (grahics dont react), but when pressing once in the side of the screen and then in the middle the graphics do react, the dialogs does get cancelled when pressing outside, so I'm wondering if there is some very slim "transparent" dialog in the middle or something - I just have no clue why this would happen.
EDIT: Actually looking through the variables I save in Settings class I see that I do save the width and height in pixels of the screen. Width used to set the width of the custom dialogs to a certain % of the screen width. IF the Settings gets wiped, that might explain why I don't see any dialogs as width is set to 0... I really can't seem to figure what is happening if it's not all my variables in the Settings class that gets wiped - everything really do point to that.
dialog.getWindow().setLayout(Settings._widthpx - (Settings._widthpx/5), dialog.getWindow().getAttributes().height); //Width = 80%
Actually I have to admit that I don't really know what is causing this, as I'm rather new to Android. I suspect that the GC does delete all my variables after a while when the Settings class haven't been used - causing all settings to be wiped when the user returns a few hours later. This however does not explain why the buttons doesn't work (one of them require no setting).
This main activity can start other activities, and these can again launch new activities.
I think all my problems can be solved if I can just force the whole app to close when HOME is pressed, and thereby force it to be started from scratch whenever the icon is pressed - forcing the listeners on the buttons and the settings to be loaded.
I read on here about "android:clearTaskOnLaunch", " android:launchMode" and "android:finishOnTaskLaunch" but I'm not quite sure how to use these correct.
Anyone who could either explain to me why the buttons does not work, or what might happen to the variables in my Settings class when the app has been in the background for a while, or maybe give me a few good hints on how to use the "activity" settigns properly.
EDIT: The app will be running Android 1.6+, so I can't use any newer functions or anything...
Thank you very much
DO NOT override home key functionality. Maybe you'll find something, somewhere, that would allow you to do this. A god-fearing, standards-embracing application would not in any way override the home key.
Just put android:clearTaskOnLaunch=true in your manifest. This will ensure that your main activity will be launched everytime you press the launcher icon.
I suspect that the GC does delete all my variables after a while when
the Settings class haven't been used - causing all settings to be
wiped when the user returns a few hours later
Android is not that evil to do this to your app. It may kill your application, services, and whatever is running on the background after some time of inactivity and/or need for more memory, but will never leave your application hanging in there without your variables.
I had actually customized this for android 2.0 development 2.0.May be better options are available now.What I did was to declare all activities global and when you click home button from anywhere we check whether each of them is not null.If not null close them and set references to null.The activities are only declared globally.They are defined only when they are to be used.
If you are using Android 4.0 or higher, you can enable "Don't keep activities" in Developer options?. If you don't have a 4.0 device, then use the emulator.
you have to kill all the activities that are on the top of current activity on Home button and
for do that you have to override Home key functionality in which you have to write
Intent intent = new Intent(context,login.class);
intent.setFlags(Intent.FLAG_ACTIVITY_CLEAR_TOP);
startActivity(intent);
this will clear all the activities on the top of that activity where you press home key and switch it to login activity and
if you don't want to override Home key functionallity then you have to write only one in Androidmanifest.xml that is android:clearTaskOnLaunch=true from this when ever you click on app icon it will starts your 1st activity
I Suggest the second approach is best suted for you.
For posterity, in my app I just added finish(); in the onStop() method after everything i do there and it worked as a charm.
I'm facing a head-scratching moment similar to what this person (from Jan 2008) experienced when I realized that there is no cancel button in Android's progress dialog or spinners. It is now July 2009 and I've just installed the cupcake version of Android. Has this thing changed? If not, are you adding a cancel button into the dialogs and how do you do it?
not sure about the whole cancel button...i've heard reports of the onCancel() method not firing properly. my solution just consists of making a normal button on the dialog with a call to return whenever the button is pressed.
private void createCancelProgressDialog(String title, String message, String buttonText)
{
cancelDialog = new ProgressDialog(this);
cancelDialog.setTitle(title);
cancelDialog.setMessage(message);
cancelDialog.setButton(buttonText, new DialogInterface.OnClickListener()
{
public void onClick(DialogInterface dialog, int which)
{
// Use either finish() or return() to either close the activity or just the dialog
return;
}
});
cancelDialog.show();
}
then just use a simple call method from elsewhere in your activity
createCancelProgressDialog("Loading", "Please wait while activity is loading", "Cancel");
rather simple solution, but it does the trick ;)
also just to note that cancelDialog is an activity wipe variable, if you dont need to call it from elsewhere, then you should be able to get away with just limiting the scope of the variable to that method.
I'm no Android user or developer, but I think the answer given in the linked-to thread is pretty decent: there's a hardware "back" key on all Android devices. Users are supposed to know to press Back to back out of whatever activity they're currently in.
Thus, the UI designers feel it's unnecessary to include a GUI back/cancel button. This could be viewed as the UI application of the DRY principle; if there's already one way of doing something, that's enough.
The hardware key is the answer here. I'd be careful about generalising the DRY principle to UIs. There are plenty of cases where you need to hammer, hammer, hammer the same point to the user repeatedly via headings, body text, colours and images.
Users dont "read" UIs the way you read a novel. They scan read.
I can't speak for other apps, but in mine anything that might cause the UI thread to wait is executed in a seperate thread. The most I'll do is show a small progress spinner in the titlebar to let the user know something is going on in the background.
As an Android user, and developer, I can say, in my opinion, and based around my understanding of the platform, that there is a good reason for not having a cancel button by default on the cancel-free progress dialogs.
As a developer, these dialogs can not be cancelled by default, that is, you have to explicitely set them as cancelable.
This makes sense, because their purpose is to alert the user, via the UI thread, that some work is going on elsewhere that is important to the updating of the UI thread, before the user should continue their use of the application.
For example, when fetching a list of data to occupy an empty screen, there is nothing to interact with, and the user needs to be made aware that something is going on, and to expect there to be nothing available to interact with until this process is complete.
However, there may be cases, such as data retrieval from the internet, where the process is "sketchy" and due to connectivity issues, you may not be able to complete the request, and get stuck here.
This as a develop is where you enable the dialog to be cancel-able.
Now as a user, one that clearly understands the UI paradigm of Android, I know that if I want to go back to what I was doing before, or "cancel" the current operation, I should hit the back key.
As a user, it's no different to knowing that in Android, the menu key can often reveal hidden options, some times on a seemingly blank screen with no way to interact.
This is the behaviour a user expects, and the way the platform is designed. You could very well add a cancel button, but from a users perspective that understands their phone, this makes no difference, the back key is clearly the intended choice for this purpose.
Activities and UIs have a flow, you flow through activities and UI "screens" which are "stacked" and the back button essentially "pops" the last thing off the stack to return you to where you were previously. If you see the dialog as another of these activities, you want to pop it from the top of the stack to return to what is underneath, or an activity previous to that.
If a developer has a dialog that can not be cancelled by back, it is either, by design, for which there can, in cases, be very good reason for, or, it is poor development and an oversight on the devloper's part.