Подтвердить что ты не робот

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);
            }
        }
    }
}
Создается

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

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

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

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

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

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

Любые предложения?

EDIT:

Снимок экрана: enter image description here

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

4b9b3361

Ответ 1

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

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

Открыв макет Dialog с помощью Hierarchy Viewer, я смог изучить весь макет AlertDialog и что именно что происходило: enter image description here

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

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

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

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

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

Неправильно, измените 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();
}

И вот рабочий результат: enter image description here

Это смущает меня, почему весь 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

Я нашел один вопрос с ответом B T. Диалог вышел из строкового (не в центре экрана). Чтобы исправить это, я добавил изменение гравитации родительских макетов. См. Обновленный метод 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();
}

Ответ 5

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

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);

Ответ 6

Использование setStyle(STYLE_NORMAL, android.R.style.Theme_Holo_Light_Dialog);

Ответ 7

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

Если вы хотите создать собственное диалоговое окно с любым из ваших представлений. Затем вы можете выбрать самый простой способ сделать это.

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

БОНУС. С помощью этого метода вы можете использовать scrollview, recycler view и любой тип макета в диалоговом окне.

В этом примере R.layout.dialog_update_name - образец макета. которые вы хотите раздуть в диалоговом окне.

public void showCustomDialog(final Context context) {
    this.context = context;
    dialog = new Dialog(context);
    dialog.requestWindowFeature(Window.FEATURE_NO_TITLE);
    LayoutInflater inflater = (LayoutInflater) context.getSystemService(Context.LAYOUT_INFLATER_SERVICE);
    view = inflater.inflate(R.layout.dialog_update_name, null, false);
    findByIds(view);  /*HERE YOU CAN FIND YOU IDS AND SET TEXTS OR BUTTONS*/
    ((Activity) context).getWindow().setSoftInputMode(WindowManager.LayoutParams.SOFT_INPUT_STATE_VISIBLE | WindowManager.LayoutParams.SOFT_INPUT_ADJUST_RESIZE);
    dialog.setContentView(view);
    final Window window = dialog.getWindow();
    window.setLayout(WindowManager.LayoutParams.MATCH_PARENT, WindowManager.LayoutParams.WRAP_CONTENT);
    window.setBackgroundDrawableResource(R.color.colorTransparent);
    window.setGravity(Gravity.CENTER);
    dialog.show();
}

Ответ 8

просто используйте 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);
    }
}