I had a working DialogFragment that was using an inner class to do a bunch of things on some objects, set menu icons etc. When i went to Android Studio i realised that was incorrect and i've been trying to change the inner class to be static.
In so doing, I am now trying to use onCreateDialog to, as per Google docs, "doPositiveClick" and "doNegativeClick", so that the calling MainActivity can do the work on those objects instead of the fragment doing it.
What is now confusing me however, is how do I set the layout in the fragment - I can enter a title, message and buttons as such:
return new AlertDialog.Builder(getActivity())
.setTitle(R.string.alert_title)
.setMessage(R.string.alert_message)
.setPositiveButton(R.string.set,
new DialogInterface.OnClickListener() {
public void onClick(DialogInterface dialog, int whichButton) {
((MainActivity)getActivity()).doPositiveClick();
}
}
)
.setNegativeButton(R.string.cancel,
new DialogInterface.OnClickListener() {
public void onClick(DialogInterface dialog, int whichButton) {
((MainActivity)getActivity()).doNegativeClick();
}
}
)
But previously I was doing the layout like:
final EditText input = new EditText(MainActivity.this);
LinearLayout.LayoutParams lp = new LinearLayout.LayoutParams(20, 20);
input.setText("5");
input.setLayoutParams(lp);
input.setRawInputType(Configuration.KEYBOARD_QWERTY);
Problem is, where does this go in onCreateDialog ? The Google docs shows how to set text on a dialog textView, but that is within onCreateView().
My confusion is that the google doc doesnt do both, ie, it doesnt show how to both, set up custom elements, AND set up the positive/negative click in the calling MainActivity - or if it does, i'm sorry I cant see it right now.
So can anyone make it clearer for me, using the above onCreateDialog, how can I have an editText field, with a default value that takes user input, and then get back that input to the doPositiveClick() to process.
DialogFragment can use in 2 ways: dialog or view.
case1: use DialogFragment as a dialog. you have to implement onCreateDialog() to return a dialog. and then have to show the dialog in the following way. see the example:
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();
}
}
Create and have to show dialog as the following way. show this way don't care, whether onCreateView() is implemented or not.
// Create the fragment and show it as a dialog.
DialogFragment newFragment = MyDialogFragment.newInstance();
newFragment.show(getFragmentManager(), "dialog");
case2: use as view (it is not feature of dialog). it is only view. you have to implement onCreateView() and show dialog as the following way:
public static class MyDialogFragment extends DialogFragment {
static MyDialogFragment newInstance() {
return new MyDialogFragment();
}
#Override
public View onCreateView(LayoutInflater inflater, ViewGroup container,
Bundle savedInstanceState) {
View v = inflater.inflate(R.layout.hello_world, container, false);
View tv = v.findViewById(R.id.text);
((TextView)tv).setText("This is an instance of MyDialogFragment");
return v;
}
}
and have to show view as follow. the same as use Fragment class. show this way don't care, whether onCreateDialog() is implemented or not.
FragmentTransaction ft = getFragmentManager().beginTransaction();
DialogFragment newFragment = MyDialogFragment.newInstance();
ft.add(R.id.embedded, newFragment);
ft.commit();
summary:
in design, you can implement onCreateView() and onCreateDialog() together and use the same source code with this DialogFragment lifecycle. If the screen is small, use DialogFragment as Dialog. If the screen is big, use DialogFragment as view (the same common Fragment class).
Notice that use the correct way to show DialogFragment to suitable with onCreateView() and onCreateDialog() to prevent exception.
sorry guys I thought I exhausted my searches but just after I posted this I was able to fix it, putting the text field/layout inside the onCreateDialog BEFORE the Builder and then doing setView() to that input as such:
**LinearLayout.LayoutParams lp = new LinearLayout.LayoutParams(20, 20);
final EditText input = new EditText(getActivity());
input.setText("5");
input.setLayoutParams(lp);
input.setRawInputType(Configuration.KEYBOARD_QWERTY);**
// Use the Builder class for convenient dialog construction
return new AlertDialog.Builder(getActivity())
.setTitle(R.string.alert_title)
.setMessage(R.string.alert_message)
**.setView(input)**
.setPositiveButton(R.string.set,
new DialogInterface.OnClickListener() {
public void onClick(DialogInterface dialog, int whichButton) {
((MainActivity)getActivity()).doPositiveClick();
}
}
)
Only question now is how do I get the value back after the user inputs ?
Related
I have a custom dialog which extends AppCompatDialogFragment. I am calling it from within one Fragment of a BottomNavigationBar. It gets dismissed when a message is returned from a ListenerService. It all works well until I switch to a different fragment using one of the icons on the BottomNavigationBar and then back to the fragment with Dialog functionality. The next time I show the dialog the message does not dismiss it.
I have a member variable:
SendingMessageDialog sendingMessageDialog;
When I click a button it calls this to show the dialog:
sendingMessageDialog = new SendingMessageDialog();
sendingMessageDialog.show(getActivity().getSupportFragmentManager(), "Sending Message");
When the message is receive I call this:
sendingMessageDialog.dismiss();
sendingMessageDialog = null;
I have also tried it this way:
private void dismissDialog() {
Fragment prev = getActivity().getSupportFragmentManager().findFragmentByTag("Sending Message");
if (prev != null) {
Log.d(TAG, "dismissDialog: prev is not null");
SendingMessageDialog df = (SendingMessageDialog) prev;
df.dismiss();
}
}
My SendingMessageDialog which extends AppCompatDialogFragment, I have this code in my onCreateDialog:
#NonNull
#Override
public Dialog onCreateDialog(#Nullable Bundle savedInstanceState) {
Log.d(TAG, "onCreateDialog: starting");
AlertDialog.Builder builder = new AlertDialog.Builder(getActivity());
LayoutInflater inflater = getActivity().getLayoutInflater();
View view = inflater.inflate(R.layout.send_message_dialog, null);
builder.setView(view)
.setTitle("Sending Token")
.setNegativeButton("cancel", new DialogInterface.OnClickListener() {
#Override
public void onClick(DialogInterface dialogInterface, int i) {
}
});
return builder.create();
}
I have looked up a few post on stackoverflow but none seem to address this specific issue. I saw this one, but there is no cancel() method available. I saw this one but I have to dismiss the dialog when I get a message from another part of the system, so I cannot just do this in-line sort of thing. This one just seemed like a simple mistake of calling show on the wrong dialog. I couldn't quite get a handle on this one, but it's 8 years old and seems to be about the coordination of two dialogs.
Many of the examples identify the dismiss is not working. In my case it is, until I switch fragments using the bottom navigation and then return to the fragment where the dialog process exists.
Nothing special happening in the navigation:
selectedFragment = new RunScenarioListFragment();
and:
getActivity().getSupportFragmentManager()
.beginTransaction()
.replace(R.id.frame_layout_run_scenario,selectedFragment)
.addToBackStack(null)
.commit();
I have a MainActivity containing 5 fragments, 2 of which have a help icon on the toolbar on top right. I have hidden this icon on other 3 fragments. Upon clicking help icon, an alert dialog shows up with title, message and a positive button.
This is my Alert Dialog code:
public class HelpDialogFragment extends DialogFragment {
#Override
public Dialog onCreateDialog(Bundle savedInstanceState) {
AlertDialog.Builder builder = new AlertDialog.Builder(getActivity());
builder.setTitle("Help");
builder.setMessage("Placeholder");
builder.setPositiveButton("Got It", new DialogInterface.OnClickListener() {
public void onClick(DialogInterface dialog, int id) {}
});
return builder.create();
}
}
and this is how I am showing it from MainActivity:
#Override
public boolean onOptionsItemSelected(MenuItem item) {
switch (item.getItemId()) {
case R.id.action_help:
DialogFragment helpDialog = new HelpDialogFragment();
helpDialog.show(getSupportFragmentManager(), "dialogHelp");
return true;
}
return super.onOptionsItemSelected(item);
}
The above code works but I would like to show different message based on the fragment selected so how to change the message? I tried this to change title
helpDialog.getDialog().setTitle("Some Text");
Please note I want to change Dialog message, i.e main content, I only got setTitle() method on getDialog() and not setMessage(), the above setTitle is just for example purpose but even it is throwing NullPointerException.
As you can see in the above screenshot, "Placeholder" text is the default text I added at the time of creating AlertDialog but now how to change it?
From reading your post and comments it looks like you need to set different titles depending on whatever fragment is visible. And the creation of dialogs happens from Activity so you are not sure what title to set.
The problem is essentially identifying the visible fragment and set message according to it.
You can pass the message with arguments like this.
Fragment fragment = new Fragment();
Bundle bundle = new Bundle();
bundle.putString(message, "My title");
fragment.setArguments(bundle);
Then in your Fragment, get the data (e.g. in onCreate() method)
Bundle bundle = this.getArguments();
if (bundle != null) {
String message = bundle.getString(message, defaultValue);
}
How to identify the currently visible fragment? You can do this as suggested in these answers. Once you get the current fragment, just send the message in the arguments above according to it.
By combining the above 2 ideas you can do this.
Another way would be to start the dialog from the fragment and not from the Activity but that would involve more changes so the above approach is better.
First pass the required message over a bundle while calling HelpDialogFragment class
HelpDialogFragment helpDialog = new HelpDialogFragment();
Bundle bundle = new Bundle();
bundle.putString("placeholder", "Custom placeholder");
helpDialog.setArguments(bundle);
helpDialog.show(getSupportFragmentManager(), "dialogHelp");
Now modify your HelpDialogFragment class create the dialog like this
public class HelpDialogFragment extends DialogFragment {
#Override
public Dialog onCreateDialog(Bundle savedInstanceState) {
AlertDialog.Builder builder = new AlertDialog.Builder(getActivity());
builder.setTitle("Help");
if (getArguments() != null)
builder.setMessage(getArguments().getString("placeholder",""));
builder.setPositiveButton("Got It", new DialogInterface.OnClickListener() {
public void onClick(DialogInterface dialog, int id) {}
});
return builder.create();
}
}
I have an dialogfragment and its view consists of a simple seekbar and positive and negative buttons. When I dismiss dialogfragment onclick, a new dialogfragment is created and showed. After second click on button, dialog is dismissed, but my desire is dismissing after first click. Also I should note that my activity hosts two fragments and I call dialog.show() from first fragment.
MyListFragment.java
FragmentManager manager = getFragmentManager();
fndSrchFragment dialog = new fndSrchFragment();
dialog.setTargetFragment(MyListFragment.this, REQUEST_COLOR);
dialog.show(manager, SRCH_FND);
fndSrchFragment.java
public class fndSrchFragment extends DialogFragment {
.
.
//variable definition
#Override
public Dialog onCreateDialog(Bundle savedInstanceState) {
View view = LayoutInflater.from(getActivity()).inflate(R.layout.color_picker, null);
rSb=(SeekBar) view.findViewById(R.id.r_seek_bar);
.
.
//code for interacting with seekbar
.
.
AlertDialog.Builder builder = new AlertDialog.Builder(getActivity());
builder.setView(view);
builder.setPositiveButton(android.R.string.ok, new DialogInterface.OnClickListener() {
#Override
public void onClick(DialogInterface dialog, int which) {
int ccolor = android.graphics.Color.rgb(rComponent, gComponent, bComponent);
sendResult(Activity.RESULT_OK, ccolor);
// two click is needed for dismiss which I don't know why?
dismiss();
}
});
builder.setNegativeButton(android.R.string.cancel, new DialogInterface.OnClickListener() {
#Override
public void onClick(DialogInterface dialog, int which) {
// two click is needed for dismiss which I don't know why?
dismiss();
}
});
return builder.create();
}
Thank You in Advance.
Set up a log command inside your onMenuItemActionExpand(). I bet you'll see this method is called right after you dismissed the dialog. Most likely, this method is being called by the system once focus is returned from the dialog to the activity. You should but your call to dialog.show() inside a something like onOptionsItemSelected(MenuItem item) instead.
So I have a fragment (WifiSetupFragment) that calls a DialogFragment, and that DialogFragment needs to pass a string back to the original fragment. I know to do this you have an interface in the activity that will send data to the original fragment like so, which I am already doing:
FragmentTransaction transaction = getFragmentManager().beginTransaction();
transaction.addToBackStack(null);
transaction.replace(R.id.content_frag, WifiSetupFragment.newInstance(password));
transaction.commit();
So the first time I call WifiSetupFragment, I haven't created a DialogFragment yet because I haven't clicked on an item to open the dialog. My question is should I just call
WifiSetupFragment.newInstance(null)
and have a null check for the password string in my fragment? Because I don't have a password unless the DialogFragment is open, and it's not always open. If this made no sense, please tell me and I'll try to explain more clearly. I guess it just seems strange to me to have a parameter for a string that might only be sent to this fragment occasionally since the data isn't constantly being passed in.
You don't need to communicate between these Fragments through the Activity. What you can do instead:
Make your WifiSetupFragment.newInstance() accept no parameters.
Make WifiSetupFragment implement a new interface, let's call it OnPasswordSuppliedListener.
Once you create your DialogFragment instance, attach it to a getChildFragmentManager() instead of getFragmentManager().
Now inside of your DialogFragment subclass you can reference WifiSetupFragment by calling getParentFragment().
Cast getParentFragment() to your interface and voila!
Note: I'm assuming you're using Fragments from the support library. Otherwise please be aware that nested Fragments feature was introduced in the API 17.
Your dialog can define an interface allowing to send input password back to parent fragment / activity:
public class TestDialog extends DialogFragment {
private TextView mPasswordView;
private OnPasswordDefinedCallback mCallback;
public static TestDialog newInstance() {
TestDialog dialog = new TestDialog();
return dialog;
}
#Override
public Dialog onCreateDialog(Bundle savedInstanceState) {
// inflate layout for your dialog (it must include edit text for password)
LayoutInflater inflater = getActivity().getLayoutInflater();
View layout = inflater.inflate(R.layout.dialog_test, null);
// getting ui elements from layout
mPasswordView = (TextView) layout.findViewById(R.id.txt_password);
// building dialog
AlertDialog.Builder builder = new AlertDialog.Builder(getActivity());
builder.setView(layout);
builder.setPositiveButton(android.R.string.ok, new DialogInterface.OnClickListener() {
#Override
public void onClick(DialogInterface dialog, int which) {
try {
mCallback = (OnPasswordDefinedCallback) getTargetFragment();
} catch (ClassCastException e) {
throw new ClassCastException("must implement OnPasswordDefinedCallback");
}
if (mCallback != null) {
// send password back to parent
mCallback.doPasswordDefined(mPasswordView.getText().toString());
}
dismiss();
}
});
return builder.create();
}
public interface OnPasswordDefinedCallback {
void doPasswordDefined(String password);
}
}
Then in WifiSetupFragment you can proceed as follows for opening PasswordDialog:
TestDialog dialog = TestDialog.newInstance();
dialog.setTargetFragment(WifiSetupFragment.this, 1);
dialog.show(getChildFragmentManager(), null);
WifiSetupFragment must of course implement interface OnPasswordDefinedCallback.
This question already has answers here:
requestFeature() must be called before adding content
(9 answers)
Closed 8 years ago.
I have dialog fragment. I have intention to use this fragment in activity and dialog. And I override onCreateDialog and onCreateView method. here is coding.
#Override
public View onCreateView(LayoutInflater inflater, ViewGroup container,
Bundle savedInstanceState) {
View view = inflater.inflate(R.layout.interval_time_popup, null);
setup(view, false);
return view;
}
#Override
public Dialog onCreateDialog(Bundle savedInstanceState) {
AlertDialog.Builder builder = new AlertDialog.Builder(getActivity());
View view = getActivity().getLayoutInflater().inflate(R.layout.interval_time_popup, null);
builder.setTitle("Interval Time");
builder.setView(view);
setup(view, true);
builder.setPositiveButton("Set", new DialogInterface.OnClickListener() {
public void onClick(DialogInterface dialog, int which) {
listener.setOnIntervalTime(hourNp.getValue(), minNp.getValue());
dismiss();
}
});
builder.setNegativeButton("Cancel", new OnClickListener() {
public void onClick(DialogInterface dialog, int which) {
dismiss();
}
});
return builder.create();
}
I use this fragment in the activity class like that.
SelectTimeIntervalDialogFragment fragment = new SelectTimeIntervalDialogFragment();
fragment.setHrMin(hr, min);
Bundle args = new Bundle();
FragmentTransaction t = getSupportFragmentManager().beginTransaction();
t.replace(R.id.shift_period_interval_layout, fragment);
t.commit();
I call it from another activity like that.
if((getResources().getConfiguration().screenLayout &
Configuration.SCREENLAYOUT_SIZE_NORMAL) ==
Configuration.SCREENLAYOUT_SIZE_NORMAL){
Intent intent = new Intent(ShiftPeriodActivity.this, SelectIntervalActivity.class);
intent.putExtra("intervalHr", speriod.intervalHr);
intent.putExtra("intervalMin", speriod.intervalMin);
startActivityForResult(intent, 1);
} else {
FragmentManager fm = getSupportFragmentManager();
intervalDialog = new SelectTimeIntervalDialogFragment();
intervalDialog.setHrMin(speriod.intervalHr, speriod.intervalMin);
intervalDialog.show(fm, "interval_fragment");
}
I have two conditions. When the screen size is normal, it call activity which include fragment dialog. Otherwise, it call the popup dialog. I got the exception when it call the popup dialog. It said requestFeature() must be called before adding content. Can I use like this? I would like to know how to overcome this.
Thanks.
It seems your problem is exactly the same as mine - similar-ish code and the same exception thrown.
The solution is to do what #laalto suggested: use either onCreateView() or onCreateDialog() but not both of them at the same time. I know the exception can be seemingly caused by many different things but this is what helped in my case; hopefully it'll help someone else in the future :)
i think you are using requestWindowFeature(); at on create
use it before setContentView(R.layout.activity);