Проблема с диалогом: перед добавлением содержимого необходимо вызвать requestFeature()

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

final EditText newKey = (EditText) findViewById(R.id.dialog_result);
AlertDialog.Builder keyBuilder = new AlertDialog.Builder(StegDroid.this);
keyBuilder
.setCancelable(false)
.setPositiveButton("Try Again", new DialogInterface.OnClickListener() {
    public void onClick(DialogInterface dialog, int id) {
        Log.v("Dialog","New Key: "+newKey.getText().toString());
    }
})
.setNegativeButton("Cancel", new DialogInterface.OnClickListener() {
       public void onClick(DialogInterface dialog, int id) {
            dialog.cancel();
       }
   });
AlertDialog dialog = keyBuilder.create();
dialog.setTitle("Decryption Failed");
dialog.setContentView(R.layout.decrypt_failed_dialog);
dialog.show();

Однако я всегда получаю это исключение:

01-11 18:49:00.507: ERROR/AndroidRuntime(3461): android.util.AndroidRuntimeException: requestFeature() must be called before adding content
01-11 18:49:00.507: ERROR/AndroidRuntime(3461):     at com.android.internal.policy.impl.PhoneWindow.requestFeature(PhoneWindow.java:181)
01-11 18:49:00.507: ERROR/AndroidRuntime(3461):     at com.android.internal.app.AlertController.installContent(AlertController.java:199)
01-11 18:49:00.507: ERROR/AndroidRuntime(3461):     at android.app.AlertDialog.onCreate(AlertDialog.java:251)

...

в строке dialog.show(). Что я должен делать, чтобы избавиться от этого?

Ответ 1

Перед созданием диалогового окна вам нужно установить пользовательский вид. Также вам нужно использовать setView(View) вместо setContentView(), если вы используете кнопки положительных и отрицательных по умолчанию, предоставленные для вас AlertDialog.

final EditText newKey = (EditText) findViewById(R.id.dialog_result);
AlertDialog.Builder keyBuilder = new AlertDialog.Builder(StegDroid.this);
keyBuilder
.setCancelable(false)
.setPositiveButton("Try Again", new DialogInterface.OnClickListener() {
    public void onClick(DialogInterface dialog, int id) {
        Log.v("Dialog","New Key: "+newKey.getText().toString());
    }
})
.setNegativeButton("Cancel", new DialogInterface.OnClickListener() {
       public void onClick(DialogInterface dialog, int id) {
            dialog.cancel();
       }
   });
keyBuilder.setTitle("Decryption Failed");
keyBuilder.setView(getLayoutInflater().inflate(R.layout.decrypt_failed_dialog, null));
AlertDialog dialog = keyBuilder.create();
dialog.show();

Ответ 2

Также убедитесь, что вы не возвращаете уже показанный диалог. Например, ProgressDialog имеет удобный статический метод show(), который принимает некоторые параметры и возвращает ProgressDialog. Оказывается, вы не можете использовать этот метод и вернуть результирующий ProgressDialog, потому что тогда, когда ОС пытается показать его (и он уже показан), он выкинет это же исключение.

Остерегайтесь также: вышеописанное поведение наблюдается в эмуляторе Android, но на самом деле оно не выдавало исключение и отлично работало на моем Droid Incredible running 2.2.

Ответ 3

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

public class MyDialogFragment extends DialogFragment {
    private View mLayout;
    private ViewGroup mContainer;

    @Nullable
    @Override
    public View onCreateView(LayoutInflater inflater, ViewGroup container, Bundle savedInstanceState) {
        mContainer = container;
        if (getShowsDialog()) {
            // one could return null here, or be nice and call super()
            return super.onCreateView(inflater, container, savedInstanceState);
        }
        return getLayout(inflater, container);
    }

    private View getLayout(LayoutInflater inflater, ViewGroup container) {
        mLayout = inflater.inflate(R.layout.my_layout, container, false);
        return mLayout;
    }

    @Override
    public Dialog onCreateDialog(Bundle savedInstanceState) {
        return new AlertDialog.Builder(getContext())
                .setPositiveButton(R.string.ok, null)
                .setNegativeButton(R.string.cancel, null)
                .setNeutralButton(R.string.filter_clear_selection, null)
                .setView(getLayout(LayoutInflater.from(getContext()), mContainer))
                .create()
            ;
    }
}

Это позволяет мне добавить мой фрагмент как любой нормальный фрагмент (в макете), но также отображать его как диалог, в котором он работает автономно.

Ответ 4

Для тех, у кого эта проблема возникает при использовании DialogFragment.

В моем случае это произошло потому, что я неправильно расширил класс DialogFragment. Я возвращал представление onCreateView() и также возвращал пользовательский диалог в onCreateDialog(). В документации указано:

У вас может быть дизайн пользовательского интерфейса, в котором вы хотите, чтобы часть пользовательского интерфейса в некоторых ситуациях отображаются как диалог, но как полный экран или встроенный фрагмент в других (возможно, в зависимости от того, большой экран или маленький экран). Класс DialogFragment предлагает вам эта гибкость, потому что она все еще может вести себя как встраиваемая Фрагмент.

Однако вы не можете использовать AlertDialog.Builder или другие объекты Dialog для постройте диалог в этом случае. Если вы хотите, чтобы DialogFragment был встраиваемый, вы должны определить диалоговый интерфейс в макете, а затем загрузить макета в обратном вызове onCreateView().

Решена проблема, не создавая ничего на onCreateDialog() (только вернув реализацию суперкласса).

Ответ 5

У меня была аналогичная проблема. Но мой диалог должен был отображаться для IME. Когда я переключился на Dialog вместо AlertDialog и пропустил create, программа работала для меня.

Ответ 6

Это также происходит, если вы пытаетесь получить доступ к любому элементу представления с findViewById() перед onCreate() что в моем случае я сделал:

 private class ShareViaDialog extends Dialog  {
 private ImageView mainSmallIcon = findViewById(R.id.mainSmallIcon);    //this is wrong
  @Override
        protected void onCreate(Bundle savedInstanceState) {
            requestWindowFeature(Window.FEATURE_NO_TITLE);
            super.onCreate(savedInstanceState);
}}

поэтому правильный способ избежать этой ошибки:

 private class ShareViaDialog extends Dialog  {
     private ImageView mainSmallIcon;
      @Override
            protected void onCreate(Bundle savedInstanceState) {
                requestWindowFeature(Window.FEATURE_NO_TITLE);
                super.onCreate(savedInstanceState);
mainSmallIcon = findViewById(R.id.mainSmallIcon); //should be here
    }}

Ответ 7

У меня был диалог Alert в моем приложении, и это давало мне исключение для Android API 23.

Оказалось, эта проблема была с импортом. Я изменил android.app.AlertDialog на androidx.appcompat.app.AlertDialog в импорте, и проблема была решена.

Это для проектов, которые используют AndroidX Artifacts. Если вы не используете AndroidX, рассмотрите возможность использования версии Android.support.v7.app.AlertDialog в AppCompat.

Ответ 8

Точный способ решения этой проблемы:

requestFeature(int) является методом Window. Вы должны импортировать этот класс (android.view.Window).

И вы должны получить окно Window объекта AlertDialog.Builder. Затем вызовите requestFeature(int). Входной параметр является константой окна.

keyBuilder.getWindow().requestFeature(Window.FEATURE_ACTION_BAR);