J’ai eu ce problème dans une application que je construis. Veuillez ignorer toutes les lacunes en matière de conception et le manque d’approches des meilleures pratiques, ceci est simplement pour montrer un exemple de ce que je ne peux pas résoudre.
J’ai DialogFragment
qui renvoie un AlertDialog
base avec un ensemble de View
personnalisé utilisant AlertDialog.Builder.setView()
. Si cette View
nécessite une taille spécifique, comment puis-je obtenir le redimensionnement correct de la Dialog
de Dialog
pour afficher tout le contenu dans la View
personnalisée?
Voici le code 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éé, ce qui ouvre le DialogFragment
en un clic. La View
personnalisée ( myView
) doit avoir une largeur de 800 et une hauteur de 300, ce qui est correctement défini dans une substitution de onMeasure()
. Cette View
dessine ses limites mesurées en magenta à des fins de débogage.
La largeur 800 est plus large que la taille de Dialog
par défaut de 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:
WindowManager.LayoutParams
de la Dialog
et les remplace en utilisant myDialog.getDialog().getWindow().get/setAtsortingbutes()
setLayout(w, h)
via myDialog.getDialog().getWindow().setLayout()
Je les ai essayés partout où je peux penser ( onStart()
, dans un onShowListener
, après que le Dialog
soit créé et montré, etc.) et peut généralement obtenir les deux méthodes pour fonctionner correctement si les LayoutParams
sont fournis 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:
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 la fenêtre entière est ajustée. Cela fournit donc – si une autre est nécessaire – la raison pour laquelle WRAP_CONTENT
est essentiel les valeurs fixes ne sont pas appropriées):
J’ai une solution de travail qui, pour être honnête, me semble trop profonde pour obtenir un résultat aussi simple. Mais ici c’est:
Qu’est-ce qui se passe exactement:
En ouvrant la mise en page Dialog
avec le visualiseur hiérarchique , j’ai pu examiner l’intégralité de la structure de AlertDialog
et ce qui se passait exactement:
Le point bleu est toutes les parties de haut niveau ( Window
, frameworks pour le style visuel Dialog
, etc.) et à partir de la fin du bleu , les composants pour AlertDialog
sont ( rouge = titre, jaune = un talon de survol, peut-être pour la liste AlertDialog
s, vert = Contenu du Dialog
, c.-à-d. vue personnalisée, orange = boutons).
De là, il était clair que le chemin à 7 vues (du début du bleu à la fin du green ) était ce qui ne parvenait pas à WRAP_CONTENT
. L’ LayoutParams.width
de LayoutParams.width
de chaque View
révélé que tous les éléments sont atsortingbués à LayoutParams.width = MATCH_PARENT
et qu’une taille est définie (en haut, je suppose). Donc, si vous suivez cet arbre, il est clair que votre View
personnalisée en bas de l’arborescence ne pourra jamais affecter la taille du Dialog
.
Alors, quelles étaient les solutions existantes?
View
du haut et à modifier ses LayoutParams
. De toute évidence, avec tous les objects View
de l’arborescence correspondant au parent, si le niveau supérieur est défini sur une taille statique, la Dialog
entière changera de taille. Mais si le niveau supérieur est défini sur WRAP_CONTENT
, tous les autres objects View
de l’arborescence recherchent toujours l’arborescence pour “MATCH leur PARENT”, plutôt que de regarder l’arborescence pour “WRAP leur CONTENU”. Comment résoudre le problème:
Manipulez, modifiez le LayoutParams.width
de tous les objects View
dans le chemin affectant pour être WRAP_CONTENT
.
J’ai trouvé que cela ne pouvait être fait onStart
cycle de vie de DialogFragment
. Donc, le onStart
est implémenté 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); }
Ensuite, la fonction de modifier de manière appropriée la hiérarchie de 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:
Cela m’embrouille du fait que l’ensemble du Dialog
ne voudrait pas être WRAP_CONTENT
avec un jeu de WRAP_CONTENT
explicite pour gérer tous les cas qui correspondent à la taille par défaut, mais je suis sûr qu’il y a une bonne raison entendez-le).
Après
dialog.show();
juste utiliser
dialog.getWindow().setLayout(ViewGroup.LayoutParams.WRAP_CONTENT, yourHeight);
Solution très simple, mais ça marche pour moi. Je prolonge une boîte de dialog mais suppose que cela fonctionnera également pour DialogFragment.
AlertDialog utilise ces deux atsortingbuts de fenêtre pour définir la plus petite taille possible pour que, sur les tablettes, 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 dialog par défaut de votre choix et modifier les valeurs pour appliquer votre propre logique.
J’ai trouvé un problème avec la réponse de BT . Le dialog est resté alloué (pas au centre de l’écran). Pour résoudre ce problème, j’ai ajouté la gravité changeante des mises en page des parents. Voir la méthode mise à jour de 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(); }
Cela a bien fonctionné pour moi:
WindowManager.LayoutParams lp = new WindowManager.LayoutParams(); lp.copyFrom(dialog.getWindow().getAtsortingbutes()); lp.width = WindowManager.LayoutParams.WRAP_CONTENT; lp.height = WindowManager.LayoutParams.WRAP_CONTENT; dialog.show(); dialog.getWindow().setAtsortingbutes(lp);
Utilisez setStyle(STYLE_NORMAL, android.R.style.Theme_Holo_Light_Dialog);
Sa réponse tardive, mais je pense que je devrais partager cela avec les nouveaux venus afin qu’ils sachent quel dialog ils devraient préférer.
Si vous souhaitez créer une boîte de dialog personnalisée avec l’une de vos vues. Ensuite, vous pouvez choisir ci-dessous la manière la plus simple de le faire.
Pour que la question que vous voulez que la boîte de dialog soit un contenu wrap, vous pouvez créer une mise en page parent avec match_parent avec un arrière-plan transparent et avec un centre de gravité. et mettez votre mise en page principale en dessous. il ressemblera donc au dialog positionné au centre.
BONUS: Par cette méthode, vous pouvez utiliser la vue de défilement, la vue du recycleur et tout type de disposition dans la boîte de dialog.
Dans cet exemple, R.layout.dialog_update_name
est la disposition exemple. que vous voulez gonfler sur le dialog.
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(); }
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); } }