Intereting Posts

AlertDialog с пользовательским представлением: изменить размер, чтобы обернуть содержимое представления

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

У меня есть DialogFragment который возвращает базовый AlertDialog с настраиваемым View использующим AlertDialog.Builder.setView() . Если этот View имеет требование к конкретному размеру, как мне заставить Dialog правильно изменять размер, чтобы отобразить весь контент в пользовательском View ?

Это пример кода, который я использовал:

 package com.test.test; import android.os.Bundle; import android.app.Activity; import android.app.AlertDialog; import android.app.Dialog; import android.app.DialogFragment; import android.content.Context; import android.graphics.Canvas; import android.graphics.Color; import android.graphics.Paint; import android.graphics.Paint.Style; import android.view.Gravity; import android.view.LayoutInflater; import android.view.View; import android.view.WindowManager; import android.view.View.OnClickListener; import android.view.ViewGroup; import android.view.ViewGroup.LayoutParams; import android.widget.ArrayAdapter; import android.widget.Button; import android.widget.EditText; import android.widget.FrameLayout; import android.widget.LinearLayout; import android.widget.Spinner; import android.widget.TextView; public class MainActivity extends Activity { @Override protected void onCreate(Bundle savedInstanceState) { super.onCreate(savedInstanceState); // Use a button for launching Button b = new Button(this); b.setText("Launch"); b.setOnClickListener(new OnClickListener() { @Override public void onClick(View v) { // Launch the dialog myDialog d = new myDialog(); d.show(getFragmentManager(), null); } }); setContentView(b); } public static class myDialog extends DialogFragment { @Override public Dialog onCreateDialog(Bundle savedInstanceState) { // Create the dialog AlertDialog.Builder db = new AlertDialog.Builder(getActivity()); db.setTitle("Test Alert Dialog:"); db.setView(new myView(getActivity())); return db.create(); } protected class myView extends View { Paint p = null; public myView(Context ct) { super(ct); // Setup paint for the drawing p = new Paint(); p.setColor(Color.MAGENTA); p.setStyle(Style.STROKE); p.setStrokeWidth(10); } @Override protected void onMeasure(int widthMeasureSpec, int heightMeasureSpec) { setMeasuredDimension(800, 300); } @Override protected void onDraw(Canvas canvas) { // Draw a rectangle showing the bounds of the view canvas.drawRect(0, 0, getMeasuredWidth(), getMeasuredHeight(), p); } } } } 

Создается Button , которая открывает DialogFragment по DialogFragment . Пользовательский View ( myView ) должен иметь ширину 800 и высоту 300, которая правильно установлена ​​в переопределении onMeasure() . Этот View отображает измеренные границы в пурпуре для целей отладки.

Ширина 800 шире, чем размер Dialog окна по умолчанию на моем устройстве, но обрезается, а не растягивается правильно.

Я рассмотрел следующие решения:

  • DialogFragment.getDialog возвращает null
  • Как контролировать ширину и высоту диалогового окна Alert Dial по умолчанию в Android?
  • Диалоговое окно оповещения или диалоговое окно Custom Alert

Я вывел следующие два подхода к кодированию:

  1. Получите WindowManager.LayoutParams Dialog myDialog.getDialog().getWindow().get/setAttributes() и переопределите их с помощью myDialog.getDialog().getWindow().get/setAttributes()
  2. Используя метод setLayout(w, h) через myDialog.getDialog().getWindow().setLayout()

Я пробовал их везде, о чем я могу думать (переопределяя onStart() , в onShowListener , после того, как Dialog создан и показан и т. Д.), И обычно может заставить оба метода работать правильно, если LayoutParams поставляется с определенным значением . Но всякий раз, когда предоставляется WRAP_CONTENT , ничего не происходит.

Какие-либо предложения?

РЕДАКТИРОВАТЬ:

Снимок экрана: Введите описание изображения здесь

Снимок экрана с конкретным значением (здесь упоминается примечание 900, 850 не охватывает всю ширину представления, что имеет смысл, учитывая, что все окно корректируется. Таким образом, это обеспечивает – если нужно другое – причина, по которой WRAP_CONTENT является существенным / Фиксированные значения не подходят): Введите описание изображения здесь

Solutions Collecting From Web of "AlertDialog с пользовательским представлением: изменить размер, чтобы обернуть содержимое представления"

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

Что именно происходит:

Dialog макет Dialog с помощью средства просмотра иерархии , я смог изучить весь макет AlertDialog и что именно произошло: Введите описание изображения здесь

Синяя подсветка – это все части высокого уровня ( Window , рамки для визуального стиля Dialog и т. Д.), А с конца синего вниз – компоненты для AlertDialog ( красный = заголовок, желтый = прокрутка прокрутки, возможно Для списка AlertDialog s, зеленый = содержимое Dialog AlertDialog , т.е. пользовательский вид, оранжевый = кнопки).

Отсюда было ясно, что путь 7-view (от начала синего до конца зеленого ) был тем, что не удалось правильно WRAP_CONTENT . Глядя на LayoutParams.width каждого View выяснилось, что все даны LayoutParams.width = MATCH_PARENT и где-то (я думаю, наверху) задан размер. Поэтому, если вы будете следовать этому дереву, ясно, что ваш пользовательский View в нижней части дерева никогда не сможет повлиять на размер Dialog .

Итак, каковы были существующие решения?

  • Оба подходов к кодированию, упомянутые в моем вопросе, просто получили верхний View и LayoutParams его LayoutParams . Очевидно, что при всех объектах View в дереве, соответствующем родительскому элементу, если на верхнем уровне установлен статический размер, весь Dialog изменит размер. Но если верхний уровень установлен на WRAP_CONTENT , все остальные объекты View в дереве по-прежнему ищут дерево для «MATCH the PARENT», а не для просмотра дерева «WRAP their CONTENT».

Как решить проблему:

Прямо, измените LayoutParams.width всех объектов View в тропе воздействия на WRAP_CONTENT .

Я обнаружил, что это можно сделать только после того, как onStart этап жизненного цикла onStart для DialogFragment . Таким образом, onStart реализован так:

 @Override public void onStart() { // This MUST be called first! Otherwise the view tweaking will not be present in the displayed Dialog (most likely overriden) super.onStart(); forceWrapContent(myCustomView); } 

Затем функция для правильного изменения иерархии View LayoutParams :

 protected void forceWrapContent(View v) { // Start with the provided view View current = v; // Travel up the tree until fail, modifying the LayoutParams do { // Get the parent ViewParent parent = current.getParent(); // Check if the parent exists if (parent != null) { // Get the view try { current = (View) parent; } catch (ClassCastException e) { // This will happen when at the top view, it cannot be cast to a View break; } // Modify the layout current.getLayoutParams().width = LayoutParams.WRAP_CONTENT; } } while (current.getParent() != null); // Request a layout to be re-done current.requestLayout(); } 

И вот рабочий результат: Введите описание изображения здесь

Это меня смущает, почему весь Dialog не хочет быть WRAP_CONTENT с явным набором minWidth для обработки всех случаев, которые соответствуют размеру по умолчанию, но я уверен, что для этого есть веская причина (он был бы заинтересован слышать).

После

 dialog.show(); 

Просто используйте

 dialog.getWindow().setLayout(ViewGroup.LayoutParams.WRAP_CONTENT, yourHeight); 

Очень простое решение, но оно работает для меня. Я расширяю диалог, но предполагаю, что это будет работать и для DialogFragment.

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

http://developer.android.com/reference/android/R.attr.html#windowMinWidthMajor http://developer.android.com/reference/android/R.attr.html#windowMinWidthMinor

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

Я нашел одну проблему с ответом БТ . Диалог вышел из строкового (не в центре экрана). Чтобы исправить это, я добавил изменение гравитации родительских макетов. См. Обновленный метод forceWrapContent ().

 protected void forceWrapContent(View v) { // Start with the provided view View current = v; // Travel up the tree until fail, modifying the LayoutParams do { // Get the parent ViewParent parent = current.getParent(); // Check if the parent exists if (parent != null) { // Get the view try { current = (View) parent; ViewGroup.LayoutParams layoutParams = current.getLayoutParams(); if (layoutParams instanceof FrameLayout.LayoutParams) { ((FrameLayout.LayoutParams) layoutParams). gravity = Gravity.CENTER_HORIZONTAL; } else if (layoutParams instanceof WindowManager.LayoutParams) { ((WindowManager.LayoutParams) layoutParams). gravity = Gravity.CENTER_HORIZONTAL; } } catch (ClassCastException e) { // This will happen when at the top view, it cannot be cast to a View break; } // Modify the layout current.getLayoutParams().width = ViewGroup.LayoutParams.WRAP_CONTENT; } } while (current.getParent() != null); // Request a layout to be re-done current.requestLayout(); } 

Это работало нормально для меня:

 WindowManager.LayoutParams lp = new WindowManager.LayoutParams(); lp.copyFrom(dialog.getWindow().getAttributes()); lp.width = WindowManager.LayoutParams.WRAP_CONTENT; lp.height = WindowManager.LayoutParams.WRAP_CONTENT; dialog.show(); dialog.getWindow().setAttributes(lp); 

Используйте setStyle(STYLE_NORMAL, android.R.style.Theme_Holo_Light_Dialog);

Просто используйте AppCompatDialog

 import android.content.Context; import android.os.Bundle; import android.support.v7.app.AppCompatDialog; import android.view.Window; import android.widget.ProgressBar; public class ProgressDialogCompat extends AppCompatDialog { public ProgressDialogCompat(Context context) { super(context); supportRequestWindowFeature(Window.FEATURE_NO_TITLE); } @Override protected void onCreate(Bundle savedInstanceState) { Context context = getContext(); int padding = context.getResources().getDimensionPixelSize(R.dimen.x_medium); ProgressBar progressBar = new ProgressBar(context); progressBar.setPadding(padding, padding, padding, padding); setContentView(progressBar); setCancelable(false); super.onCreate(savedInstanceState); } }