How to avoid launching Fragment on orientation change? - android

When an EditText line in the UI gains focus, a DatePickerFragment launches for the user to enter a date. On orientation change, if the EditText line has focus and a previously entered date (length() > 0) and is launching a DatePickerFragment due to the below code. I don't want the DatePickerFragment to launch though after an orientation change. Is there a way to modify or add code so that if the Activity is newly created and the EditText line has focus it won't automatically launch the DatePickerFragment ? Would it be a good idea to do something in onResume()? Or do something with !=null?
ListenerEditText.setOnFocusChangeListener(new OnFocusChangeListener() {
#Override
public void onFocusChange(View v, boolean hasFocus) {
...
if (hasFocus && (fListenerEditText.getText().length() == 0)) {
DatePickerFragment newFragment = new DatePickerFragment();
newFragment.show(getSupportFragmentManager(), "datePicker");
}
else if (hasFocus) {
...
DatePickerFragment newFragment = new DatePickerFragment();
newFragment.show(getSupportFragmentManager(), "datePicker");
}
}
});

If you have requestFocus() on your Edittext remove it also go for something like this; move the below line outside the interface
DatePickerFragment newFragment = new DatePickerFragment();
and inside the interface go for a boolean attack
if(newFragment.isShow())// true if shown false if not
and your if statment checks out this way
if & only if EditText has focus and the length is 0 show dialog, or if it has focus show dialog,there is no greater than 0 here
Hope i helped

Before adding a new DatePickerFragment check to see if one already exists. If it doesn't exist then go ahead and add the DatePickerFragment. I'm assuming that "datePicker" is the tag that is used for the fragment transaction. You can check for the fragment by using
if (getSupportFragmentManager().findFragmentByTag("datePicker")==null) {
//Add new DatePickerFragment
}
Let me know if that helps out.

Related

what is correct approach to perform UI related task when a dialog gets dismissed

I have a dialog with onDismiss handler:
public class TextReaderDialog extends DialogFragment {
...
public void onDismiss() {
}
I show this dialog and add some styles to a part of text from the fragment:
TextReaderDialog d = new TextReaderDialog();
d.show(getFragmentManager(), "sample");
Spannable spannableText = new SpannableString(tv.getText());
spannableText.setSpan(new BackgroundColorSpan(Color.LTGRAY), startOffset, startOffset + w.word.length(), Spannable.SPAN_EXCLUSIVE_EXCLUSIVE);
tv.setText(spannableText);
Whenever a dialog is dismissed, I want to remove styles from the text. How can I do that? What is the correct way to do that?
The simplest way to go about this would be to add a method to your fragment like so:
public void dismissStyles(){
//do your style dismissing here
}
Now, I assume in the dialog you are overriding DialogFragment.onDismiss(DialogInterface dialog). As long as that is the case, once you have completed that method, in your dialog's onDismiss function, you can do something to the effect of:
#Override
public void onDismiss(DialogInterface dialog) {
super.onDismiss(dialog);
MyFragment fragment = (MyFragment) activity
.getFragmentManager()
.findFragmentByID(R.id.containerOfYourFragment);
if(fragment != null){
fragment.dismissStyles();
}
}
Here, activity should be the current activity that your fragment and dialog are hosted in. You can pass this to the dialog in a constructor, or depending on where the dialog is located. You could also just pass the current fragment to the dialog in the constructor as well, and then it would simply be called by myFragment.dismissStyles();.

How to have multiple datepickers on one Android screen?

I created one screen in my Android app which contains a datepicker like so:
TextView userDateView = (TextView) getView().findViewById(R.id.text_user_date);
userDateView.setOnClickListener(new View.OnClickListener() {
#Override
public void onClick(View v) {
DatePickerFragment newFragment = new DatePickerFragment();
newFragment.setListner(OverviewFragment.this);
newFragment.show(getFragmentManager(), "datePicker");
}
});
and I created a callback which receives the date back and updates the userDateView, which all works fine. In another screen I now want to have two dates next to eachother which the user can both select. Unfortunately the callback doesn't know from which of the two dates the user started the datepicker fragment.
Does anybody know how I can somehow know from which button the datepickerFragment was started? All tips are welcome!
A simple approach is to supply a parameter to each DatePickerFragment and send this parameter back to your callback, like requestCode on Activity.onActivityResult.
See how to supply parameters to Fragments:
http://developer.android.com/reference/android/app/Fragment.html#setArguments(android.os.Bundle)
Btw, you can find a different approach here:
Multiple DatePickers in same activity
Try something like this:
DatePickerFragment newFragment = new DatePickerFragment();
newFragment.setListner(new DatePickerDialog.OnDateSetListener() {
#Override
public void onDateSet(DatePicker view, int year, int monthOfYear, int dayOfMonth) {}
});
newFragment.show(getFragmentManager(), "datePicker");

How to correctly dismiss a DialogFragment?

The docs say this for the dismiss() method from the Dialog class:
Dismiss this dialog, removing it from the screen. This method can be invoked
safely from any thread. Note that you should not override this method to do
cleanup when the dialog is dismissed, instead implement that in onStop().
In my code, all I do is call getDialog().dismiss() to dismiss it. But I am not doing anything else or even using onStop(). So I am asking exactly how to correctly dismiss a DialogFragment to avoid any memory leaks, etc..
tl;dr: The correct way to close a DialogFragment is to use dismiss() directly on the DialogFragment.
Details: The documentation of DialogFragment states
Control of the dialog (deciding when to show, hide, dismiss it) should be done through the API here, not with direct calls on the dialog.
Thus, you should not use getDialog().dismiss(), since that would invoke dismiss() on the dialog. Instead, you should use the dismiss() method of the DialogFragment itself:
public void dismiss()
Dismiss the fragment and its dialog. If the fragment was added to the back stack, all back stack state up to and including this entry will be popped. Otherwise, a new transaction will be committed to remove the fragment.
As you can see, this takes care not only of closing the dialog but also of handling the fragment transactions involved in the process.
You only need to use onStop if you explicitly created any resources that require manual cleanup (closing files, closing cursors, etc.). Even then, I would override onStop of the DialogFragment rather than onStop of the underlying Dialog.
I think a better way to close a DialogFragment is this:
Fragment prev = getSupportFragmentManager().findFragmentByTag("fragment_dialog");
if (prev != null) {
DialogFragment df = (DialogFragment) prev;
df.dismiss();
}
This way you dont have to hold a reference to the DialogFragment and can close it from everywhere.
Why don't you try using only this code:
dismiss();
If you want to dismiss the Dialog Fragment by its own. You can simply put this code inside the dialog fragment where you want to dismiss the Dialog.
For example:
button.setOnClickListener(new View.OnClickListener() {
#Override
public void onClick(View v) {
dismiss();
}
});
This will close the recent Dialog Fragment that is shown on the screen.
Hope it helps for you.
I gave an upvote to Terel's answer. I just wanted to post this for any Kotlin users:
supportFragmentManager.findFragmentByTag(TAG_DIALOG)?.let {
(it as DialogFragment).dismiss()
}
Kotlin Version of Terel answer
(fragmentManager.findFragmentByTag(TAG) as? DialogFragment)?.dismiss()
You should dismiss you Dialog in onPause() so override it.
Also before dismissing you can check for null and is showing like below snippet:
#Override
protected void onPause() {
super.onPause();
if (dialog != null && dialog.isShowing()) {
dialog.dismiss();
}
}
There are references to the official docs (DialogFragment Reference) in other answers, but no mention of the example given there:
void showDialog() {
mStackLevel++;
// DialogFragment.show() will take care of adding the fragment
// in a transaction. We also want to remove any currently showing
// dialog, so make our own transaction and take care of that here.
FragmentTransaction ft = getFragmentManager().beginTransaction();
Fragment prev = getFragmentManager().findFragmentByTag("dialog");
if (prev != null) {
ft.remove(prev);
}
ft.addToBackStack(null);
// Create and show the dialog.
DialogFragment newFragment = MyDialogFragment.newInstance(mStackLevel);
newFragment.show(ft, "dialog");
}
This removes any currently shown dialog, creates a new DialogFragment
with an argument, and shows it as a new state on the back stack. When
the transaction is popped, the current DialogFragment and its Dialog
will be destroyed, and the previous one (if any) re-shown. Note that
in this case DialogFragment will take care of popping the transaction
of the Dialog is dismissed separately from it.
For my needs I changed it to:
FragmentManager manager = getSupportFragmentManager();
Fragment prev = manager.findFragmentByTag(TAG);
if (prev != null) {
manager.beginTransaction().remove(prev).commit();
}
MyDialogFragment fragment = new MyDialogFragment();
fragment.show(manager, TAG);
CustomFragment dialog = (CustomDataFragment) getSupportFragmentManager().findFragmentByTag("Fragment_TAG");
if (dialog != null) {
dialog.dismiss();
}
Adding to the other answers, when having a DialogFragment that is full screen calling dismiss() won't pop the DialogFragment from the fragment backstack. A workaround is to call onBackPressed() on the parent activity.
Something like this:
CustomDialogFragment.kt
closeButton.onClick {
requireActivity().onBackPressed()
}
I found that when my fragment was defined in the navigation graph with a <fragment> tag (for a full screen dialogfragment), the dialogfragment would not dismiss with the dismiss() command. Instead, I had to pop the back stack:
findNavController(getActivity(), R.id.nav_host_fragment).popBackStack();
However, if the same dialogfragment was defined in the navigation graph with a <dialog> tag, dismiss() works fine.
Just call dismiss() from the fragment you want to dismiss.
imageView3.setOnClickListener(new View.OnClickListener() {
#Override
public void onClick(View view) {
dismiss();
}
});
Consider the below sample code snippet which demonstrates how to dismiss a dialog fragment safely:
DialogFragment dialogFragment = new DialogFragment();
/**
* do something
*/
// Now you want to dismiss the dialog fragment
if (dialogFragment.getDialog() != null && dialogFragment.getDialog().isShowing())
{
// Dismiss the dialog
dialogFragment.dismiss();
}
Happy Coding!
Here is a simple AppCompatActivity extension function, which closes opened Dialog Fragment:
fun AppCompatActivity.whenDialogOpenDismiss(
tag: String
) {
supportFragmentManager.findFragmentByTag(tag)?.let {
if(it is DialogFragment) it.dismiss() }
}
Of course you can call it from any activity directly.
If you need to call it from a Fragment just make the same extension function about Fragment class

Reset Android DialogFragment

I am using a custom DialogFragment to let a user change his login credentials. There are some text fields and two buttons (save/cancel). The layout is set in DialogFragment's onCreateView method.
If I open the dialog text fields are filled with default values. When the user changes text in a text field and clicks the cancel button the dialog is dismissed. Next time the dialog opens the text field changed before does not contain the default value as i expected but the text the user changed before. The text fields are not reset. This is almost the same problem mentioned here Reset an Android Dialog. The problem is that the solution provided refers to a Dialog which is deprecated in API level 11 and i cannot use onPrepareDialog with a DialogFragment.
Is there a similar way to reset the content of a DialogFragment?
You can override onResume() in your class, which extends DialogFragmet, as follows:
private static class MyDialogFragment extends DialogFragment {
public static MyDialogFragment newInstance() {
// ...
}
#Override
public Dialog onCreateDialog(Bundle savedInstanceState) {
// ...
}
#Override
public void onResume() {
super.onResume();
Dialog dialog = getDialog();
// reset code goes here - use dialog as you would have in onPrepareDialog()
}
}
You can also use .setText() method in Your activity as reaction after negative button click. Eg:
In DialogFragment.java, onCreateDialog(...)define AlertDialog.Builder
AlertDialog.Builder builder = new AlertDialog.Builder(getActivity());
then
//this is better than creating button in layout
builder.setNegativeButton(R.string.button_cancel,
new DialogInterface.OnClickListener() {
public void onClick(DialogInterface dialog, int whichButton) {
((MainAct) getActivity()).cancelDialog(DialogFragment.this);
}
}
);
In MainActivity.java create method cancelDialog(DialogFragment df) {
//here use df to reset text fields
}

DialogFragment and back button

Is there any possibility to intercept the key button in DialogFragment? sorry for the naive question.. the onBackPressed of my FragmentActivity is never called.
thanks in advance
if (imageFile.exists()) {
ShowPicDialog newFragment = ShowPicDialog.newInstance();
FragmentTransaction ft = manager.beginTransaction();
Fragment prev = manager.findFragmentByTag("picDialog");
if (prev != null) {
ft.remove(prev);
}
ft.addToBackStack("picDialog");
newFragment.getArguments().putString("path", imageFile.getAbsolutePath());
newFragment.show(ft, "picDialog");
}
sorry I added the snip of code I use to show the dialog.
It's hard to say for sure what the issue is, since you haven't posted any code. But my first guess is that you haven't added the DialogFragment to the back stack by calling the addToBackStack method of the FragmentTransaction that you're using to add your fragment to the activity.
There are examples right in the Android documentation pages that give examples of a good pattern for using a DialogFragment in your Activity.
Since you are displaying a Dialog, the created Dialog will receive the key events, not the parent Activity. So, set a Dialog.OnKeyListener when you create the Dialog's fragment, and call setCancelable(false) on the Dialog to prevent the back key from dismissing it. You can then handle the back key in your OnKeyListener's onkey method.
Best way to Handle DialogFragment with back button:
#Override
public Dialog onCreateDialog(Bundle savedInstanceState) {
return new Dialog(getActivity(), getTheme()){
#Override
public void onBackPressed() {
// On backpress, do your stuff here.
}
};
}
Rahul Pundhir's answer works great if you aren't using the builder pattern. If you are using the Builder pattern on your dialog you can instead do this:
#Override
public Dialog onCreateDialog(Bundle savedInstanceState) {
AlertDialog alertDialog = new AlertDialog.Builder(getContext())
.setTitle(...)
.setPositiveButton(...)
.setNegativeButton(...)
.setMessage(...)
.create();
alertDialog.setOnKeyListener((dialog, keyCode, event) -> {
if (keyCode == KeyEvent.KEYCODE_BACK
&& event.getAction() == KeyEvent.ACTION_UP) {
// TODO do the "back pressed" work here
return true;
}
return false;
});
return alertDialog;
}
This works by mimicking how the system calls onBackPressed() in the first place (ignoring the tracking and listening for ACTION_UP). See the source on Dialog

Categories

Resources