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:
WindowManager.LayoutParams
de la Dialog
et remplacez-le à l'aide de myDialog.getDialog().getWindow().get/setAttributes()
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:
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):
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 Dialog
avec visionneuse de hiérarchie , j'ai pu examiner la présentation complète de la AlertDialog
et savoir ce qui se passait exactement:
La surbrillance bleue bleue représente toutes les parties de haut niveau (Window
name__, frames pour le style visuel Dialog
name__, 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 AlertDialog
name__s, green = Dialog
contenu, 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 View
name__, 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 View
personnalisé en bas de l’arborescence ne pourra jamais affecter la taille de la Dialog
.
Alors, que faisaient les solutions existantes?
View
et modifiaient son LayoutParams
name__. Évidemment, avec tous les objets View
de l'arborescence correspondant au parent, si le niveau supérieur est défini avec une taille statique, l'intégralité de Dialog
changera de taille. Mais si le niveau supérieur est défini sur WRAP_CONTENT
, tous les autres objets View
de 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 View
dans le chemin affectant en WRAP_CONTENT
.
J'ai constaté que cela ne pouvait être fait qu'après l'appel de onStart
name____ cycle de cycle de vie du DialogFragment
name__. Donc, onStart
est 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 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();
}
Et voici le résultat de travail:
Cela me laisse perplexe sur le fait que tout Dialog
ne voudrait pas être WRAP_CONTENT
avec un ensemble minWidth
explicite 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).
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.
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.
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();
}
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);
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();
}
Utilisez setStyle(STYLE_NORMAL, Android.R.style.Theme_Holo_Light_Dialog);