Android: FragmentTransaction hide doesn't work for DialogFragment - android

I'm trying to add a
fragmentTransaction.hide(myDialogFragment);
fragmentTransaction.addToBackStack(null);
to a FragmentTransaction so that the dialog will re-appear when the user hits the back button, but it's not working. I originally overrode onCreateDialog in my DialogFragment, but I noticed that the documentation for the hide call on FragmentTransaction states:
This is only relevant for fragments whose views have been added to a
container.
So instead, now I'm overriding onCreateView. Now it sort of hides, but not really. The dialog merely shrivels, but the window still remains dark. I have to hit the back button to get rid of it, which is not the behavior I want, obviously. What am I missing here?

A DialogFragment maintains a dialog internally and calls show and hide methods on it according to its own lifecycle. Calling FragmentTransaction.hide() just tries to set the visibility of the fragment's view, as returned by Fragment.onCreateView(), to View.GONE. The view of the DialogFragment is coincidently the view used for its internal dialog, and so what you are doing is hiding the content on the dialog. Unfortunately, hiding the view does not 'dismiss' the dialog and so the screen will still be dimmed.
When you call DialogFragment.show(FragmentTransaction,String), a FragmentTransaction is created to add it to the FragmentManager. Ordinarily, showing the dialog is considered the 'active' transaction, and then dismissing it is just popping the back stack the appropriate number of times. If you did not add any other fragments in between, then a new FragmentTransaction is created with a remove operation. If we could access this, then we could just add a backstack entry and make this operation reversible. Unfortunately, this is not possible and so the best we can do is just to make our own dismiss method (and hope the internal state does not get too screwed up):
public class UndoDialogFragmentActivity extends FragmentActivity {
#Override
public void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.main);
Button button = (Button) findViewById(R.id.button);
button.setOnClickListener(new OnClickListener() {
#Override
public void onClick(View v) {
// show a dialog fragment in the normal way
new MyDialogFragment().show(getSupportFragmentManager(), "dialog");
}
});
}
private static class MyDialogFragment extends DialogFragment {
#Override
public void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setStyle(STYLE_NO_TITLE, getTheme());
// do not allow back button to dismiss dialog; confusing behaviour otherwise!
setCancelable(false);
}
#Override
public View onCreateView(LayoutInflater inflater, ViewGroup container, Bundle savedInstanceState) {
Button button = new Button(getActivity());
button.setText("Dismiss");
button.setOnClickListener(new OnClickListener() {
#Override
public void onClick(View v) {
// pressing back after 'dismissing' the dialog will cause it to be added again
getFragmentManager().beginTransaction().remove(MyDialogFragment.this).addToBackStack(null).commit();
}
});
return button;
}
}
}
Clicking on the button in the fragment will cause a DialogFragment to be opened, with its own dismiss button. After pressing dismiss, you can show the dialog again by pressing the back key, undoing the remove operation. This produces somewhat questionable behaviour when you allow the back key to both show and hide the dialog, but the details can be decided by you according to your application.

I was able to hide a dialog of a DialogFragment by calling getDialog().hide() from within my DialogFragment.

If you're using API Level 11 or higher, you can simply call dismiss() on the DialogFragment, either from FragmentActivity or from DialogFragment itself.

Related

Android Navigation Component not working with Dialog Fragments

Disclaimer: I've checked the documentation and since 2.1.0 the navigation components has supported Dialog Fragments. (https://developer.android.com/jetpack/androidx/releases/navigation#2.1.0)
Error That I'm Getting
I'm getting this error when trying to go from a DialogFragment to my Start Destination:
java.lang.IllegalStateException: Fragment PostDistressDialog{829f5d1} (bbbc4926-684b-491b-9772-e0f0ffebe0af)} not associated with a fragment manager.
PostDistressDialog is a DialogFragment called from JournalEntryFragment(can be seen in map below) using the navigation component. PostDistressDialog is not an inner class of JournalEntryFragment. It is in a class of its own extending DialogFragment
Picture of my Navigation Graph
Function Calling NavController
public class PostDistressDialog extends DialogFragment implements ISaveDatabase {
...
#NonNull
#Override
public Dialog onCreateDialog(#Nullable Bundle savedInstanceState) {
if (getArguments()!=null) {
...
// Set up the Alert Dialog
AlertDialog.Builder alertDialog = new AlertDialog.Builder(getContext());
alertDialog.setTitle(R.string.distressed_levels);
alertDialog.setMessage(R.string.distressed_how_feel_post);
// Inflate and set the layout for the dialog
View layout = View.inflate(getActivity(), R.layout.dialog_seekbar, null);
alertDialog.setView(layout);
....
// Add okay button
alertDialog.setPositiveButton(R.string.okay, new DialogInterface.OnClickListener() {
public void onClick(DialogInterface dialog, int id) {
// Save post distress value in Journal Entry
mJournalEntry.setPostDistress(mTempDistressValue);
// Save to Journal Entry to database
// Check if journal entry empty
if(isJournalEntryEmpty(mJournalEntry)){
...
}
else{
// Give title if empty
if(mJournalEntry.getTitle().isEmpty()) {
....
// Save to database
new SaveDatabase(getContext(),PostDistressDialog.this).execute(mJournalEntry);
}
// Go to main menu
}
});
return alertDialog.create();
}
return null;
}
...
#Override
public void databaseSavingCompleted(){
NavHostFragment.findNavController(this).navigate(PostDistressDialogDirections.postDistressDialogToJournalListAction());
}
}
Where this is public class PostDistressDialog extends DialogFragment
Dialog in my Navigation XML File
<dialog
android:id="#+id/postDistressDialog"
android:name="com.dgrullon.cbtjourney.dialogs.PostDistressDialog"
android:label="PostDistressDialog" >
<argument
android:name="postDistressDialogArguments"
app:argType="com.dgrullon.cbtjourney.pojo.JournalEntries"/>
<action
android:id="#+id/postDistressDialog_to_journalListAction"
app:destination="#id/journalList"
app:popUpTo="#id/journalList"
app:popUpToInclusive="true" />
</dialog>
AlertDialog automatically dismisses the Dialog (and hence, removes your DialogFragment) when the callback you add to setPositiveButton is fired. Because you're doing work asynchronously, your databaseSavingCompleted method is called after the DialogFragment is destroyed, detached from the FragmentManager, and removed from the NavController - you're leaking a reference to your DialogFragment (as it would otherwise be garbage collected).
Therefore when NavHostFragment.findNavController(this) fires, all hooks that would let it access the NavController are already cleaned up.
If you don't want your button to immediately dismiss the dialog, you need to pass in null to setPositiveButton() and instead get a reference to the button after the dialog has been created by calling its getButton() API and manually setting an OnClickListener that would kick off your AsyncTask (and disable the button to prevent it from being clicked more than once).

Android custom Dialog not showing after FragmentActivity recreate

On my app i have a ViewPager inside FragmentActivity that contain a fragments. In one of that fragment (called FragOne) i have a button that on tap open a custom Dialog (a Class extends android.app.Dialog) on certain position of screen:
myButton.setOnClickListener(new View.OnClickListener() {
#Override
public void onClick(View view) {
MyCustomDialog = new MyCustomDialog(getContext());
dialog.getWindow().setFlags(
WindowManager.LayoutParams.FLAG_NOT_FOCUSABLE, WindowManager.LayoutParams.FLAG_NOT_FOCUSABLE);
dialog.getWindow().getDecorView().setSystemUiVisibility(
getActivity().getWindow().getDecorView().getSystemUiVisibility());
dialog.show();
dialog.getWindow().clearFlags(WindowManager.LayoutParams.FLAG_NOT_FOCUSABLE);
dialog.getWindow().setLayout(myWidth, myHeight);
}
});
When user perform some action on dialog some tasks are executed, recreate() of FragmentActivity is called, then dialog dismiss itself.
Now happen that if try to open again the dialog by press the button, i can't see it anymore. For make dialog appear i need to go to change viewpager current visible fragment, then go back to FragOne.
How can i do for let open dialog again without change fragment and go back to FragOne?

How to make changes in widget in parent activity based on the logic written in fragment

As an exercise i am writing a simple Ebook reader app. This has only 1 Main Activity with 3 widgets on it, 2 buttons (Previous, Next) and one Fragment container(I have used Frame Layout). All pages are different fragments that I have created that will go inside the container, and these fragments have only 1 scrollable text view that will only display text. when "next" button is pressed it should go to page2(fragment2) and when previous is pressed it should go back(previous fragment).
My problem is I don't want the "previous" button to show up on the initial screen (page1) and similarly the "next" button should not be observed on the last page.
The approach i tried was, in my fragment1(page1) class, i wrote an if condition like,
#Override
public View onCreateView(LayoutInflater inflater, #Nullable ViewGroup
container, #Nullable Bundle savedInstanceState) {
View view=inflater.inflate(R.layout.fragmentpage1,container,false);
textView1=(TextView)view.findViewById(R.id.tview);
**View listView = getActivity().findViewById(R.id.previous);**
textView1.setText(R.string.page1);
if (textView1.isEnabled()){
listView.setVisibility(View.INVISIBLE);
}
return view;
}
I am checking if textview1(the first fragment) is present or not , if yes then hide the previous button on the Main Activity. This works but it completely hides the previous button even when i go to page2. I tried all possible "is" options but none of them are giving me the results I want.
One workaround that i found was to add "setvisibility" of previous button to all fragments, so on fragment1 it is invisible and then on fragment2 i changed that to visible. But that becomes lengthy if there are 100s of fragments(pages).
Please provide me with a simple solution, I am new to Android.
Below is my Main Activity code:(Do let me know if any changes needs to be done to make code more clean)
public class MainActivity extends FragmentActivity {
Fragment fragment;
FragmentManager fm=getSupportFragmentManager();
FragmentTransaction ft;
private static Button next;
private static Button previous;
#Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_main);
next=(Button)findViewById(R.id.next);
previous=(Button)findViewById(R.id.previous);
create();
}
public void create(){
fragment= new FragmentPage1();
ft= fm.beginTransaction();
ft.add(container,fragment);
ft.commit();
}
public void next(View view){
fragment= new FragmentPage2();
ft= fm.beginTransaction();
ft.replace(container,fragment);
ft.commit();
}
}
Maybe you can try to use the getItem() method in FragmentPagerAdapter class.
Try to disable or enable your buttons using the switch case. Hope this can help =)

DialogFragment dismiss() does not pop backstack

I have a simple DialogFragment that calls dismiss when exits, according to the documentation:
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.
however, I found that the fragment is still on the backstack after calling dismiss() so I have to click back button to clear it. Does anyone know why ?
here's my code:
public void onCreate(Bundle b) {
super.onCreate(b);
setContentView(R.layout.test_layout);
class MyDialogFragment extends DialogFragment implements OnClickListener{
#Override
public View onCreateView(LayoutInflater inflater, ViewGroup container, Bundle savedInstanceState) {
View v = inflater.inflate(R.layout.hello_world, container, false);
Button b = (Button)v.findViewById(R.id.btn);
b.setOnClickListener(this);
return v;
}
#Override
public void onClick(View v) {
dismiss();
}
}
getFragmentManager().beginTransaction().add(android.R.id.content, new MyDialogFragment(), "test").addToBackStack("b").commit();
}
#Override
public void onBackPressed() {
if (getFragmentManager().getBackStackEntryCount() > 0 ){
getFragmentManager().popBackStack();
} else {
super.onBackPressed();
}
}
}
I also found out that if I don't override onBackPressed(), the back button simple doesn't work, no matter how many fragments I add to the activity, the back button always exits the activity right away.
I can confirm what #Luksprog said in his comment: the dialog must be started through show(FragmentTransaction, String).
Note after looking the source: make sure to call addToBackStack(String) on the supplied transaction or else it still won't work.
That it's a wrong way to create a DialogFragment.
Never ever use the FragmentManager to show a DialogFragment. To be shown there are a method called show(FragmentTransacion, String).
In java:
MyDialogFragment mDialogFragment = new MyDialogFragment();
mDialogFragment.show(getFragmentManager(), "MyDialogFragment");
For another hand, to dismiss the dialog just do this:
mDialogFragment.dismiss()
Another think that I would like to highlight is that the MyDialogFragment class is defined inner onCreate method :'(
Please, define the class outside the method or in another file if you want :)
Good Look!
dismiss()
findNavController().navigate(FirstBottomSheetDialogDirections.actionFirstSheetToSecondSheet())
This code is always the wrong thing to do: dismiss() is an asynchronous operation that doesn't actually dismiss anything immediately. That is unlike the navigate() which does immediately update the NavController's state, stacking the new dialog destination on top of the previous one.
This means that when the asynchronous dismiss actually happens, it correctly removes the dialog and, because it is a navigation stack, removes everything on top of it - including your second dialog. However, due to a bug in the DialogFragmentNavigator, we don't actually dismiss that second dialog, which is why it appears to work, despite everything actually already being internally out of sync (thus causing the later crash).
The correct way to pop a destination and navigate to a new destination as an atomic, immediate operation is to use popUpTo and popUpToInclusive. Therefore you can fix the sample app by removing the call to dismiss() and updating the action to pop the first dialog as part of the navigate call:
<action
android:id="#+id/action_firstSheet_to_secondSheet"
app:destination="#id/secondSheet"
app:popUpTo="#id/firstSheet"
app:popUpToInclusive="true"/>
This correctly pops the first dialog off the back stack and then navigates to the new dialog destination.
please refer this link : https://issuetracker.google.com/issues/191073055

After screen orientation change the dialogFragment appears apparently without any call

here there is part of the Activity where the screen orientation change:
protected void onCreate(Bundle savedInstanceState)
{
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_main);
et = (EditText) findViewById(R.id.editText1);
et.setOnLongClickListener(new View.OnLongClickListener()
{
#Override
public boolean onLongClick(View v)
{
Fragment1 dialogFragment = new Fragment1();
dialogFragment.show(getFragmentManager(), null);
dialogFragment.setTextDialog(et.getText().toString());
return true;
}
});
}
Apparentely it seems that the dialog that will appear inside the DialogFragment should appear just after the onLongClick over the editText
(I know that when the screen orientation change the Activity is restarted, but it shouldn't start normally like the first time that is created?)
My problem:
when I open at least once the dialog and I close it, after the screen orientation change I have the dialog displayed again on the screen, like if I long-Clicked the editText.
I don't absolutely know why this happens.
I attach also the structure of dialog fragment:
public Dialog onCreateDialog(Bundle savedInstanceState)
{
final Dialog dialog = super.onCreateDialog(savedInstanceState);
dialog.requestWindowFeature(Window.FEATURE_NO_TITLE);
LayoutInflater adbInflater = LayoutInflater.from(getActivity());
View eulaLayout = adbInflater.inflate(R.layout.dialog_crypt, null);
Button btn_OK = (Button) eulaLayout.findViewById(R.id.btnOK);
dialog.setContentView(eulaLayout);
final EditText et = (EditText)eulaLayout.findViewById(R.id.editText2);
et.setText(textDialog);
if(et.length()>0)
{
et.setText(et.getText().toString() + " ");
}
et.setSelection(et.length());
btn_OK.setOnClickListener(
new View.OnClickListener()
{
#Override
public void onClick(View v)
{
textDialog = et.getText().toString();
((Main)getActivity()).setTextOnEditText(textDialog);
dialog.dismiss();
}
});
return dialog;
}
Thanks so much for the help.
Try removing the dialog from stack using fragment manager instead of just dismissing it.
getFragmentManager().beginTransaction().remove(dialogFragment.this).commit();
By the way, instead of just using a Fragment for your dialog, you should use DialogFragment itself. Checkout: DialogFragment
Also, don't ever call your activity methods like this ( ((Main)getActivity()).setTextOnEditText(textDialog);
unless your fragment is a static inner class. Instead, create an interface to talk between fragments and activity.
When screen changes orientation, it calls onSaveInstanceState method, and it saves the state in the Bundle object including the stack. If you dismiss the dialog without clearing this stack, it will then show the dialog when you rotate the phone since this is in the saveInstanceState bundle.
You must clear dialog off the stack with:
getActivity().getSupportFragmentManager().beginTransaction().remove(this).commit();
if you use support library for dialog fragment, or
getActivity().getFragmentManager().beginTransaction().remove(this).commit();
When a config change (like rotation) occurs the old Fragment isn't destroyed - it just adds itself back to the Activity when it's recreated (android retains fragments by default). So if you have your DialogFragment shown before rotation, it will instantly show up after rotation.

Categories

Resources