I have developed application in which I want to display Fragment as a dialog,
I used Tabs and Fragment in my application, I have just one activity and I replace the fragment as I need,
If we used activity then we declare "android:theme="#android:style/Theme.Dialog" in Manifest file to display activity as dialog, same thing I want to do for Fragment
We can show a Fragment as a dialog using two means , but a single way.
Explanation:
Way:
Extend class DialogFragment and override any one of two methods:
onCreateView() OR
onCreateDialog().
Diff. between these two:
Overriding onCreateView() will let you show a Fragment as dialog and you can make the Title text customized.
On the other hand, overriding onCreateDialog(), you can again show a fragment as dialog and here you can customize the entire dialog fragment. Means, you can inflate any view to show as dialog.
If you need any source code explaining the above text, let me know.
Note:
Using DialogFragment has a disadvantage. It doesn't handle screen orientation. And crashes the app.
So, you need to use setRetainInstance() inside onCreate() of DialogFragment class.
This is a loading dialog that I use:
import android.app.Dialog;
import android.app.DialogFragment;
import android.app.ProgressDialog;
import android.os.Bundle;
public class LoadingDialogFragment extends DialogFragment {
private final String message;
private LoadingDialogFragment(String message) {
this.message = message;
}
public static LoadingDialogFragment newInstance(String message) {
LoadingDialogFragment fragment = new LoadingDialogFragment(message);
return fragment;
}
#Override
public Dialog onCreateDialog(Bundle savedInstanceState) {
final ProgressDialog dialog = new ProgressDialog(getActivity());
dialog.setMessage(message);
dialog.setIndeterminate(true);
dialog.setCancelable(true);
return dialog;
}
}
Can be instantiated like this:
LoadingDialogFragment.newInstance(context.getString(R.string.loading_message))
You can inflate views and setContentView from inside of this dialog if you want a custom layout.
http://developer.android.com/guide/topics/ui/dialogs.html#CustomLayout
Just use a DialogFragment. It is the intended fragment subclass for this use..
http://developer.android.com/reference/android/app/DialogFragment.html
Your fragment class should extend DialogFragment rather than fragment.
Check out the docs: http://developer.android.com/reference/android/app/DialogFragment.html
Related
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).
In ViewPager, I have displayed the fragment which has custom ListView. Now I tried to display each item details of the custom list view on another dialog fragment if I clicked on any item. But I have faced the following issue
"The method show(android.app.FragmentManager, java.lang.String) in
the type DialogFragment is not applicable for the arguments
(android.support.v4.app.FragmentManager, java.lang.String)"
I have used the getSupportFragmentManager() instead of the getFragmentManager() but it does not work.
Can you please check if you're importing this:
import android.support.v4.app.DialogFragment;
And not this:
import android.app.DialogFragment;
to call another fragment from within a fragment you should use getChildFragmentManager(); //preferred or getActivity().getSupportFragmentManager()
This trick may work for you make Singleton object of your Activity class like this :
public class YourActivityclass extends AppCompatActivity{
static YourActivityclass yourActivity;
#Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity);
yourActivity=this;
}
public static YourActivityclass getInstance(){
return yourActivity;
}
}
then to open dialogfragment on a fragment call like this :
date.show(YourActivityclass.getInstance().getSupportFragmentManager(), "name");
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();.
I am having problems referencing an already instantiated Fragment from within my Activity.
I have two tabs as part of the Action Bar and for each Tab I am instantiating an instance of the same fragment....
Fragment = Player
Tab1 = Player1 (Player Fragment with details from DB of Player 1)
Tab2 = Player2 (Player Fragment with details from DB of Player 2)
The Player fragment includes a field (Games played) which is amendable using a Number Picker.
So... I have created an interface which I have implemented on my activity. When the button is clicked on the Fragment it calls the interface which on the activity creates a Dialog Fragment to display the number picker. Once the number picker has been closed and passed back the number of Games Played to the Activity I then want to update this value on the correct Fragment (So if the button was pressed on Tab 2 then the value on the Player2 should get updated.)
This is where I am drawing a blank.
I am using actionBar.addTab to add each tab and using a TabsListener Class implementing ActionBar.TabListener to do a replace of the correct Fragment when each tab is pressed.
ActionBar handles the FragmentManager stuff for you...
What I think I need to do here within my Activity is get the Current Fragment so that I can make a call to a method in this fragment to update it. But when Adding the Fragment through the TabListener I cannot see a way that I can either get and store the ID of the Fragment instance or set a Tag for it. If I could then I could use getFragmentByID or getFragmentByTag to find it.
Any ideas on how I should do this.
I thought I had a completely different solution whereby I made by database update in the Number Picker itself and then simply let onResume() update the value in my Visible Fragment when the DialogFragment closes but it seems that onResume() is not called when the DialogFragment is closed.
I have not posted code examples as I hope the above simplifies the question.
Both the 'add' and 'replace' methods of FragmentTransaction have a number of overloads. Using the one with the parameters:
(int containerViewId, Fragment fragment, String tag)
...allows you to provide a tag name for the fragment that you can subsequently use to retrieve it with a call to findFragmentByTag.
You can specify the TabListener (http://developer.android.com/reference/android/app/ActionBar.TabListener.html) and get notified when tab is reselected and here you can update the UI.
In my opinion you should update the database after user selects the value from the dialog. You can specify OnClickListener for different buttons on dialog and get notified when something is selected or cancelled.
Example at http://developer.android.com/reference/android/app/DialogFragment.html#AlertDialog,
public static class MyAlertDialogFragment extends DialogFragment {
public static MyAlertDialogFragment newInstance(int title) {
MyAlertDialogFragment frag = new MyAlertDialogFragment();
Bundle args = new Bundle();
args.putInt("title", title);
frag.setArguments(args);
return frag;
}
#Override
public Dialog onCreateDialog(Bundle savedInstanceState) {
int title = getArguments().getInt("title");
return new AlertDialog.Builder(getActivity())
.setIcon(R.drawable.alert_dialog_icon)
.setTitle(title)
.setPositiveButton(R.string.alert_dialog_ok,
new DialogInterface.OnClickListener() {
public void onClick(DialogInterface dialog, int whichButton) {
((FragmentAlertDialog)getActivity()).doPositiveClick();
}
}
)
.setNegativeButton(R.string.alert_dialog_cancel,
new DialogInterface.OnClickListener() {
public void onClick(DialogInterface dialog, int whichButton) {
((FragmentAlertDialog)getActivity()).doNegativeClick();
}
}
)
.create();
}
}
I'm trying to implement a simple Dialog into my code. But it does not work. I have searched every available tutorial, including the official developer guide but nothing works. The error I got from logcat is that I'm getting a nullPointerException, I'm guessing that's on the getActivity. Any help?
This is what I have: This is my Custom Dialog class.
public class SaveDialog extends DialogFragment {
public Dialog onCreateDialog(Bundle savedInstanceState) {
AlertDialog.Builder builder = new AlertDialog.Builder(getActivity());
builder.setTitle("Save Password");
builder.setView(getContentView());
Dialog dialog = builder.create();
dialog.show();
return dialog;
}
private View getContentView() {
LayoutInflater inflater = getActivity().getLayoutInflater();
return inflater.inflate(R.layout.dialog, null);
}
}
and this is my main activity where the onclick occurs
private void savePassword() {
SaveDialog savePasswordDialog = new SaveDialog();
savePasswordDialog.show(savePasswordDialog.getSupportFragmentManager(), "tag");
}
Every single time I fire up the onClick, the app crashes. On top of that, currently I am trying to use getSupportFragmentManager, but it says it's undefined.
You should use getSupportFragmentManager(), which is only available in FragmentActivity.
You should change your activity to a fragment one.
Check this answer
Try this..works!!
((AppCompatActivity)activity).getSupportFragmentManager()
Just call getFragmentManager() from your android.support.v4.app.DialogFragment or android.support.v4.app.Fragment. It will return a android.support.v4.app.FragmentManager (this is, the support FragmentManager)
You don't have to manually show the dialog in onCreateDialog(), just returning it is sufficient for DialogFragment to work its magic (and show the dialog) when you call savePassword().
So remove this line from onCreateDialog :
dialog.show();
and it should work. Good luck!