Android

AlertDialog with custom view: Resize to wrap the view's content

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 одним щелчком мыши. Пользовательское значение View (myView) должно иметь ширину 800 и высоту 300, которые правильно заданы при переопределении onMeasure(). Это View отображает его измеренные границы пурпурным цветом для целей отладки.

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

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

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


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

  2. Используя setLayout(w, h) метод через myDialog.getDialog().getWindow().setLayout()

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

Есть предложения?

Редактировать:

Скриншот ситуации: введите описание изображения здесь

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

Переведено автоматически
Ответ 1

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

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

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

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

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

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


  • Оба подхода к кодированию, упомянутые в моем вопросе, заключались в простом получении верхней части View и изменении ее LayoutParams. Очевидно, что со всеми View объектами в дереве, соответствующими родительскому, если на верхнем уровне установлен статический размер, все Dialog изменит размер. Но если установлен верхний уровень в WRAP_CONTENT, все остальные View объекты в дереве по-прежнему ищут вверх по дереву, чтобы "СООТВЕТСТВОВАТЬ своему РОДИТЕЛЮ", в отличие от поиска вниз по дереву, чтобы "ОБЕРНУТЬ их СОДЕРЖИМОЕ".

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

Грубо говоря, измените LayoutParams.width всех View объектов в влияющем пути на WRAP_CONTENT.

Я обнаружил, что это может быть сделано только ПОСЛЕ вызова 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 набором для обработки всех случаев, которые укладываются в размер по умолчанию, но я уверен, что для этого есть веская причина таким, какой он есть (было бы интересно услышать это).

Ответ 2

После

dialog.show();

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

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

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

Ответ 3

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

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

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

Ответ 4

У меня это сработало нормально:

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);
java android android-layout