Créer des interfaces utilisateur côté client en HTML, partie 1

Ce mois-ci, je reviens à la programmation pendant un moment. J'ai besoin de me reposer de l'étrangeté de la discussion Talkback dans la chronique du mois dernier. J'ai l'intention d'écrire davantage sur les problèmes de théorie à l'avenir, cependant, mais pas pour les deux prochains mois.

J'ai besoin de faire une clarification de la colonne du mois dernier. Beaucoup de gens ont interprété mes commentaires sur les interfaces utilisateur comme préconisant des objets lourds contenant des milliards de méthodes de rendu. Ce n'est pas ce que j'avais en tête. Il existe de nombreuses façons viables de créer des interfaces utilisateur (UI) sans exposer les détails de l'implémentation. Les modèles Gang of Four Builder et Visiteur viennent immédiatement à l'esprit. Une drawYourself()méthode simple ne peut évidemment pas fonctionner sur autre chose que les objets les plus simples, et avoir 50 méthodes drawYourselfInThisFormat()et drawYourselfInThatFormat()est une recette absurde pour un code ingérable. Cependant, beaucoup de gens pensent que je préconise cette approche, alors je m'excuse si j'ai donné cette impression.

Comme j'ai vu beaucoup de malentendus concernant le problème de l'interface utilisateur, je prévois de vous montrer quelques implémentations d'approches de construction d'interface utilisateur orientées objet (OO) dans les prochaines colonnes. J'ai présenté une telle solution dans JavaWorld il y a quelques années (voir Ressources), mais j'ai construit de meilleurs systèmes dans les années intermédiaires. Cette colonne actuelle présente un élément de l'un de ces systèmes d'interface utilisateur: une classe d'infrastructure que j'ai utilisée pour créer des interfaces utilisateur côté client de manière OO. Ce n'est pas en soi une solution au problème de l'interface utilisateur, mais c'est un élément de base utile.

Étant donné que les exemples de code sont assez volumineux, je vais diviser la présentation en deux morceaux. Ce mois-ci, c'est la documentation et le code d'application; le mois prochain est le code source.

Lisez toute la série "Créer des interfaces utilisateur côté client en HTML":

  • Partie 1: Rendre le JEditorPane utile (octobre 2003)
  • Partie 2: Les sources HTMLPane (novembre 2003)

Utilisation du HTML côté client

Le HTML est une chose merveilleuse. Il vous permet de mettre en place des interfaces utilisateur complexes avec un minimum de tracas; il fait un excellent travail de séparation de la structure et de la disposition de l'interface utilisateur de la logique métier; c'est facile à écrire; et c'est facile à entretenir. La disposition de la boîte à outils de la fenêtre abstraite (AWT) / Swing est, en revanche, extrêmement difficile à utiliser. Vous devez modifier (et recompiler) le code pour changer l'apparence de vos écrans, et le code d'une mise en page triviale est lui-même non trivial, s'étendant à de nombreuses pages. Ne serait-il pas bien si vous pouviez spécifier l'intégralité de votre interface utilisateur côté client en HTML?

(Je sais que certains d'entre vous répondront à la question précédente par un "Non, ce ne serait pas bien!". Beaucoup soutiennent que HTML et une bonne expérience utilisateur sont des concepts mutuellement exclusifs, puisque HTML force votre interface utilisateur à "une cascade infinie boîte de dialogue ". D'un autre côté, de nombreuses applications peuvent exploiter efficacement HTML dans au moins une partie de l'interface utilisateur - pour les rapports tabulaires si rien d'autre. Vous ne pouvez pas jeter le bébé avec l'eau du bain.)

La JEditorPaneclasse de Swing , au début, semble être une réponse au problème de mise en page HTML. Il comprend l'entrée HTML d'une certaine manière. Par exemple, le code suivant affiche un cadre qui affiche du texte HTML simple:

JFrame main_frame = nouveau JFrame (); Volet JEditorPane = nouveau JEditorPane (); pane.setContentType ("texte / html"); pane.setEditable (faux); pane.setText ("" + "" + "" + "" + " Hello World " + "" + ""); main_frame.setContentPane (volet); main_frame.setDefaultCloseOperation (JFrame.EXIT_ON_CLOSE); main_frame.pack (); main_frame.show ();

Je dis "à la mode" car il JEditorPanen'est pas particulièrement doué pour gérer du HTML complexe. Il ne fait pas un excellent travail avec les tables imbriquées, et il ne fait pas très bien les feuilles de style en cascade (CSS). (On m'a dit que beaucoup de ces problèmes seront résolus dans la version Java 1.5 l'année prochaine, mais pour le moment, nous devons tous les supporter.) Enfin, JEditorPanene fait pas un travail particulièrement bon de pose des choses comme les boutons radio. Ils ne sont pas correctement alignés avec la ligne de base du texte et affichent toujours un arrière-plan gris, ils ne fonctionnent donc pas bien si vous modifiez la couleur d'arrière-plan de la page (avec un style sur la balise, par exemple).

Tous ces défauts sont ennuyeux, mais le problème avec le show-stop JEditorPaneest qu'il fonctionne comme un contrôle de texte, pas comme une installation de mise en page. Vous pouvez spécifier un HTML dans l'entrée, par exemple, mais le formulaire est soumis à un serveur Web lorsque vous cliquez sur le bouton Soumettre. Pour être utile pour spécifier une interface utilisateur côté client, vous souhaitez que les données du formulaire reviennent au programme qui a affiché le formulaire, et non à un serveur distant. Vous avez également besoin d'un moyen pratique d'ajouter des balises personnalisées à des fins d'entrée ou d'affichage non standard ou de fournir des espaces réservés pour le Swing standard que JComponentsvous souhaitez utiliser sur le formulaire. ( JEditorPanevous permet de le faire, mais le mécanisme est loin d'être pratique.) Enfin, vous devez gérer des choses comme un bouton Annuler, qui n'existe pas en HTML.

Heureusement, le plus flagrant des problèmes ci-dessus peut être résolu en utilisant des fonctions de personnalisation intégrées à JEditorPanelui-même. La résolution de ces problèmes implique cependant un certain compromis. Par exemple, vous pouvez gérer le problème du bouton Annuler en implémentant un interpréteur JavaScript et en prenant en charge l' onclickattribut, mais cela représente beaucoup de travail. De même, fournir une véritable prise en charge des balises personnalisées (où vous pouvez traiter tout ce qui se trouve entre une balise de début et de fin) est très difficile à faire avec l'analyseur existant. Vous pouvez remplacer leJEditorPaneanalyseur avec un meilleur, mais c'est aussi beaucoup de travail. J'ai opté pour des solutions plus simples qui ont fait le travail. J'ai mis suffisamment de fonctionnalités dans ma classe pour pouvoir l'utiliser pour créer une interface utilisateur pour le programme que j'écrivais, mais je n'ai pas fourni une solution «parfaite». Le problème que je résolvais était: fournir un moyen de spécifier une interface utilisateur en HTML. Je ne résolvais pas le problème: fournir un moyen d'afficher tout le HTML possible dans une application côté client. La HTMLPaneclasse que je présente dans cet article résout bien le problème de la spécification d'une interface utilisateur en HTML.

Utilisation du HTMLPane

Ma classe d'entrée HTML côté client uniquement HTMLPane, est un JEditorPanedérivé qui résout les problèmes évoqués précédemment. Le listing 1 montre comment utiliser un HTMLPane. J'ai créé un JDialogdérivé simple appelé HtmlDialogdans lequel vous pouvez spécifier la disposition de la boîte de dialogue au format HTML. Le HtmlDialogest un exemple trivial du modèle de conception Façade. Il fait juste le travail par cœur nécessaire pour mettre un HTMLPanedans une boîte de dialogue et l'afficher.

La HtmlDialog.Testclasse (liste 1, ligne 134) fournit un exemple simple d'utilisation du HtmlDialog. Il crée un cadre principal presque vide ( owner). L'utilisation de code comme l'extrait de code reproduit ci-dessous main()crée un HtmlDialogobjet dont le contenu est spécifié dans le fichier relatif à CLASSPATH com/holub/ui/HTML/test/okay.html(liste 2). La chaîne "Test HtmlDialog"apparaît dans la barre de titre. Enfin, main()affiche la boîte de dialogue en appelant d.popup(), qui ne reviendra pas tant que l'utilisateur n'aura pas fermé la boîte de dialogue:

// Affiche le fichier okay.html dans une boîte de dialogue qui a // le titre "Test HtmlDialog". // Dialogue HtmlDialog = new HtmlDialog (owning_frame, "com / holub / ui / HTML / test / okay.html", "Test HtmlDialog"); // Ouvre la boîte de dialogue et attend que l'utilisateur la ferme. // dialog.popup (); // Imprime les données "formulaire" que l'utilisateur a tapées. // System.out.println ("hidden =" + dialog.data (). GetProperty ("hidden") + "user-input" + dialog.data (). GetProperty ("user-input"));

Les données de formulaire (le texte saisi par l'utilisateur dans un élément ou équivalent), sont disponibles via la méthode HtmlDialogs data(), qui renvoie un java.util.Propertiesobjet contenant des paires clé / valeur représentant les données du formulaire. L'appel ci-dessus à dialog.data().getProperty("hidden")renvoie la chaîne "hidden-field data". L' dialog.data().getProperty("user-input")appel renvoie tout ce que l'utilisateur a tapé dans le champ de saisie.

La plupart du travail impliqué dans l'instanciation de l'encapsulé HTMLPanese produit dans le HtmlDialogconstructeur (Listing 1, ligne 46). Le constructeur configure d'abord un ActionListenerqui gère le bouton Soumettre sur le formulaire. Cet observateur ferme la boîte de dialogue actuelle et copie toutes les données de formulaire de la HTMLPanevers la datavariable d'instance. Le constructeur récupère ensuite le fichier d'entrée du CLASSPATH, puis charge le HTML dans HTMLPaneusing setText(). (Il existe également une setPage(URL)méthode, mais vous auriez besoin d'une URL pour le chemin absolu du fichier si vous l'utilisiez. Je voulais que le nom de fichier HTML soit relatif à CLASSPATH.)

Le traitement d'annulation est géré dans popup()(ligne 121), ce qui suppose qu'un bouton Annuler a été enfoncé si une touche Annuler existe dans les données de formulaire soumises. (Plus d'informations sur la façon dont ces données pénètrent dans l' Propertiesobjet dans un instant.)