Проблема с раздуванием пользовательского представления для AlertDialog в DialogFragment

Я пытаюсь создать DialogFragment, используя настраиваемое представление в AlertDialog. Это представление должно быть увеличено из xml. В моем DialogFragment классе у меня есть:

@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();
}

Я пробовал другие методы инфляции для .setView(), такие как:

.setView(getActivity().getLayoutInflater().inflate(R.layout.dialog, (ViewGroup) getView(), false))

и

.setView(getActivity().getLayoutInflater().inflate(R.layout.dialog, (ViewGroup) getTargetFragment().getView(), false))

После установки целевого фрагмента во фрагменте, который показывает это диалоговое окно.

Все эти попытки раздуть мое пользовательское представление приводят к следующему исключению:

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)

Хотя, если я попытаюсь использовать getLayoutInflator(Bundle) DialogFragment следующим образом:

.setView(getLayoutInflater(savedInstanceState).inflate(R.layout.dialog, null))

Я получаю StackOverflowError.

Кто-нибудь знает, как раздуть пользовательский вид для AlertDialog в DialogFragment?


person ashughes    schedule 22.09.2011    source источник


Ответы (9)


Первая строка ошибки дает мне подсказку, что это связано с тем, как вы создаете свой диалог, а не с самим диалогом.

Вы создаете диалоговое окно автоматически (что может означать, что оно вызывается до того, как все представления будут настроены) или в ответ на нажатие кнопки? Сначала у меня были проблемы с фрагментами из-за порядка создания экземпляров.

Я использовал тот же код для установки представления, что и у вас, и мой результат работает. Я вырезал другие настройки, чтобы это выглядело чище, но это работает с ним или без него.

@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();
}
person ProjectJourneyman    schedule 17.01.2012
comment
это сработало для меня, но я хотел бы понять, почему. Почему getLayoutInflater из dialogFragment возвращает другой layoutinflater, чем у Activity? - person stork; 23.03.2012
comment
Каждый LayoutInflater привязан к контексту (который различается между Activity и фрагментом), а фрагменты все усложняют. Кроме того, есть несколько вещей, которые не работают рекурсивно (например, диалог внутри диалога). Я предполагаю, что проблема лежит где-то между порядком создания экземпляров и рекурсией, но может быть проще просто свернуть с этим, чем копать дальше... - person ProjectJourneyman; 27.03.2012
comment
Это привело меня к реальной проблеме, которая заключалась в том, что вы должны вызывать setView() прежде всего (например, setTitle(), что я делал неправильно). Спасибо. - person ashughes; 27.03.2012
comment
Все в порядке, но теперь кнопки «Положительно» и «Отрицательно» не отображаются. - person Shajeel Afzal; 26.01.2015

Я удивлен этими ответами, поскольку ни один из них не решает проблему.

DialogFragment позволяет повторно использовать один и тот же пользовательский интерфейс как для диалога, так и для интеграции в ваше приложение в другом месте в виде фрагмента. Довольно полезная функция. Согласно документации Google, вы можете добиться этого, переопределив onCreateDialog и onCreateView. http://developer.android.com/reference/android/app/DialogFragment.html

Здесь есть три сценария:

  1. Переопределить только onCreateDialog — работает как диалоговое окно, но не может быть интегрировано в другое место.
  2. Переопределить только onCreateView — не работает как диалоговое окно, но может быть интегрировано в другое место.
  3. Переопределить оба — работает как диалоговое окно и может быть интегрировано в другое место.

Решение: класс AlertDialog вызывает другой класс, который вызывает requestFeature. Чтобы исправить это.. Не используйте AlertDialog, вместо этого используйте обычный диалог или что-то, что возвращает super.onCreateDialog. Это решение, которое я нашел, работает лучше всего.

Предупреждение: все другие диалоговые окна, такие как DatePickerDialog, ProgressDialog, TimePickerDialog, наследуются от AlertDialog и, скорее всего, вызовут ту же ошибку.

Итог: DialogFragment хорош, если вам нужно создать очень индивидуальный интерфейс, который нужно использовать в нескольких местах. Похоже, повторное использование существующих диалогов Android не работает.

person vangorra    schedule 30.01.2014
comment
Из экспериментов такая проблема бывает только на определенных версиях Android. Видел, как он не работает 21 и 22, а работает 23, 24. - person Jeff Muir; 17.11.2016
comment
Использование простого диалога вместо AlertDialog было для меня ответом. Спасибо. - person Manuel Lopera; 26.04.2018
comment
В документации для DialogFragment говорится, что onCreateDialog .. .это наиболее полезно для создания AlertDialog, позволяющего отображать стандартные оповещения для пользователя, которые управляются фрагментом. - person cambunctious; 30.09.2018

Избегайте сбоя функции запроса и используйте тот же макет:

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();
    }
}
person SolarDriftSolutions    schedule 08.02.2014
comment
К вашему сведению, вы можете использовать getShowsDialog(), чтобы узнать, отображается ли ваш фрагмент как модальный. Но посмотрите на ответ @vangorra. - person Neige; 10.04.2014
comment
Я знаю, что это старо, но мне пришлось прочитать МНОГИЕ ответы на этот и подобные вопросы, и это ЕДИНСТВЕННЫЙ, в котором упоминалось, что вам нужно вернуть super.onCreateView для модальных диалогов. Спасибо. - person Aaron Goselin; 04.07.2018

У меня такая же проблема. В моем случае это было связано с тем, что Android Studio создала шаблон onCreateView, который повторно раздувал новое представление вместо того, чтобы возвращать представление, созданное в onCreateDialog. onCreateView вызывается после onCreateDialog, поэтому решением было просто перезапустить представление фрагментов.

@Override
public View onCreateView(LayoutInflater inflater, ViewGroup container,
                         Bundle savedInstanceState) {
    return this.getView();
}
person seiterm    schedule 25.11.2014

Столкнулся с той же проблемой, и потребовалось много времени, чтобы избавиться от ошибки. Наконец, передача идентификатора ресурса методу setView() решила проблему. Добавьте установленный вид, как показано ниже:

.setView(R.layout.dialog)

person Vinayak Bhat    schedule 09.12.2015
comment
Если вы используете .setView(R.layout.dialog), но не View view = getActivity().getLayoutInflater().inflate(R.layout.dialog, null); builder.setView(view);, вы увидите макет, но не сможете получить доступ к элементам управления (представлениям). Таким образом, вы не будете добавлять addTextChangedListener, setOnClickListener и т. д. - person CoolMind; 03.04.2019

В вашем коде, где вы звоните

 create().

Заменить

show().
person coder_For_Life22    schedule 22.09.2011
comment
onCreateDialog() должен возвращать Dialog, поэтому я использую .create(). DialogFragment показывается фрагментом, который создает и добавляет этот DialogFragment к своему FragmentManager через DialogFragment.show(FragmentTransaction, String) - person ashughes; 22.09.2011

Я не раздувал XML, но успешно выполнил генерацию динамического представления в DialogFragment:

@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();
}
person Shinyosan    schedule 03.11.2011

Вместо этого вы хотите создать собственное представление в методе onCreateView, как обычно. Если вы хотите сделать что-то вроде изменения заголовка диалогового окна, вы делаете это в onCreateView.

Вот пример, чтобы проиллюстрировать, что я имею в виду:

        @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;
    }

Затем вы просто звоните:

DialogFragment df = new MyDialogFragment();
df.show(..);

И вуаля, диалог с вашим собственным представлением.

person stork    schedule 13.12.2011
comment
К сожалению, это не позволяет вам устанавливать стандартные диалоговые кнопки ОС. - person Jon Willis; 06.02.2013
comment
точно. поскольку у вас нет панели действий в диалоговом окне, я полностью рассчитывал на кнопки AlertDialog - person njzk2; 30.01.2014

Поскольку мне потребовалось много времени для решения одной и той же проблемы (всплывающее простое текстовое диалоговое окно), я решил поделиться своим решением:

Файл layoutconnection_dialog.xml содержит простой TextView с текстом сообщения:

<?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>

Действие, показывающее «диалог», реализует внутренний класс (поскольку DialogFragment является фрагментом, а не диалоговым окном; дополнительную информацию см. https://stackoverflow.com/a/5607560/6355541). Activity может активировать и деактивировать DialogFragment с помощью двух функций. Если вы используете android.support.v4, вы можете перейти на 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");
}
person vasquez    schedule 17.02.2017