AlertDialog avec vue personnalisée: redimensionner pour envelopper le contenu de la vue

j'ai eu ce problème dans une application que je construis. S'il vous plaît, ignorez toutes les lacunes de la conception et l'absence de meilleures pratiques, c'est purement pour montrer un exemple de ce que je ne peux pas résoudre.

j'ai DialogFragment qui renvoie une base AlertDialog avec un View défini à l'aide de AlertDialog.Builder.setView() . Si ce View a une exigence de taille spécifique, Comment puis-je obtenir le Dialog de se redimensionner correctement pour afficher tous les contenu dans la coutume View ?

C'est le code d'exemple que j'ai utilisé:

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

un Button est créé, qui ouvre le DialogFragment sur un clic. Le custom View ( myView ) est requis pour avoir une largeur de 800 et une hauteur de 300 qui est correctement réglé dans un override de onMeasure() . Ce View , tire ses limites mesurées en magenta pour les besoins du débogage.

le 800 la largeur est plus large que la taille par défaut Dialog sur mon appareil, mais est coupée plutôt que de s'étirer correctement.

j'ai examiné les solutions suivantes:

j'ai déduit les deux approches de codage suivantes:

  1. Get WindowManager.LayoutParams de la Dialog et de les remplacer à l'aide de myDialog.getDialog().getWindow().get/setAttributes()
  2. utilisant la méthode setLayout(w, h) par myDialog.getDialog().getWindow().setLayout()

je les ai essayés partout où je peux penser (en remplaçant onStart() , dans un onShowListener , après le Dialog est créé et affiché, etc) et peut généralement faire fonctionner correctement les deux méthodes si les LayoutParams sont fournis une valeur spécifique . Mais chaque fois que WRAP_CONTENT est fourni, rien ne se passe.

des suggestions?

EDIT:

Capture d'écran de la situation: enter image description here

capture D'écran d'une valeur spécifique (la note 900 est entrée ici), 850 ne couvre pas toute la largeur de la vue, ce qui est logique étant donné que toute la fenêtre est ajustée. De sorte que fournit - si un autre était nécessaire-raison pour laquelle WRAP_CONTENT est essentiel / valeurs fixes ne sont pas appropriés): enter image description here

48
demandé sur Community 2013-02-16 08:54:59

8 réponses

j'ai une solution de travail que pour être honnête, je pense creuse bien trop profond pour obtenir un résultat si simple. Mais le voici:

Ce qui se passe exactement:

en ouvrant la mise en page Dialog avec le visionneur de hiérarchie , j'ai pu examiner la mise en page entière du AlertDialog et ce qui se passait exactement: enter image description here

le bleu le point culminant est toutes les parties de haut niveau ( Window , cadres pour le Dialog style visuel, etc) et de la fin du bleu vers le bas est où les composants pour le AlertDialog sont ( rouge = titre, jaune = un talon de scrollview, peut-être pour la liste AlertDialog s, Vert = Dialog content i.e. custom view, orange = bouton.)

de là, il était clair que le chemin de 7-view (du début du bleu à la fin du Vert ) était ce qui n'était pas correctement WRAP_CONTENT . En regardant le LayoutParams.width de chaque View révélé que tous sont donnés LayoutParams.width = MATCH_PARENT et quelque part (je suppose au sommet) une taille est fixée. Donc, si vous suivez cet arbre, il est clair que votre custom View au bas de l'arbre, jamais être en mesure d'influer sur la taille de la Dialog .

que faisaient donc les solutions existantes?

  • les deux approches de codage mentionnées dans ma question étaient simplement l'obtention du haut View et la modification de son LayoutParams . Évidemment, avec tous les objets View dans l'arbre correspondant au parent, si le niveau supérieur est défini une taille statique ,l'ensemble Dialog va changer de taille. Mais si le niveau supérieur est défini à WRAP_CONTENT , tous les autres objets View dans l'arbre sont encore en regardant l'arbre pour" correspondre à leur PARENT", par opposition à en regardant l'arbre pour"envelopper leur contenu".

comment résoudre le problème:

changer carrément le LayoutParams.width de tous les View objets dans l'affecting chemin à WRAP_CONTENT .

j'ai trouvé que cela ne pouvait être fait QU'après onStart étape du cycle de vie du DialogFragment est appelé. Ainsi le onStart est mis en œuvre comme:

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

puis la fonction de modifier de manière appropriée la View hiérarchie 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();
}

Et voici le résultat de travail: enter image description here

il me confond pourquoi l'ensemble Dialog ne voudrait pas être WRAP_CONTENT avec un minWidth explicitement défini pour gérer tous les cas qui s'inscrivent à l'intérieur de la taille par défaut, mais je suis sûr qu'il ya une bonne raison pour cela la façon dont il est (serait intéressé de l'entendre).

57
répondu B T 2013-02-17 23:54:56

après

dialog.show();

il suffit d'utiliser

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

solution très simple, mais ça marche pour moi. Je suis en train d'étendre un dialogue cependant, mais suppose que cela fonctionnera pour DialogFragment aussi.

30
répondu sweggersen 2013-07-18 13:32:55

Alertdialog's utiliser ces deux attributs de fenêtre pour définir la plus petite taille, ils peuvent être de sorte que sur les comprimés ils flottent avec une largeur raisonnable au centre de l'écran.

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

vous pouvez étendre un style de dialogue par défaut de votre choisissez et changez les valeurs pour appliquer votre propre logique.

9
répondu Simon 2015-04-07 13:36:04

j'ai trouvé un problème avec B T la réponse de . La boîte de dialogue a laissé un alignement (pas au centre de l'écran). Pour corriger cela, j'ai ajouté le changement de gravité des mises en page parentes. Voir la méthode updated 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();
}
3
répondu Yuriy Ashaev 2017-05-23 12:34:43

cela a bien fonctionné pour moi:

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
répondu Rony Tesler 2016-09-05 10:27:00

Use setStyle(STYLE_NORMAL, android.R.style.Theme_Holo_Light_Dialog);

0
répondu Mickey Tin 2015-05-21 15:07:26

sa réponse tardive, mais je pense que je devrais partager cela avec les nouveaux venus afin qu'ils sachent quel dialogue ils devraient préférer.

si vous voulez faire un dialogue personnalisé avec l'un de vos points de vue. Ensuite, vous pouvez choisir ci-dessous la manière la plus facile de le faire.

pour la question que vous voulez dialoguer doit être wrap content, vous pouvez faire une mise en page parent avec match_parent avec fond transparent et avec centre de gravité. et mettez votre disposition principale en vertu de l'. il ressemblera donc à Dialogue positionné au centre.

BONUS: par cette méthode, vous pouvez utiliser scrollview, recycler view et tout type de layout dans la boîte de dialogue.

dans cet exemple, R.layout.dialog_update_name est la disposition de l'échantillon. qui vous voulez gonfler sur le dialogue.

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();
}
0
répondu Khemraj 2017-10-27 21:00:35

il suffit d'utiliser 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);
    }
}
-1
répondu zlxrx 2016-03-18 12:43:59