I'm trying to create a DialogFragment using a custom view in an AlertDialog. This view must be inflated from xml. In my DialogFragment class I have:
#Override
public Dialog onCreateDialog(Bundle savedInstanceState) {
return new AlertDialog.Builder(getActivity())
.setTitle("Title")
.setView(getActivity().getLayoutInflater().inflate(R.layout.dialog, null))
.setPositiveButton(android.R.string.ok, this)
.setNegativeButton(android.R.string.cancel, null)
.create();
}
I have tried other inflation methods for .setView() such as:
.setView(getActivity().getLayoutInflater().inflate(R.layout.dialog, (ViewGroup) getView(), false))
and
.setView(getActivity().getLayoutInflater().inflate(R.layout.dialog, (ViewGroup) getTargetFragment().getView(), false))
After setting the target fragment in the fragment that is showing this dialog.
All of these attempts to inflate my custom view result in the following exception:
E/AndroidRuntime(32352): android.util.AndroidRuntimeException: requestFeature() must be called before adding content
E/AndroidRuntime(32352): at com.android.internal.policy.impl.PhoneWindow.requestFeature(PhoneWindow.java:214)
E/AndroidRuntime(32352): at com.android.internal.app.AlertController.installContent(AlertController.java:248)
E/AndroidRuntime(32352): at android.app.AlertDialog.onCreate(AlertDialog.java:314)
E/AndroidRuntime(32352): at android.app.Dialog.dispatchOnCreate(Dialog.java:335)
E/AndroidRuntime(32352): at android.app.Dialog.show(Dialog.java:248)
E/AndroidRuntime(32352): at android.support.v4.app.DialogFragment.onStart(DialogFragment.java:339)
E/AndroidRuntime(32352): at android.support.v4.app.Fragment.performStart(Fragment.java:1288)
E/AndroidRuntime(32352): at android.support.v4.app.FragmentManagerImpl.moveToState(FragmentManager.java:873)
E/AndroidRuntime(32352): at android.support.v4.app.FragmentManagerImpl.moveToState(FragmentManager.java:1041)
E/AndroidRuntime(32352): at android.support.v4.app.BackStackRecord.run(BackStackRecord.java:625)
E/AndroidRuntime(32352): at android.support.v4.app.FragmentManagerImpl.execPendingActions(FragmentManager.java:1360)
E/AndroidRuntime(32352): at android.support.v4.app.FragmentManagerImpl$1.run(FragmentManager.java:411)
E/AndroidRuntime(32352): at android.os.Handler.handleCallback(Handler.java:587)
E/AndroidRuntime(32352): at android.os.Handler.dispatchMessage(Handler.java:92)
E/AndroidRuntime(32352): at android.os.Looper.loop(Looper.java:132)
E/AndroidRuntime(32352): at android.app.ActivityThread.main(ActivityThread.java:4028)
E/AndroidRuntime(32352): at java.lang.reflect.Method.invokeNative(Native Method)
E/AndroidRuntime(32352): at java.lang.reflect.Method.invoke(Method.java:491)
E/AndroidRuntime(32352): at com.android.internal.os.ZygoteInit$MethodAndArgsCaller.run(ZygoteInit.java:844)
E/AndroidRuntime(32352): at com.android.internal.os.ZygoteInit.main(ZygoteInit.java:602)
E/AndroidRuntime(32352): at dalvik.system.NativeStart.main(Native Method)
While if I try to use the DialogFragment's getLayoutInflator(Bundle) like this:
.setView(getLayoutInflater(savedInstanceState).inflate(R.layout.dialog, null))
I get a StackOverflowError.
Does anyone know how to inflate a custom view for an AlertDialog in a DialogFragment?
The first error line gives me the hint that this is related to how you are creating your dialog - not the dialog itself.
Are you creating the dialog automatically (which could mean this gets called before the views are all set up) or in response to a button click? I initially had problems with fragments due to instantiation order.
I used the same code to set the view as you have, and my result works. I cut out the other setup to make this look cleaner, but it works with or without it.
#Override
public Dialog onCreateDialog(Bundle savedInstanceState) {
AlertDialog.Builder builder = new AlertDialog.Builder(getActivity());
View view = getActivity().getLayoutInflater().inflate(R.layout.dialog_layout, null);
builder.setView(view);
return builder.create();
}
I'm surprised by these answers as none of them solve the problem.
A DialogFragment allows you to reuse the same UI for both a dialog and integrated in your app elsewhere as a fragment. Quite a useful feature. As per google's documentation, you can achieve this by overriding onCreateDialog and onCreateView.
http://developer.android.com/reference/android/app/DialogFragment.html
There are three scenarios here:
Override onCreateDialog only - Works as a dialog but cannot be
integrated elsewhere.
Override onCreateView only - Does not work as a dialog but can be
integrated elsewhere.
Override both - Works as a dialog and can be integrated
elsewhere.
Solution:
The AlertDialog class is calling another class which calls requestFeature. To fix this.. Don't use the AlertDialog, instead use a plain Dialog or whatever super.onCreateDialog returns. This the solution that I have found works best.
Caveat:
Other dialogs such as DatePickerDialog, ProgressDialog, TimePickerDialog all inherit from AlertDialog and will likely cause the same error.
Bottom Line:
DialogFragment is good if you need to create very customized interface that needs to be used in several places. It doesn't appear to work to reuse existing android dialogs.
Avoid request feature crash and use same layout:
public class MyCombinedFragment extends DialogFragment
{
private boolean isModal = false;
public static MyCombinedFragment newInstance()
{
MyCombinedFragment frag = new MyCombinedFragment();
frag.isModal = true; // WHEN FRAGMENT IS CALLED AS A DIALOG SET FLAG
return frag;
}
public MyCombinedFragment()
{
}
#Override
public View onCreateView(LayoutInflater inflater, ViewGroup container, Bundle savedInstanceState)
{
if(isModal) // AVOID REQUEST FEATURE CRASH
{
return super.onCreateView(inflater, container, savedInstanceState);
}
else
{
View view = inflater.inflate(R.layout.fragment_layout, container, false);
setupUI(view);
return view;
}
}
#Override
public Dialog onCreateDialog(Bundle savedInstanceState)
{
AlertDialog.Builder alertDialogBuilder = null;
alertDialogBuilder = new AlertDialog.Builder(getActivity());
View view = getActivity().getLayoutInflater().inflate(R.layout.fragment_layout, null);
alertDialogBuilder.setView(view);
alertDialogBuilder.setTitle(“Modal Dialog“);
alertDialogBuilder.setPositiveButton("Cancel", new DialogInterface.OnClickListener()
{
#Override
public void onClick(DialogInterface dialog, int which)
{
dialog.dismiss();
}
});
setupUI(view);
return alertDialogBuilder.create();
}
}
I had the same problem. In my case it was becasue Android Studio created a template onCreateView that re-inflated a new view instead of returning the view created in onCreateDialog. onCreateView is called after onCreateDialog, so the solution was to simply reurnt the fragments view.
#Override
public View onCreateView(LayoutInflater inflater, ViewGroup container,
Bundle savedInstanceState) {
return this.getView();
}
Faced the same issue, and it took lot of time to get rid of the error. Finally passing resource ID to setView() method solved the problem. Add set view as below:
.setView(R.layout.dialog)
In your code where you call
create().
Replace with
show().
I haven't inflated from XML but I have done dynamic view generation in a DialogFragment successfully:
#Override
public Dialog onCreateDialog(Bundle savedInstanceState) {
m_editText = new EditText(getActivity());
return new AlertDialog.Builder(getActivity())
.setView(m_editText)
.setPositiveButton(android.R.string.ok,null)
.setNegativeButton(android.R.string.cancel, null);
.create();
}
What you want to do instead is create your custom view in the onCreateView method like you normally would. If you want to do something like change the title of the dialog, you do that in onCreateView.
Here's an example to illustrate what I mean:
#Override
public View onCreateView(LayoutInflater inflater, ViewGroup container, Bundle savedInstanceState) {
getDialog().setTitle("hai");
View v = inflater.inflate(R.layout.fragment_dialog, container, false);
return v;
}
Then you just call:
DialogFragment df = new MyDialogFragment();
df.show(..);
And voila, a dialog with your own custom view.
As i needed long time for solving the same problem (Pop up a simple Text Dialog) i decided to share my solution:
The layoutfile connectivity_dialog.xml contains a simple TextView with the message text:
<?xml version="1.0" encoding="utf-8"?>
<RelativeLayout xmlns:android="http://schemas.android.com/apk/res/android"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:gravity="center"
android:layout_gravity="center">
<TextView
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_marginStart="20dp"
android:layout_marginEnd="20dp"
android:layout_marginTop="20dp"
android:layout_marginBottom="20dp"
android:text="Connectivity was lost"
android:textSize="34sp"
android:gravity="center"
/>
</RelativeLayout>
The Activity showing the "dialog" implements a inner class (as DialogFragment is a Fragment and not a Dialog; further info see https://stackoverflow.com/a/5607560/6355541). The Activity can activate and deactivate the DialogFragment via two functions. If you're using android.support.v4, you may want to change to getSupportFragmentManager():
public static class ConnDialogFragment extends DialogFragment {
public static ConnDialogFragment newInstance() {
ConnDialogFragment cdf = new ConnDialogFragment();
cdf.setRetainInstance(true);
cdf.setCancelable(false);
return cdf;
}
#Override
public View onCreateView(LayoutInflater inflater, ViewGroup container, Bundle savedInstanceState) {
return inflater.inflate(R.layout.connectivity, container, false);
}
}
private void dismissConn() {
DialogFragment df = (DialogFragment) getFragmentManager().findFragmentByTag("conn");
if (df != null) df.dismiss();
}
private void showConn() {
FragmentTransaction ft = getFragmentManager().beginTransaction();
Fragment prev = getFragmentManager().findFragmentByTag("conn");
if (prev != null) ft.remove(prev);
ft.addToBackStack(null);
ConnDialogFragment cdf = ConnDialogFragment.newInstance();
cdf.show(ft, "conn");
}
Related
i just need an Alert Dialog with title, msg and buttons, but showed as bottom sheet.
where is a way to obtain this (without custom view)?
Instead of using AlertDialog inside of bottomsheetdialogfragment.
Create one bottomsheetdialog which behaves like your requirement.
Please refer
https://medium.com/glucosio-project/moving-from-dialogs-to-bottomsheetdialogs-on-android-15fb8d140295
How to use BottomSheetDialog?
the best way is to use BottomSheetDialogFragment as you said and to set it a custom view that you wants with only title, msg and buttons
BottomSheetDialog dialog = new BottomSheetDialog(YourActivity.this);
dialog.setContentView(YourView);
dialog.show();
The AlertDialog and the BottomSheetDialog extend AppCompatDialog both but have different implementation.
Since the layout is simple (just title, message and buttons), it is easier to use a BottomSheetDialog with a custom layout, rather than to use a AlertDialog and adapt all the behaviour and animations of the BottomSheet.
Just use the BottomSheetDialogFragment (which creates a BottomSheetDialog):
public class MyBottomSheetDialog extends BottomSheetDialogFragment {
#Nullable #Override
public View onCreateView(#NonNull LayoutInflater inflater, #Nullable ViewGroup container,
#Nullable Bundle savedInstanceState) {
super.onCreateView(inflater, container, savedInstanceState);
//Use your custom layout
View view = inflater.inflate(R.layout.yourLayout, container, false);
return view;
}
}
And then
MyBottomSheetDialog myBottomSheetDialog = new MyBottomSheetDialog();
myBottomSheetDialog.show(getSupportFragmentManager(), "TAG");
I answer to my question: Simply no, you're forced to use a CustomView
I'm writing a custom dialog on android.
I did this using the onCreateView method.
public class CheckpointLongPressDialog extends DialogFragment {
public void CheckpointLongPressDialog() {}
#Nullable
#Override
public View onCreateView(LayoutInflater inflater, ViewGroup container, Bundle savedInstanceState) {
View view = inflater.inflate(R.layout.fragment_checkpoint_long_press_dialog, container);
getDialog().setTitle("TITLE");
return view;
}
How can i center the title programmatically?
Maybe its not the best way, I use a custom title TextView.
TextView title = new TextView(mainActivity);
title.setText(alertTitle);
title.setBackgroundResource(R.drawable.gradient);
title.setPadding(10, 10, 10, 10);
title.setGravity(Gravity.CENTER); // this is required to bring it to center.
title.setTextSize(22);
getDialog().setCustomTitle(title);
I solve the problem using a builder and inflating the xml layout.
private AlertDialog.Builder builder;
#Override
public Dialog onCreateDialog(Bundle savedInstanceState) {
// Use the Builder class for convenient dialog construction
builder = new AlertDialog.Builder(getActivity());
LayoutInflater inflater = getActivity().getLayoutInflater();
builder.setView(inflater.inflate(R.layout.fragment_checkpoint_long_press_dialog, null));
// Create the AlertDialog object and return it
return builder.create();
}
Try this..
final int titleId = getActivity().getResources().getIdentifier("alertTitle", "id", "android");
TextView title = (TextView) getDialog().findViewById(titleId);
if (title != null) {
title.setGravity(Gravity.CENTER);
}
What if you use the whole layout to inflate also your custom title?. Instead of getDialog().setTitle("TITLE"); you can also include a TextView in your custom layout for the title.
The title view is using default theme. You have 2 ways to do what you want, first one is better for having a more customized experience:
Use this to have a dialog without title, and then make custom title bar in the layout of this fragment.
dialog.getWindow().requestFeature(Window.FEATURE_NO_TITLE);
Extend the default theme for the dialog and update it, then set it in this dialog.
I am creating a dialog and setting setContentView of a layout. And I am programmatically adding buttons, images to layout in dialog setContentView . Now how I can assign dialog box view to another view.
That is a layout is assigned to a a view like below
View getview=R.layout.tamil_alphabet_speak_word;
Similarly how can I assign the dialog box view to another view. Since I am adding all elements to the view "TamilAlphabets" programmatically the child are null it returns for the below code.
Alphbetdialog=new Dialog(TamilAlphabets.this);
Alphbetdialog.requestWindowFeature(Window.FEATURE_NO_TITLE);
Alphbetdialog.setContentView(R.layout.tamil_alphabetsdialog);
(adding elements to the layout "TamilAlphabets" code
..............
)
LayoutInflater inflator=(LayoutInflater)TamilAlphabets.this.getSystemService
(TamilAlphabets.this.LAYOUT_INFLATER_SERVICE);
View row=inflator.inflate(tamil_alphabetsdialog, Parent,false);
LinearLayout l1=(LinearLayout)row.findViewById(R.id.alphabetlayout1);
ViewGroup vg=(ViewGroup)l1;
vg.getChildCount();
So I need to assign the dialog box view to another view how do I do that.
I need something like this
View getview=<I need dialog box view>
You should avoid using Dialog class directly and instead use Dialog subclass's or DialogFragment
https://developer.android.com/guide/topics/ui/dialogs.html:
The Dialog class is the base class for dialogs, but you should avoid instantiating Dialog directly. Instead, use one of the following subclasses:
AlertDialog
A dialog that can show a title, up to three buttons, a list of selectable items, or a custom layout.
DatePickerDialog or TimePickerDialog
A dialog with a pre-defined UI that allows the user to select a date or time.
In any case im guessing that what you want is a custom dialog and what is recomendaded is using the DialogFragment class in that case here is an example
Dialog fragment layout
<TextView xmlns:android="http://schemas.android.com/apk/res/android"
android:id="#+id/textView1"
android:layout_width="match_parent"
android:layout_height="match_parent">
Dialog class
public class DialogExampleFragment extends DialogFragment {
private static final String ARG_PARAM = "extra:PARAM";
private String mText;
#Override
public void onAttach(Activity activity) {
super.onAttach(activity);
}
#Override
public void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
Bundle arguments = getArguments();
mText = arguments.getString(ARG_PARAM);
}
#Override
public Dialog onCreateDialog(Bundle savedInstanceState) {
Dialog dialog = super.onCreateDialog(savedInstanceState);
dialog.setTitle("title");
return dialog;
}
public static DialogExampleFragment newInstance(String message) {
Bundle args = new Bundle();
args.putSerializable(ARG_PARAM, message);
DialogExampleFragment fragment = new DialogExampleFragment();
fragment.setArguments(args);
return fragment;
}
#Override
public View onCreateView(LayoutInflater inflater, ViewGroup container, Bundle savedInstanceState) {
View root = inflater.inflate(R.layout.fragment_dialog_example, container, false);
TextView t = (TextView) root.findViewById(R.id.textView1);
t.setText(mText);
return root;
}
}
To show as a dialog
DialogExampleFragment.newInstance("Message")
.show(getFragmentManager(), "dialog");
Note since a DialogFragment is a fragment it has de advantage of being able to be shown as a Dialog or as a regular fragment you can get all the information on the link i posted above
THere a tons of question about android.util.AndroidRuntimeException: requestFeature() must be called before adding content. But none of the proposed solutions worked for me.
I have a custom DialogFragment
#Override
public Dialog onCreateDialog(Bundle savedInstanceState) {
return new AlertDialog.Builder(getActivity()).create();
}
#Override
public final View onCreateView(LayoutInflater inflater, ViewGroup container, Bundle savedInstanceState) {
return inflater.inflate(R.layout.notification_dialog, null);
}
#Override
public void onViewCreated(View view, Bundle savedInstanceState) {
super.onViewCreated(view, savedInstanceState);
//setting up dialog
}
I'm showing it like this
newDialogInstance().show(activity.getFragmentManager(), "tag-dialog-fragment");
And each time I get:
android.util.AndroidRuntimeException: requestFeature() must be called before adding content
at com.android.internal.policy.impl.PhoneWindow.requestFeature(PhoneWindow.java:226)
at com.android.internal.app.AlertController.installContent(AlertController.java:234)
at android.app.AlertDialog.onCreate(AlertDialog.java:337)
at android.app.Dialog.dispatchOnCreate(Dialog.java:355)
at android.app.Dialog.show(Dialog.java:260)
at android.app.DialogFragment.onStart(DialogFragment.java:490)
at android.app.Fragment.performStart(Fragment.java:1719)
Could someone explain me what is going on here?
This is a late answer but maybe that'll help someone, the problem comes from the fact that you are trying to inflate the dialog from both onCreateDialog and onCreateView. To avoid this you can avoid using onCreateView and inflate your layout in onCreateDialog instead.
You would get this:
#Override
public Dialog onCreateDialog(Bundle savedInstanceState) {
// Get the layout inflater
LayoutInflater inflater = getActivity().getLayoutInflater();
View layout = inflater.inflate(R.layout.notification_dialog, null);
/** Add modifications on your layout if needed */
AlertDialog.Builder builder = new AlertDialog.Builder(getActivity());
// Add your custom layout to the builder
builder.setView(layout);
return builder.create();
}
Then just remove onCreateView or use it to do other things like using savedInstanceState as explained in this other answer: https://stackoverflow.com/a/15602648/2206688
You can also review the documentation example:
http://developer.android.com/guide/topics/ui/dialogs.html#CustomLayout
Another late answer and I wrote part of it already as a comment but maybe it's useful for someone else as well.
As Yoann already wrote, the problem is that the View is created twice and, in the case of the dialog, it creates its own window which causes the problem. The official documentation (http://developer.android.com/reference/android/app/DialogFragment.html#AlertDialog) makes it seem that it is possible to overwrite onCreateView and onCreateDialog at the same time and show a custom layout that can be embedded or used AlertDialog-styled:
Instead of (or in addition to) implementing
onCreateView(LayoutInflater, ViewGroup, Bundle) to generate the view
hierarchy inside of a dialog, you may implement onCreateDialog(Bundle)
to create your own custom Dialog object.
This is most useful for creating an AlertDialog, [...]
It is possible to overwrite both methods, just not in combination with the AlertDialog.Builder.
What is working for me so far is this:
public class CustomDialogFragment extends DialogFragment {
#NonNull
#Override
public Dialog onCreateDialog(Bundle savedInstanceState) {
if (isInLayout())
return super.onCreateDialog(savedInstanceState);
AlertDialog.Builder builder = new AlertDialog.Builder(getActivity())
.setView(R.layout.my_custom_layout)
.setTitle(R.string.my_title)
.setPositiveButton(R.string.ok, new DialogInterface.OnClickListener() {
#Override
public void onClick(DialogInterface dialog, int which) {
// TODO implement
}
});
return builder.create();
}
#Nullable
#Override
public View onCreateView(LayoutInflater inflater, ViewGroup container, Bundle savedInstanceState) {
if (!isInLayout())
return super.onCreateView(inflater, container, savedInstanceState);
View v = inflater.inflate(R.layout.my_custom_layout, container);
return v;
}
}
I'm using isInLayout() to decide whether to call the super method or use the custom implementation.
EDIT: This example uses the support library classes: http://developer.android.com/reference/android/support/v7/app/AlertDialog.Builder.html#setView(int)
Use like this :
#Override
public Dialog onCreateDialog(Bundle savedInstanceState) {
AlertDialog.Builder builder = new AlertDialog.Builder(getActivity());
View view = getActivity().getLayoutInflater().inflate(R.layout.notification_dialog, null);
builder.setView(view);
return builder;
}
I'm trying to create a DialogFragment that shows a dialog with a custom ListView inside.
public class MultiSelectDialogCustom extends DialogFragment {
ListView mLocationList;
private ArrayList<String> mOfficeListItems = new ArrayList<String>();
public static MultiSelectDialogCustom newInstance(int title) {
MultiSelectDialogCustom dialog = new MultiSelectDialogCustom();
Bundle args = new Bundle();
args.putInt("title", title);
dialog.setArguments(args);
return dialog;
}
#Override
public View onCreateView(LayoutInflater inflater, ViewGroup container,
Bundle savedInstanceState) {
Collections.addAll(mOfficeListItems, getResources().getStringArray(R.array.offices));
View v = inflater.inflate(R.layout.fragment_choice_list, container,
true);
mLocationList = (ListView)v.findViewById(R.id.location_criteria_list);
final FunctionListArrayAdapter adapter = new FunctionListArrayAdapter(
this, android.R.layout.simple_list_item_1, mOfficeListItems);
mLocationList.setAdapter(adapter);
getDialog().setTitle(getArguments().getInt("title"));
return v;
}
}
When calling it from a fragment :
MultiSelectDialogCustom dialogFrag = MultiSelectDialogCustom_.newInstance(R.string.dialog_title);
dialogFrag.show(getActivity().getSupportFragmentManager(), null);
It only shows a blank dialog with the title... why my isn't my list displayed?
Instead of using onCreateView you should be overriding onCreateDialog and inside of it, it'll look something like:
#Override
public Dialog onCreateDialog(Bundle savedInstanceState) {
Collections.addAll(mOfficeListItems, getResources().getStringArray(R.array.offices));
View v = getActivity().getLayoutInflater().inflate(R.layout.fragment_choice_list, null);
mLocationList = (ListView)v.findViewById(R.id.location_criteria_list);
final FunctionListArrayAdapter adapter = new FunctionListArrayAdapter(
this, android.R.layout.simple_list_item_1, mOfficeListItems);
mLocationList.setAdapter(adapter);
AlertDialog.Builder builder = new AlertDialog.Builder(getActivity());
builder.setTitle(getArguments().getInt("title")).setView(v);
return builder.create();
}
This quote from the DialogFragment documentation page describes what you're trying to do:
Implementations should override this class and implement onCreateView(LayoutInflater, ViewGroup, Bundle) to supply the content of the dialog. Alternatively, they can override onCreateDialog(Bundle) to create an entirely custom dialog, such as an AlertDialog, with its own content.
In your case, it seems like onCreateDialog is the way to go since you want to do a custom inner view.
May be you are missing something very small but important. Are you missing notifyDataSetChanged() in your adapter?
"Once you have added the new item to the adapter you have to call notifyDataSetChanged() so that the listview refreshes itself with the new set of data found in the adapter."
what I forgot was:
view.setAdapter(adapter);
after I added that the code worked