svn-gvsig-desktop / tags / v1_11_0_Build_1305 / docs / Andami developer guide.html @ 47796
History | View | Annotate | Download (16.6 KB)
1 | 1935 | vcaballero | <!DOCTYPE html PUBLIC "-//W3C//DTD HTML 4.01 Transitional//EN">
|
---|---|---|---|
2 | <html>
|
||
3 | <head>
|
||
4 | <meta content="text/html; charset=ISO-8859-1" |
||
5 | http-equiv="content-type"> |
||
6 | <title>Andami developer guide</title> |
||
7 | <link href="style.css" rel="stylesheet" type="text/css"> |
||
8 | </head>
|
||
9 | <body>
|
||
10 | <table width="90%" border="0"> |
||
11 | <tr>
|
||
12 | <td class="Nivel2"><a href="#Introduccion"><strong><font color="#FFFFFF">Introducción</font></strong></a></td> |
||
13 | </tr>
|
||
14 | <tr>
|
||
15 | <td class="Nivel2"><a href="#Creacion_de_plugins"><font color="#FFFFFF"><strong>Creación |
||
16 | de plugins</strong></font></a></td> |
||
17 | </tr>
|
||
18 | <tr>
|
||
19 | <td class="Nivel2"><a href="#Funcionamiento_del_class_loader"><strong><font color="#FFFFFF">Funcionamiento |
||
20 | del class loader</font></strong></a></td> |
||
21 | </tr>
|
||
22 | <tr>
|
||
23 | <td class="Nivel2"><a href="#Servicios_a_los_plugins"><font color="#FFFFFF"><strong>Servicios |
||
24 | a los plugins</strong></font></a></td> |
||
25 | </tr>
|
||
26 | <tr>
|
||
27 | <td><strong> <a |
||
28 | href="#Ejecucion_en_segundo_plano">Ejecución en segundo plano</a></strong></td> |
||
29 | </tr>
|
||
30 | <tr>
|
||
31 | <td><strong> <a href="#Acceso_a_las_extensiones">Acceso |
||
32 | a las extensiones</a></strong></td> |
||
33 | </tr>
|
||
34 | <tr>
|
||
35 | <td class="urbanismo"><strong> <a href="#Persistencia_de_los_plugins">Persistencia |
||
36 | de los plugins</a></strong></td> |
||
37 | </tr>
|
||
38 | <tr>
|
||
39 | <td><strong> <a href="#popupmenus">Pop-up menus</a></strong></td> |
||
40 | </tr>
|
||
41 | <tr>
|
||
42 | <td><strong> <a href="#Vistas">Vistas</a></strong></td> |
||
43 | </tr>
|
||
44 | <tr>
|
||
45 | <td> <a href="#SingletonView">SingletonView's</a></td> |
||
46 | </tr>
|
||
47 | </table>
|
||
48 | |||
49 | |||
50 | |||
51 | <p> <strong><font size="+1"><a name="Introduccion"></a>Introducción</font></strong></p> |
||
52 | <p> Andami es un framework orientado a plugins construido sobre |
||
53 | swing que permite la construcción de forma rápida y extensible |
||
54 | de aplicaciones MDI (Multiple Document Interface). Tiene ya implementadas muchas |
||
55 | de las funcionalidades requeridas para este tipo de aplicaciones tales como |
||
56 | un menú Ventana, en la que van apareciendo las ventanas que se abren,
|
||
57 | soporte para el traducciones, configuración personalizada para cada usuario,
|
||
58 | actualizaciones automáticas, tanto del propio Andami como de los plugins,
|
||
59 | persistencia del tamaño y posición de la ventana, del idioma, |
||
60 | distintos tipos de vista, con la finalidad de facilitar la programación
|
||
61 | de ventanas especiales, ... Todo esto además de tener ya solventados
|
||
62 | los problemas típicos de la programación de un entorno como éste |
||
63 | que no son pocos.<br>
|
||
64 | <br>
|
||
65 | Además, Andami está diseñada de forma |
||
66 | que la propia lógica MDI puede ser reemplazada. Si en lugar de una aplicación |
||
67 | estilo arcView, se quiere que cada vista que se añada, se haga en una
|
||
68 | ventana de windows nueva (de las que aparecen en la barra de estado) o una vista |
||
69 | como la de Eclipse, no hay más que desarrollar el plugin adecuado.<br> |
||
70 | <br>
|
||
71 | Andami está diseñada para ser amistosa con |
||
72 | el usuario y para ello incorpora la posibilidad de añadir tooltips y
|
||
73 | enabletexts a los botones. El tooltip es el texto que aparece cuando el ratón
|
||
74 | se detiene sobre un botón o menú. El enableText es el texto que |
||
75 | aparece en el caso anterior cuando el botón está desactivado, |
||
76 | permitiendo mostrar al usuario qué es lo que debe hacer para que dicho
|
||
77 | botón se active (o cualquier otro mensaje).<br> |
||
78 | <br>
|
||
79 | Por último, Andami tiene una gestión de errores |
||
80 | que agradará a cualquier programador, ya que en casos de errores graves,
|
||
81 | el propio framework avisa de dicho fallo y aconseja al usuario salvar los cambios |
||
82 | y reiniciar el programa, a parte de que toda excepción no capturada por
|
||
83 | el usuario se redirige a un fichero de log configurable en el que se escribirá
|
||
84 | su traza.<br>
|
||
85 | </p>
|
||
86 | <p> <strong><font size="+1"><a name="Creacion_de_plugins"></a>Creación |
||
87 | de plugins</font></strong></p> |
||
88 | <p> Andami gira en torno al concepto de plugin. Andami mantiene |
||
89 | un directorio como directorio de los plugins, que se puede cambiar en cualquier |
||
90 | momento. Un plugin viene definido por la existencia de un subdirectorio dentro |
||
91 | del directorio de los plugins, siendo el nombre del plugin el nombre de dicho |
||
92 | directorio. Dentro de dicho directorio debe haber un fichero config.xml en el |
||
93 | que se configuran los puntos de entrada y salida del plugin (menúes,
|
||
94 | barras de herramientas, etiquetas de la barra de estado, menúes contextuales),
|
||
95 | las librerías que va a usar, el paquete de traducciones, los plugins
|
||
96 | de los que depende, etc. El fichero plugin-config.xsd contiene el esquema que |
||
97 | ha de seguir este fichero y están comentados todos los elementos. En
|
||
98 | el CorePlugin que viene con Adami se puede ver un ejemplo de config.xml.<br>
|
||
99 | <br>
|
||
100 | Las rutas de los directorios son siempre relativas al directorio |
||
101 | del plugin y los textos de los menúes, tooltips y enabletext's son claves
|
||
102 | en el fichero de traducciones en caso de que haya y textos literales en caso |
||
103 | de que no haya traducciones. El fichero de traducciones es un fichero de propiedades |
||
104 | común: ver ResourceBundle en la API de Java.<br> |
||
105 | <br>
|
||
106 | Dentro de los plugins aparece el concepto de extensión. |
||
107 | Una extensión es instalada por los plugins mediante la implementación |
||
108 | de la interfaz com.iver.andami.plugins.Extension y la instalación de
|
||
109 | unos controles geobernados por ésta en el fichero config.xml. Mediante
|
||
110 | esta implementación se le dice a Andami la condición que se debe |
||
111 | cumplir para que los controles sean visibles o estén activos. Además |
||
112 | se implementa la acción a llevar a cabo cuando se selecciona uno de los
|
||
113 | menúes o botones asociados a la extensión. Cabe resaltar que Andami |
||
114 | crea una instancia de cada extensión configurada en config.xml, por lo
|
||
115 | que las clases que implementen la interfaz Extension deben de tener un constructor |
||
116 | sin argumentos.<br>
|
||
117 | <br>
|
||
118 | En los tag 'extension' existe un atributo class-name en el |
||
119 | que se especifica la clase que gobierna el punto de extensión que se
|
||
120 | está definiendo. Esta clase deberá implementar la interfaz com.iver.andami.plugins.Extension |
||
121 | y será mediante ésta que gobernará los menúes y |
||
122 | botones asociados a este punto de extensión<br> |
||
123 | <br>
|
||
124 | Una problema común en este framework es no ver cómo |
||
125 | se mantiene la información del proyecto concreto que se está desarrollando. |
||
126 | El lugar adecuado es una de las extensiones instaladas. Por ejemplo en el caso |
||
127 | de gvSIG está la extensión com.iver.cit.gvsig.ProjectExtension, |
||
128 | en la cual hay un atributo Project que es la raíz del árbol jerárquico |
||
129 | del cual penden las vistas, mapas, tablas, ... Además esta extensión |
||
130 | tiene un método getProject que devuelve la referencia al proyecto, de
|
||
131 | manera que se puede acceder desde cualquier punto de la aplicación. Para
|
||
132 | ver el acceso a las instancias de las extensiones ver <a href="#Acceso_a_las_extensiones">acceso |
||
133 | a las extensiones</a></p> |
||
134 | <p><br> |
||
135 | <font size="+1"><strong><a name="Funcionamiento_del_class_loader"></a>Funcionamiento |
||
136 | del class loader</strong></font></p> |
||
137 | <p> En Andami, el class loader de cada plugin delega primero en el classloader
|
||
138 | del sistemaes decir, que si se ejecuta desde eclipse buscará por todos
|
||
139 | los jars que haya en el classpath del proyecto, y si se ejecuta desde la linea |
||
140 | de comandos, buscará en la variable de entorno CLASSPATH o en el argumento
|
||
141 | -classpath que se pase como parámetro a java.<br> |
||
142 | <br>
|
||
143 | Si el class loader del sistema no satisface la búsqueda, se buscará |
||
144 | en los jars del directorio especificado por el config.xml del plugin que intenta |
||
145 | cargar la clase y si no se encuentra en dichos jars, se buscará en los
|
||
146 | jars de los plugins de los cuales depende el plugin que intenta cargar la clase.<br>
|
||
147 | <br>
|
||
148 | <strong><font size="+1"><a name="Servicios_a_los_plugins"></a>Servicios |
||
149 | a los plugins</font></strong></p> |
||
150 | <p><br> |
||
151 | Andami ofrece a los plugins distintos servicios |
||
152 | a través de la clase PluginServices en cuyo javadoc se puede obtener
|
||
153 | información sobre como usarlos. Existen unos servicios genéricos |
||
154 | que vienen dados por métodos estáticos de dicha clase y luego |
||
155 | está el método getPluginServices que obtiene una instancia de |
||
156 | esta clase específica del plugin, mediante la cual puede acceder a servicios
|
||
157 | concretos de cada plugin, traducciones, persistencia, directorio del plugin, |
||
158 | ...<br>
|
||
159 | <br> |
||
160 | <strong><a name="Ejecucion_en_segundo_plano"></a>Ejecución en segundo |
||
161 | plano</strong><br> |
||
162 | Es conveniente que la interfaz gráfica esté |
||
163 | siempre en funcionamiento, nunca bloqueada, aunque sea sólo para mostrar
|
||
164 | al usuario que el programa está procesando. Para ello hay que realizar
|
||
165 | las tareas que puedan tomar demasiado tiempo en un thread a parte. La clase |
||
166 | PluginServices proporciona un método estático backgroundExecution |
||
167 | al cual se le pasa un objeto Runnable, que es ejecutado en segundo plano, dejando |
||
168 | el thread de la interfaz libre para responder pero con sus eventos bloqueados |
||
169 | con el fin de que la interfaz responda y se redibuje, pero se ignoren los eventos |
||
170 | que produce el usuario mientras se procesa la petición<br> |
||
171 | <br>
|
||
172 | <a name="Acceso_a_las_extensiones"></a><strong>Acceso a las extensiones</strong><br> |
||
173 | Para acceder a la instancia de una extensión |
||
174 | se puede usar simplemente el método de PluginServices getExtension(Class)
|
||
175 | a la cual habrá que pasar como parámetro la clase de la extensión |
||
176 | a la que se quiere acceder. Dicho método retorna un objeto Extensión |
||
177 | y por tanto habrá que hacer casting a la clase concreta de dicha extensión, |
||
178 | habiendo obtenido así la referencia a la instancia de la extensión |
||
179 | deseada.<br>
|
||
180 | A la hora de desarrollar habrá que tener en el build |
||
181 | path del entorno de desarrollo que se use, el jar del plugin dentro del cual |
||
182 | está la extensión que se quiere obtener, para poder pasarle como |
||
183 | parámetro a getExtension(Class) la clase de la misma.<br> |
||
184 | <br>
|
||
185 | <strong><a name="Persistencia_de_los_plugins"></a>Persistencia de |
||
186 | los plugins</strong><br> |
||
187 | Uno de los servicios que ofrece Andami a los |
||
188 | plugins es la facilidad de guardar datos genéricos de los mismos en el
|
||
189 | directorio del usuario de manera que cada usuario mantiene su propia configuración
|
||
190 | de los plugins. Para ello, las instancias de PluginServices contienen una propiedad |
||
191 | persistentXML que puede ser obtenida y asignada y que es de tipo XMLEntity, |
||
192 | pudiendo añadir información de tipo básico (String, int, |
||
193 | long, ...) a dicha instancia y siendo esta información persistente entre
|
||
194 | ejecuciones.<br>
|
||
195 | <br>
|
||
196 | <strong><a name="popupmenus"></a>Pop-up menus</strong><br> |
||
197 | Otro servicio proporcionado por Andami es el de pop-up |
||
198 | menu's extensibles. Mediante el XML se puede definir un pop-up menu con un nombre |
||
199 | y unas entradas al igual que cualquier otro menú, con la única |
||
200 | diferencia que para mostrar el popup menú habrá que registrar |
||
201 | un listener de la siguiente manera:<br>
|
||
202 | </p>
|
||
203 | <div style="text-align: center;"> |
||
204 | <pre><>public void addPopupMenuListener(String name, Component c, ActionListener listener)</><br></pre> |
||
205 | </div>
|
||
206 | Lo cual hará que al pinchar con el |
||
207 | botón derecho sobre el componente 'c' aparezca el menú |
||
208 | 'name' del fichero de configuración del plugin y al seleccionar
|
||
209 | cualquier entrada se ejecutará el ActionListener que se pasa
|
||
210 | como parámetro.<br> |
||
211 | <br>
|
||
212 | Estos menúes son extensibles porque cualquier |
||
213 | otro plugin puede añadir entradas a dicho menú en su |
||
214 | propio fichero config.xml referenciando el menú al que quiere
|
||
215 | extender mediante el nombre del plugin más el nombre del
|
||
216 | menú. Un ejemplo. Tenemos un plugin llamado com.iver.cit.gvsig
|
||
217 | que instala un menú de la siguiente manera:<br> |
||
218 | <pre> <popupMenu name="cascada"><br> <entry text="Cascada" <br> tooltip="cascada_tooltip" <br> enableText="cascada_enable" actionCommand="CASCADA"/><br> <entry text="Tile" <br> tooltip="tile_tooltip" <br> enableText="cascada_enable" actionCommand="CASCADA"/><br> </popupMenu><br></pre> |
||
219 | y tenemos otro plugin que quiere añadir una entrada a dicho
|
||
220 | menú. Para ello deberá de incluir un fragmento similar a |
||
221 | este en su fichero config.xml:<br>
|
||
222 | <pre> <popupMenu name="com.iver.cit.gvsig.cascada"><br> <entry text="Nueva entrada" actionCommand="NUEVA"/><br> </popupMenu><br></pre> |
||
223 | y además deberá de registrarse como listener de la manera |
||
224 | que se explicó anteriormente.<br> |
||
225 | <br>
|
||
226 | <strong><a name="Vistas"></a>Vistas</strong><br> |
||
227 | El servicio más importante que |
||
228 | proporciona Andami es el de añadir vistas al marco principal.
|
||
229 | Podemos calsificar las vistas por varios criterios:<br>
|
||
230 | <ul>
|
||
231 | <li>Por su modalidad</li> |
||
232 | <ul>
|
||
233 | <li>Modales</li> |
||
234 | <li>No modales</li> |
||
235 | </ul>
|
||
236 | </ul>
|
||
237 | <ul>
|
||
238 | <li>Por su contenido</li> |
||
239 | <ul>
|
||
240 | <li>Singleton (Contenido identificable)</li> |
||
241 | <li>Normales (Contenido no identificable)</li> |
||
242 | </ul>
|
||
243 | </ul>
|
||
244 | Para crear una vista hay que hacer una clase que |
||
245 | derive de JPanel en la que se pone toda la funcionalidad como si de un |
||
246 | diálogo normal se tratara. Además esta clase ha de |
||
247 | implementar la interfaz View la cual proporciona un método
|
||
248 | getViewInfo que es invocado una vez en la vida de la vista. Este objeto |
||
249 | contiene las características que tendrá la vista: |
||
250 | maximizable, resizable, title, ... y puede ser actualizado en cualquier |
||
251 | momento, reflejándose estos cambios de manera automática |
||
252 | en la interfaz de usuario. Una vez implementada la vista, hay que |
||
253 | añadirla al manger MDI, que es el encargado de manejar toda la
|
||
254 | lógica relacionada con las vistas. Para obtener una referencia
|
||
255 | al manager MDI la clase PluginServices tiene un método
|
||
256 | getMDIManager, el cual a su vez tiene un método addView mediante
|
||
257 | el cual se puede añadir la vista.<br> |
||
258 | <br>
|
||
259 | Si durante la ejecución se quiere cambiar |
||
260 | alguna propiedad de la vista tal como el tamaño, la
|
||
261 | posición o el
|
||
262 | título, sólo hay que acceder al objeto ViewInfo de la |
||
263 | misma y realizar los cambios de la manera deseada. El siguiente trozo |
||
264 | de código cambia el título de una ventana (se supone que |
||
265 | es ejecutado desde la clase que implementa View).<br>
|
||
266 | <pre> PluginServices.getMDIManager().getViewInfo(this).setTitle("Nuevo título");<br></pre> |
||
267 | Puede ser necesario que algunas vistas realicen |
||
268 | algún tipo de procesamiento al ser activadas pero esto no se
|
||
269 | sabe cuando ocurre ya que lo que se entrega al manager MDI es un |
||
270 | JPanel. Para recibir los eventos sobre las vistas, además de
|
||
271 | implementar View hay que implementar ViewListener la cual |
||
272 | proporcionará los métodos que serán invocados |
||
273 | cuando sucedan los eventos oportunos en las vistas.<br>
|
||
274 | <br>
|
||
275 | <big><a name="SingletonView"></a>SingletonView<br> |
||
276 | </big> Un tipo especial de vistas son las |
||
277 | SingletonView. Su principal característica es que se les define
|
||
278 | el contenido de las mismas, de manera que cuando hay una SingletonView |
||
279 | minimizada en el proyecto y se intenta añadir un JPanel con el
|
||
280 | mismo contenido, en lugar de aparecer otra ventana en el escritorio, lo |
||
281 | que sucede es que la ventana que estaba minimizada se restaura a su |
||
282 | posición anterior. Para esto, el método addView devuelve |
||
283 | una referencia a la vista que se muestra, sea esta la que se |
||
284 | está añadiendo o la que ya está añadida. |
||
285 | Además, al cerrar dicha ventana se guardan las dimensiones y la
|
||
286 | posición de las mismas, de manera que al volverla a abrir se
|
||
287 | recuerdan estos datos.<br>
|
||
288 | </body>
|
||
289 | </html> |