wake-up-neo.com

AlertDialog mit benutzerdefinierter Ansicht: Ändern Sie die Größe, um den Inhalt der Ansicht zu umschließen

Ich habe dieses Problem in einer Anwendung, die ich baue. Bitte ignorieren Sie alle Konstruktionsmängel und das Fehlen von Best-Practice-Ansätzen. Dies ist lediglich ein Beispiel für das, was ich nicht lösen kann.

Ich habe DialogFragment, das eine grundlegende AlertDialog mit einer benutzerdefinierten View zurückgibt, die mit AlertDialog.Builder.setView() eingestellt ist. Wenn diese View eine bestimmte Größenanforderung hat, wie bekomme ich die Dialog so, dass sie ihre Größe korrekt ändert, um den gesamten Inhalt in der benutzerdefinierten View anzuzeigen?

Dies ist der Beispielcode, den ich verwendet habe:

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

Es wird eine Button erstellt, die die DialogFragment mit einem Klick öffnet. Die benutzerdefinierte Variable View (myView) muss eine Breite von 800 und eine Höhe von 300 haben, die in einer Überschreibung von onMeasure() korrekt festgelegt ist. Diese View zeichnet ihre gemessenen Grenzen in Magenta für Debugging-Zwecke.

Die Breite von 800 ist breiter als die Standardgröße Dialog auf meinem Gerät, wird jedoch abgeschnitten und nicht richtig gedehnt.

Ich habe die folgenden Lösungen durchgesehen:

Ich habe die folgenden zwei Codierungsansätze abgeleitet:

  1. Rufen Sie den WindowManager.LayoutParams der Dialog ab und überschreiben Sie sie mit myDialog.getDialog().getWindow().get/setAttributes()
  2. Verwenden der setLayout(w, h)-Methode durch myDialog.getDialog().getWindow().setLayout()

Ich habe sie überall ausprobiert, wo ich mir vorstellen kann (überschreibt onStart(), in einer onShowListener, nachdem die Dialog erstellt und gezeigt wurde, usw.) und kann im Allgemeinen beide Methoden korrekt ausführen, wenn die LayoutParams einen spezifischen Wert liefert. Wenn jedoch WRAP_CONTENT geliefert wird, passiert nichts.

Irgendwelche Vorschläge?

EDIT:

Screenshot der Situation: enter image description here

Screenshot eines bestimmten Werts (Hinweis 900 wird hier eingegeben, 850 deckt nicht die gesamte Breite der Ansicht ab, was angesichts der Einstellung des gesamten Fensters sinnvoll ist. Damit wird - falls ein anderer benötigt wurde - der Grund angegeben, warum WRAP_CONTENT unerlässlich ist/feste Werte sind nicht angemessen): enter image description here

52
B T

Ich habe eine funktionierende Lösung, die, um ehrlich zu sein, zu tief ist, um ein so einfaches Ergebnis zu erzielen. Aber hier ist es:

Was genau passiert: 

Durch Öffnen des Dialog-Layouts mit dem Hierarchy Viewer konnte ich das gesamte Layout von AlertDialog überprüfen und was genau vor sich ging: enter image description here

Das Highlight von blue sind alle übergeordneten Teile (Window, Rahmen für den visuellen Stil Dialog usw.), und ab dem Ende des blue -Downs befinden sich die Komponenten für AlertDialog (rot) = title, yellow = ein Scrollview-Stub, möglicherweise für Liste AlertDialogs, green = Dialog-Inhalt (z. B. benutzerdefinierte Ansicht, orange = Schaltflächen).

Von hier aus war klar, dass der Pfad mit 7 Ansichten (vom Anfang des blue bis zum Ende des green) der WRAP_CONTENT fehlgeschlagen war. Ein Blick auf den LayoutParams.width von jedem View ergab, dass alle LayoutParams.width = MATCH_PARENT angegeben sind und irgendwo (ich denke oben) eine Größe eingestellt ist. Wenn Sie diesem Baum folgen, ist es klar, dass Ihre benutzerdefinierte View am unteren Rand des Baums never die Größe von Dialog beeinflussen kann.

Was haben also die bestehenden Lösungen getan?

  • Beide der in meiner Frage erwähnten -Coding-Ansätze erhielten einfach die oberste View und modifizierten deren LayoutParams. Wenn alle View-Objekte in der Baumstruktur dem übergeordneten Objekt entsprechen und die oberste Ebene eine statische Größe hat, ändert sich natürlich die gesamte Dialog-Größe. Wenn jedoch die oberste Ebene auf WRAP_CONTENT eingestellt ist, sind alle übrigen View-Objekte in der Baumstruktur noch suchen in der Baumstruktur, um "ihre Eltern abzustimmen", im Gegensatz zu in der Baumstruktur bis ". WRAP ihr Inhalt ".

So lösen Sie das Problem:

Ändern Sie unverblümt den LayoutParams.width aller View-Objekte im betreffenden Pfad in WRAP_CONTENT.

Ich habe festgestellt, dass dies nur nach dem Aufruf von onStart des Lifecycle-Schrittes von DialogFragment möglich ist. Das onStart ist also wie folgt implementiert:

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

Dann die Funktion, um die View-Hierarchie LayoutParams entsprechend zu ändern:

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

Und hier ist das Arbeitsergebnis: enter image description here

Es verwirrt mich, warum das gesamte Dialog nicht WRAP_CONTENT mit einem expliziten minWidth-Set sein möchte, das alle Fälle behandelt, die in die Standardgröße passen, aber ich bin sicher, dass es einen guten Grund dafür gibt (wäre daran interessiert) Höre es).

58
B T

Nach dem

dialog.show();

benutz einfach

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

Sehr einfache Lösung, aber es funktioniert für mich. Ich erweitere zwar einen Dialog, gehe aber davon aus, dass dies auch für DialogFragment funktioniert.

31
sweggersen

AlertDialogs verwenden diese beiden Fensterattribute, um die kleinste Größe zu definieren, die sie enthalten können, sodass sie auf Tablets mit einer angemessenen Breite in der Mitte des Bildschirms schweben.

http://developer.Android.com/reference/Android/R.attr.html#windowMinWidthMajorhttp://developer.Android.com/reference/Android/R.attr.html# windowMinWidthMinor

Sie können einen Standard-Dialogstil Ihrer Wahl erweitern und die Werte ändern, um Ihre eigene Logik anzuwenden.

11
Simon

Ich habe ein Problem mit der Antwort von BT gefunden . Der Dialog ist nach links ausgerichtet (nicht in der Bildschirmmitte). Um dies zu beheben, habe ich die Schwerkraft der übergeordneten Layouts geändert. Siehe aktualisierte Force Wrap Content () -Methode.

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();
}
4
Yuriy Ashaev

Das hat gut funktioniert für mich:

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);
3
Rony Tesler

Dialog sollte Inhalt umschließen  

Sie können ein übergeordnetes Layout mit match_parent mit transparentem Hintergrund und mit Schwerpunkt . Erstellen und Ihr Hauptlayout darunter legen. so wird er aussehen wie zentriert positionieren.

Mit dieser Methode können Sie Scrollview, RecyclerView und jede Art von Layout im Dialog verwenden.

public void showCustomDialog(Context context) {
    Dialog 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_layout, null, false); 
    findByIds(view);  /*find your views by ids*/
    ((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();
}
1
Khemraj

Verwenden Sie setStyle(STYLE_NORMAL, Android.R.style.Theme_Holo_Light_Dialog);

0
Mickey Tin