Définition de l'apparence
La première étape consiste à définir l'apparence que nous souhaitons donner aux fenêtres de dialogue personnalisées de notre application. Nous allons voir comment transformer l'apparence par défaut fournie par Android en l'apparence présentée ci dessous :Notre fenêtre va fournir les fonctionnalités suivantes :
- Spécification du titre par ressource ou chaine de caractères
- Spécification du contenu par ressource, chaine de caractères ou layout personnalisé
- Utilisation de zéro, un ou deux boutons
Implémentation du thème et du layout
Notre Dialog personnalisé s'appuie sur un layout qui lui est propre et qui permet de positionner les différents éléments qui le composent. Des id sont déclarés afin de permettre la récupération programmatique des titre, boutons ainsi que du corp de la fenêtre :<?xml version="1.0" encoding="utf-8"?> <LinearLayout xmlns:android="http://schemas.android.com/apk/res/android" android:orientation="vertical" android:layout_width="fill_parent" android:minWidth="280dip" android:layout_height="wrap_content"> <LinearLayout android:orientation="vertical" android:background="@drawable/header" android:layout_width="fill_parent" android:layout_height="wrap_content"> <TextView style="@style/DialogText.Title" android:id="@+id/title" android:paddingRight="8dip" android:paddingLeft="8dip" android:background="@drawable/title" android:layout_width="wrap_content" android:layout_height="wrap_content"/> </LinearLayout> <LinearLayout android:id="@+id/content" android:orientation="vertical" android:background="@drawable/center" android:layout_width="fill_parent" android:layout_height="wrap_content"> <TextView style="@style/DialogText" android:id="@+id/message" android:padding="5dip" android:layout_width="fill_parent" android:layout_height="wrap_content"/> </LinearLayout> <LinearLayout android:orientation="horizontal" android:background="@drawable/footer" android:layout_width="fill_parent" android:layout_height="wrap_content"> <Button android:id="@+id/positiveButton" android:layout_marginTop="3dip" android:layout_width="0dip" android:layout_weight="1" android:layout_height="wrap_content" android:singleLine="true"/> <Button android:id="@+id/negativeButton" android:layout_marginTop="3dip" android:layout_width="0dip" android:layout_weight="1" android:layout_height="wrap_content" android:singleLine="true"/> </LinearLayout> </LinearLayout>Le Layout de base utilisé est un LinearLayout fill_parent avec un minimum de 280dip pour que notre fenêtre personnalisée occupe toujours au moins 87,5% de la largeur de l'écran.
Un thème est également créé pour notre Dialog :
<?xml version="1.0" encoding="utf-8"?> <resources> <style name="Dialog" parent="android:style/Theme.Dialog"> <item name="android:windowBackground">@null</item> <item name="android:windowNoTitle">true</item> <item name="android:windowIsFloating">true</item> </style> </resources>Ainsi que pour l'apparence que nous souhaitons donner aux textes contenus dans notre fenêtre :
<?xml version="1.0" encoding="utf-8"?> <resources> <style name="DialogText"> <item name="android:textColor">#FF000000</item> <item name="android:textSize">12sp</item> </style> <style name="DialogText.Title"> <item name="android:textSize">16sp</item> <item name="android:textStyle">bold</item> </style> </resources>
Classe de génération de la fenêtre
Notre Builder personnalisé va reprendre les mêmes méthodes que celles définies par la classe AletDialog.Builder de l'API Android.package net.androgames.blog.sample.customdialog.dialog; import net.androgames.blog.sample.customdialog.R; import android.app.Dialog; import android.content.Context; import android.content.DialogInterface; import android.view.LayoutInflater; import android.view.View; import android.view.ViewGroup.LayoutParams; import android.widget.Button; import android.widget.LinearLayout; import android.widget.TextView; /** * * Create custom Dialog windows for your application * Custom dialogs rely on custom layouts wich allow you to * create and use your own look & feel. * * Under GPL v3 : http://www.gnu.org/licenses/gpl-3.0.html * * @author antoine vianey * */ public class CustomDialog extends Dialog { public CustomDialog(Context context, int theme) { super(context, theme); } public CustomDialog(Context context) { super(context); } /** * Helper class for creating a custom dialog */ public static class Builder { private Context context; private String title; private String message; private String positiveButtonText; private String negativeButtonText; private View contentView; private DialogInterface.OnClickListener positiveButtonClickListener, negativeButtonClickListener; public Builder(Context context) { this.context = context; } /** * Set the Dialog message from String * @param title * @return */ public Builder setMessage(String message) { this.message = message; return this; } /** * Set the Dialog message from resource * @param title * @return */ public Builder setMessage(int message) { this.message = (String) context.getText(message); return this; } /** * Set the Dialog title from resource * @param title * @return */ public Builder setTitle(int title) { this.title = (String) context.getText(title); return this; } /** * Set the Dialog title from String * @param title * @return */ public Builder setTitle(String title) { this.title = title; return this; } /** * Set a custom content view for the Dialog. * If a message is set, the contentView is not * added to the Dialog... * @param v * @return */ public Builder setContentView(View v) { this.contentView = v; return this; } /** * Set the positive button resource and it's listener * @param positiveButtonText * @param listener * @return */ public Builder setPositiveButton(int positiveButtonText, DialogInterface.OnClickListener listener) { this.positiveButtonText = (String) context .getText(positiveButtonText); this.positiveButtonClickListener = listener; return this; } /** * Set the positive button text and it's listener * @param positiveButtonText * @param listener * @return */ public Builder setPositiveButton(String positiveButtonText, DialogInterface.OnClickListener listener) { this.positiveButtonText = positiveButtonText; this.positiveButtonClickListener = listener; return this; } /** * Set the negative button resource and it's listener * @param negativeButtonText * @param listener * @return */ public Builder setNegativeButton(int negativeButtonText, DialogInterface.OnClickListener listener) { this.negativeButtonText = (String) context .getText(negativeButtonText); this.negativeButtonClickListener = listener; return this; } /** * Set the negative button text and it's listener * @param negativeButtonText * @param listener * @return */ public Builder setNegativeButton(String negativeButtonText, DialogInterface.OnClickListener listener) { this.negativeButtonText = negativeButtonText; this.negativeButtonClickListener = listener; return this; } /** * Create the custom dialog */ public CustomDialog create() { LayoutInflater inflater = (LayoutInflater) context .getSystemService(Context.LAYOUT_INFLATER_SERVICE); // instantiate the dialog with the custom Theme final CustomDialog dialog = new CustomDialog(context, R.style.Dialog); View layout = inflater.inflate(R.layout.dialog, null); dialog.addContentView(layout, new LayoutParams( LayoutParams.FILL_PARENT, LayoutParams.WRAP_CONTENT)); // set the dialog title ((TextView) layout.findViewById(R.id.title)).setText(title); // set the confirm button if (positiveButtonText != null) { ((Button) layout.findViewById(R.id.positiveButton)) .setText(positiveButtonText); if (positiveButtonClickListener != null) { ((Button) layout.findViewById(R.id.positiveButton)) .setOnClickListener(new View.OnClickListener() { public void onClick(View v) { positiveButtonClickListener.onClick( dialog, DialogInterface.BUTTON_POSITIVE); } }); } } else { // if no confirm button just set the visibility to GONE layout.findViewById(R.id.positiveButton).setVisibility( View.GONE); } // set the cancel button if (negativeButtonText != null) { ((Button) layout.findViewById(R.id.negativeButton)) .setText(negativeButtonText); if (negativeButtonClickListener != null) { ((Button) layout.findViewById(R.id.negativeButton)) .setOnClickListener(new View.OnClickListener() { public void onClick(View v) { positiveButtonClickListener.onClick( dialog, DialogInterface.BUTTON_NEGATIVE); } }); } } else { // if no confirm button just set the visibility to GONE layout.findViewById(R.id.negativeButton).setVisibility( View.GONE); } // set the content message if (message != null) { ((TextView) layout.findViewById( R.id.message)).setText(message); } else if (contentView != null) { // if no message set // add the contentView to the dialog body ((LinearLayout) layout.findViewById(R.id.content)) .removeAllViews(); ((LinearLayout) layout.findViewById(R.id.content)) .addView(contentView, new LayoutParams( LayoutParams.WRAP_CONTENT, LayoutParams.WRAP_CONTENT)); } dialog.setContentView(layout); return dialog; } } }
Création d'une fenêtre personnalisée
La création d'une fenêtre personnalisé s'effectue de la même manière qu'avec l'utilisation de la classe AletDialog.Builder :/** * Build the desired Dialog * CUSTOM or DEFAULT */ @Override public Dialog onCreateDialog(int dialogId) { Dialog dialog = null; switch (dialogId) { case CUSTOM_DIALOG : CustomDialog.Builder customBuilder = new CustomDialog.Builder(CustomDialogActivity.this); customBuilder.setTitle("Custom title") .setMessage("Custom body") .setNegativeButton("Cancel", new DialogInterface.OnClickListener() { public void onClick(DialogInterface dialog, int which) { CustomDialogActivity.this .dismissDialog(CUSTOM_DIALOG); } }) .setPositiveButton("Confirm", new DialogInterface.OnClickListener() { public void onClick(DialogInterface dialog, int which) { dialog.dismiss(); } }); dialog = customBuilder.create(); break; case DEFAULT_DIALOG : AlertDialog.Builder alertBuilder = new AlertDialog.Builder(CustomDialogActivity.this); alertBuilder.setTitle("Default title") .setMessage("Default body") .setNegativeButton("Cancel", new DialogInterface.OnClickListener() { public void onClick(DialogInterface dialog, int which) { dialog.dismiss(); } }) .setPositiveButton("Confirm", new DialogInterface.OnClickListener() { public void onClick(DialogInterface dialog, int which) { CustomDialogActivity.this .dismissDialog(DEFAULT_DIALOG); } }); dialog = alertBuilder.create(); break; } return dialog; }Vous pouvez récupérer le code source de ce tutorial à cette adresse SampleCustomDialog.
Comme toujours, n'hésitez à faire un retour si vous avez des améliorations à apporter ou si vous souhaitez partager vos créations.
Exemples...
Popup personnalisée pour le jeu Yams disponible sur le market (réalisée à partir de ce tutoriel) |
Il manque encore la manipulation dans une Activity merci de la compléter :)
RépondreSupprimerLa manipulation dans une activité est détaillée dans le dernier bout de code du post...
RépondreSupprimerIl faudrait cependant maintenant adapter un peu le code afin que la popup personnalisée utilise l'API Fragment d'Android : DialogFragment