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:
- DialogFragment.getDialog retourne null
- comment contrôler la largeur et la hauteur de la boîte de dialogue par défaut sur Android?
- Taille du dialogue D'alerte ou Boîte De Dialogue D'Alerte Personnalisée
j'ai déduit les deux approches de codage suivantes:
- Get
WindowManager.LayoutParams
de laDialog
et de les remplacer à l'aide demyDialog.getDialog().getWindow().get/setAttributes()
- utilisant la méthode
setLayout(w, h)
parmyDialog.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:
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):
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:
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 sonLayoutParams
. Évidemment, avec tous les objetsView
dans l'arbre correspondant au parent, si le niveau supérieur est défini une taille statique ,l'ensembleDialog
va changer de taille. Mais si le niveau supérieur est défini àWRAP_CONTENT
, tous les autres objetsView
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:
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).
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.
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.
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();
}
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);
Use
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 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();
}
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);
}
}