Проблема надувания пользовательского представления для AlertDialog в DialogFragment - PullRequest
46 голосов
/ 22 сентября 2011

Я пытаюсь создать 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)

Хотя, если я попытаюсь использовать DialogFragment getLayoutInflator(Bundle), вот так:

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

Я получаю StackOverflowError.

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

Ответы [ 9 ]

87 голосов
/ 18 января 2012

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

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

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

@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();
}
22 голосов
/ 31 января 2014

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

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.

11 голосов
/ 08 февраля 2014

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

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();
    }
}
2 голосов
/ 25 ноября 2014

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

@Override
public View onCreateView(LayoutInflater inflater, ViewGroup container,
                         Bundle savedInstanceState) {
    return this.getView();
}
1 голос
/ 09 декабря 2015

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

.setView(R.layout.dialog)

0 голосов
/ 17 февраля 2017

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

layoutfile connectivity_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");
}
0 голосов
/ 13 декабря 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;
    }

Затем вы просто вызываете:1006 *

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

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

0 голосов
/ 04 ноября 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();
}
0 голосов
/ 22 сентября 2011

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

 create().

Замените на

show().
...