web-dev-qa-db-fra.com

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. Veuillez ignorer toutes les lacunes de la conception et le manque d’approches de meilleures pratiques, c’est uniquement pour montrer un exemple de ce que je ne peux pas résoudre.

J'ai DialogFragment qui retourne une AlertDialog de base avec un ensemble View personnalisé à l'aide de AlertDialog.Builder.setView(). Si cette View a une taille spécifique, comment puis-je redimensionner correctement la Dialog pour afficher tout le contenu dans la View personnalisée?

Voici l'exemple de code 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);
            }
        }
    }
}

Une Button est créée, ce qui ouvre la DialogFragment sur un clic. La coutume View (myView) doit avoir une largeur de 800 et une hauteur de 300 qui est correctement définie dans un remplacement de onMeasure(). Cette View trace ses limites mesurées en magenta à des fins de débogage.

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

J'ai parcouru les solutions suivantes:

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

  1. Obtenez le WindowManager.LayoutParams de la Dialog et remplacez-le à l'aide de myDialog.getDialog().getWindow().get/setAttributes()
  2. Utilisation de la méthode setLayout(w, h) à travers myDialog.getDialog().getWindow().setLayout()

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

Aucune suggestion?

MODIFIER:

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. Ainsi, si un autre paramètre était nécessaire, la raison pour laquelle WRAP_CONTENT est essentiel/les valeurs fixes ne sont pas appropriées): enter image description here

52
B T

J'ai une solution de travail qui pour être honnête, je pense creuser beaucoup trop profondément pour obtenir un résultat aussi simple. Mais le voici:

Qu'est-ce qui se passe exactement:

En ouvrant la présentation Dialogavec visionneuse de hiérarchie , j'ai pu examiner la présentation complète de la AlertDialoget savoir ce qui se passait exactement: enter image description here

La surbrillance bleue bleue représente toutes les parties de haut niveau (Windowname__, frames pour le style visuel Dialogname__, etc.) et à la fin du blue down est l'endroit où se trouvent les composants de AlertDialog( red = title, yellow = un module de défilement, peut-être pour la liste AlertDialogname__s, green = Dialogcontenu, c'est-à-dire personnalisé vue, orange = boutons).

A partir de là, il était clair que le chemin à 7 vues (du début du bleu à la fin du vert ) était ce qui échouait correctement WRAP_CONTENT. En regardant le LayoutParams.width de chaque Viewname__, on s'aperçut que tous recevaient LayoutParams.width = MATCH_PARENT et quelque part (je suppose tout en haut) une taille définie. Donc, si vous suivez cette arborescence, il est clair que votre nom Viewpersonnalisé en bas de l’arborescence ne pourra jamais affecter la taille de la Dialog.

Alors, que faisaient les solutions existantes?

  • Les deux approches de codage mentionnées dans ma question obtenaient simplement le top Viewet modifiaient son LayoutParamsname__. Évidemment, avec tous les objets Viewde l'arborescence correspondant au parent, si le niveau supérieur est défini avec une taille statique, l'intégralité de Dialogchangera de taille. Mais si le niveau supérieur est défini sur WRAP_CONTENT, tous les autres objets Viewde l’arborescence sont toujours recherchant dans l’arbre le résultat "CORRESPONDANT PARENT", comme opposé à en regardant l’arbre pour "WRAP leur contenu".

Comment résoudre le problème:

Franchement, changez le LayoutParams.width de tous les objets Viewdans le chemin affectant en WRAP_CONTENT.

J'ai constaté que cela ne pouvait être fait qu'après l'appel de onStartname____ cycle de cycle de vie du DialogFragmentname__. Donc, onStartest implémenté comme suit:

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

Ensuite, la fonction pour modifier de manière appropriée la hiérarchie ViewLayoutParams_:

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

Cela me laisse perplexe sur le fait que tout Dialogne voudrait pas être WRAP_CONTENT avec un ensemble minWidthexplicite pour traiter tous les cas qui rentrent dans la taille par défaut, mais je suis sûr qu’il existe une bonne raison pour cela (comme cela est le cas) ( serait intéressé de l'entendre).

60
B T

Après

dialog.show();

juste utiliser

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

Solution très simple, mais cela fonctionne pour moi. J'étends un dialogue, mais je suppose que cela fonctionnera également pour DialogFragment.

31
sweggersen

AlertDialog utilise ces deux attributs de fenêtre pour définir la plus petite taille possible, de sorte qu'ils flottent sur les tablettes avec une largeur raisonnable au centre de l'écran.

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

Vous pouvez étendre un style de dialogue par défaut de votre choix et modifier les valeurs pour appliquer votre propre logique.

11
Simon

J'ai trouvé un problème avec la réponse de B T . La boîte de dialogue est alignée à gauche (pas au centre de l'écran). Pour résoudre ce problème, j'ai ajouté la gravité changeante des mises en page parent. Voir la méthode force Wrap Content () mise à jour.

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

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
Rony Tesler

Le dialogue doit être un contenu intégré 

Vous pouvez créer une mise en page parent avec match_parent avec fond transparent et avec centre de gravité . Et y placer votre mise en page principale. donc ressemblera à centre positionné dans la boîte de dialogue.

Par cette méthode, vous pouvez utiliser Scrollview, RecyclerView et n’importe quel type de disposition dans la boîte de dialogue.

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

Utilisez setStyle(STYLE_NORMAL, Android.R.style.Theme_Holo_Light_Dialog);

0
Mickey Tin