Posteado por: albalba | Junio 5, 2008

Ultimo Episodio, El Juego

Nuestro juego es el ahorcado. Decidimos hacer el ahorcado a raíz de la práctica 2, en el que nos pedían que hiciésemos uno según unas características determinadas, ese fue el que entregamos en la primera parte, pero después decidimos hacer un cambio radical. Cuando dimos swing y empezamos a aprender las interfaces gráficas, quisimos que el juego fuese totalmente diferente a lo que era, así que lo modificamos muchas veces, quedando como resultado esto:

PANTALLA INICIAL

Al ejecutar el juego, se inicia una pantalla con la imagen de Simpson.jpg (un ImageIcon). Sobre la imagen hay 4 botones JButton:

- Créditos, se muestran los créditos del juego. En el escuchador del FrameAhorcado hay un

If( evento= =”CREDITOS”)

{

(Llamamos a la clase que se encarga de eso)

}

Para que en el FrameAhorcado salga otra ventanita aparte,no hemos utilizado un Frame, sino que hemos creado unas clases internas que heredan de JDialog. El JDialog puede recibir como argumento un JFrame, entonces, en las clases de Instrucciones y Créditos, tenemos un método que se encarga de devolvernos un JFrame con un JPanel ya relleno con los elementos que queremos mostrar. El constructor de la clase Creditos, se encarga de leer desde un fichero externo el texto que presentaremos como créditos. Para salir tenemos que dar al botón Aceptar o pinchar el aspa.

- Instrucciones, la explicación de cómo jugar. Escribimos en un fichero las instrucciones que más tarde queremos que se vean cuando pulsemos este botón mediante la clase Instrucciones. Funciona igual que la clase anterior pero con diferente dimensión y texto. Para salir hay que pinchar en Aceptar o en el aspa.

- Opciones, aparece una ventana pequeña la cual hemos impedido que se pueda maximizar (con setResizable()) para evitar que se pierda el orden y la buena apariencia.También hemos bloqueado la opción de cerrar del aspa con frame.setDefaultCloseOperation(javax.swing.WindowConstants.DO_NOTHIN G_ON_CLOSE); así, la única manera de cerrar esta ventana, es pulsar el botón

Aceptar. Lo hemos hecho así para confirmar las opciones de configuración, de lo contrario, nos quedaríamos con la configuración por defecto y no con la que el usuario desea.

En la ventana se muestra los elementos en el siguiente orden:

· Una ventana desplegable, JComboBox, con diferentes temas a elegir para la palabra secreta. Los temas son: arte, ciencia, deportes, música, personajes, tecnología, programación y otros. De estos se puede marcar uno. Al abrir la ventana, por defecto se marca el tema de arte en la ventana desplegable.

· Se puede elegir la dificultad entre fácil, normal, difícil y muy difícil. Se elige gracias a 4 JRadioButton mostrando cuatro pequeños círculos, en los que si pinchas se rellenan con un punto negro. Sólo se puede elegir uno de los 4, por esta razón elegimos JRadioButton.

· Un botón Aceptar, JButton, para aceptar las opciones establecidas por el jugador.

- Iniciar Juego, si es pulsado inicia el juego. Al pulsarlo aparece una ventana que se compone de varias partes.

à PANTALLA DEL JUEGO

Estas partes están colocadas en columna obedeciendo el orden establecido por un GridLayout:

- Lo siguiente se logra gracias a la clase PanelBorder, el cual coloca un borde en un panel. En letras rojas colocamos JUGUEMOS!! poniendo un nombre al borde del panel. Mientras que el borde no se aprecia porque está en negro y no se distingue con el fondo.

- Botón denominado Menú principal, con el que puedes volver a la pantalla inicial. En el momento que el jugador esté situado en dicha ventana, se puede realizar todas las operaciones que ésta ofrece.

- La imagen del muñeco ahorcado es un ImageIcon. El juego está compuesto de 7 imágenes del ahorcado del clásico juego, y una carita feliz en el caso de haber ganado la partida. La primera vez que aparece, cuando empieza la partida, tan solo sale la soga. Si se pulsa una letra errónea, que no se encuentra en la palabra secreta, sale también la cabeza, después la cabeza y el cuerpo,… y así sucesivamente hasta completar todo su cuerpo, momento en el que se habrá perdido la partida.

- Un JLabel en el que se encuentra la palabra a adivinar oculta entre los asteriscos. Los asteriscos son de color blanco para que resalten sobre el fondo negro.

- Un JButton en amarillo, al pulsarlo se invoca a un objeto de la clase CompletaPalabra, apareciendo una ventana donde puedes introducir la palabra que se cree que es la correcta. Esta ventana sólo se cierra en el momento que se pulsa el enter. En el botón se puede ver escrito ¿sabes la solución? En la pantalla de juego, cuando se pulsa cualquier botón, esta pantalla se coloca sobre la de la palabra secreta, sólo está debajo cuando se inicia una nueva partida. De momento, está opción está deshabilitada.

- Un borde blanco que rodea el teclado en el que aparece escrito TECLADO en letras blancas. Esto se consigue gracias al PanelBorder.

- Y un array con 30 JButton, con las 27 letras del abecedario en color rojo, un botón para iniciar una nueva partida, otro para resolver la palabra y otro para salir del juego, éstos tres últimos en color negro. Hemos utilizado el constructor del JButton que permite poner un nombre y un icono al botón. Para que las letras se quedasen centradas, se empleó el método de JButton setHorizontalAlligment(int posicion);

à INTENTOS

En cada intento puedes pulsar una tecla correspondiente a una letra.

- Si la letra se encuentra en la palabra, la letra se descubre en la palabra de asteriscos en su correspondiente posición, actualizando el JLabel que la contiene.

- Si la letra no está en la palabra secreta, pierdes una oportunidad, y la imagen del ahorcado se actualiza, colocándose un miembro más del cuerpo del pobre muñeco.

- Si pulsas una letra ya pulsada anteriormente, no hace nada.

Pulsando el botón Nueva Partida, comienzas una nueva partida del ahorcado directamente preparado para jugar, sin aparecer en la pantalla de bienvenida.

Con el botón Resolver, se desvela la palabra secreta hayamos perdido, ganado o nos encontremos en medio de la partida, pero en el momento en que la pulses, ya no podrás seguir jugando con esa palabra, el jugador estará obligado a pulsar Nueva Partida si quiere seguir jugando.

Con el botón Salir, se sale de la aplicación cerrándose la ventana del ahorcado.

à PERDER Y GANAR

Se pierde si en los 6 intentos no has conseguido resolver la palabra.

De forma contraria, se gana si consigues resolver la palabra antes de que el muñeco aparezca totalmente ahorcado, es decir, antes de que se acaben los 6 intentos. Entonces, en el lugar en el que está la imagen del ahorcado, se verá una carita contenta en señal de felicitación.

Se puede jugar tantas partidas como se quiera pulsando el botón correspondiente a Nueva Partida.

à IMPLEMENTACIÓN DEL JUEGO. CLASES.

El juego se compone de las siguientes clases:

- Compara, implementa el algoritmo del ahorcado. En él situamos los arrays de String para cada tema con las posibles palabras a jugar, busca una palabra aleatoria, se escoge la palabra según la dificultad elegida (la dificultad se escoge acorde al número de letras de la palabra), oculta la palabra entre asteriscos,…

- FrameAhorcado, es la clase principal, la que engloba todas las clases. Se encarga de la interfaz gráfica de usuario. Contiene 4 clases internas: FrameConfigura, FrameCreditos, FrameInstrucciones y Configuracion. Las tres primeras llaman al método getFrame() con un objeto correspondiente a la clase Configura, Creditos e Instrucciones, respectivamente. Los 3 heredan de JDialog. Mientras que la clase interna Configuración, contiene a su vez otra clase interna llamada EscuchaBoton.

Se colocaron clases internas con el fin de reutilizar el código y buscando lo más cómodo y fácil para programar. Si no hubiésemos hecho clases internas, sería más complicado de programar. Al estar interna, podemos acceder directamente a los atributos y métodos de la clase que la contiene. En cambio, desde una clase externa, no se puede acceder a los atributos de otra clase de una forma tan sencilla, ya que siguiendo la encapsulación, están con el modificador de acceso private.

- Instrucciones, lee desde un fichero externo (llamado instrucciones.txt), las instrucciones que aparecen al pulsar dicho botón de la pantalla inicial.

- Configuración, se encarga de sacar las opciones de la pantalla de bienvenida, puedes elegir la dificultad y el tema del que tratará la palabra a adivinar.

- ComparaPalabra, hace que aparezca una ventana para resolver la palabra oculta. Si la palabra es correcta, se gana automáticamente. De lo contrario, se pierde. En estos momentos se encuentra deshabilitado.

- Créditos, la clase créditos es para mostrar lo que está escrito en el fichero créditos.txt. Al igual que la clase Instrucciones carga lo que vamos a mostrar por pantalla desde un fichero externo.

- PanelBorder, hereda de TitledBorder. En esta clase hemos construido 6 constructores diferentes para formar bordes según lo deseado en cada momento.

1. Constructor en el que se introduce el color del borde y la palabra que aparecerá en dicho borde.

public PanelBorder(Color color,String nombre)
{
super(new LineBorder(color),nombre);
}

2. Constructor al que se le pasa el color del borde, la anchura del borde y la palabra en él escrita.

public PanelBorder(Color color,int ancho,String nombre)
{
super(new LineBorder(color, ancho),nombre);
}

3. Constructor al que se le pasa el color del borde, el color de la letra de la palabra que se va a escribir en el borde y la palabra que desees colocar.

public PanelBorder(Color color,Color letra,String nombre )
{
super(new LineBorder(color),nombre);
super.setTitleColor(letra);
}

4. Constructor al que se le añade a los anteriores el ancho del borde.

public PanelBorder(Color color,Color letra,int ancho,String nombre)
{
super(new LineBorder(color, ancho),nombre);
super.setTitleColor(letra);

}

5. Constructor al que se le pasa lo del anterior pero en lugar de meter el color de la letra, pasamos la posición.

public PanelBorder(Color color,int ancho,int posicion,String nombre)
{
super(new LineBorder(color, ancho),nombre);
super.setTitleJustification(posicion);
}

6. Y el último al que se le pasan todos los parámetros anteriores.

public PanelBorder(Color color,Color letra,int ancho,int posicion,String nombre)
{
super(new LineBorder(color,ancho),nombre);
super.setTitleColor(letra);
super.setTitleJustification(posicion);
}

à RELACIÓN ENTRE CLASES.

Ver en página anexa por favor.

à VENTANA DE COMANDOS.

Para compilar el juego las imágenes (Homer y Bart, los distintas fases del ahorcado, las diferentes imágenes para los botones y la carita contenta) y los códigos fuente deben estar en la misma carpeta. Así, una vez que se haya colocado en la ventana de comandos la ruta en la que se encuentra dicha carpeta, escribir la sentencia javac *.java para compilar todos los archivos de una sola vez. Entonces, se formarán los .class de cada una de las clases. Una vez hecho esto, se ejecuta mediante java FrameAhorcado. Después, aparecerá la ventana principal del juego en el lado izquierdo de la pantalla del ordenador, y todo preparado para comenzar la partida.

à PROBLEMAS.

- Tuvimos muchos problemas sobre todo al comenzar desde cero.

- Los Escuchadores.

- Con el botón de Nueva partida: A la hora de activar este botón fue bastante complicado. Al principio cada vez que se le pulsaba, aparecía una partida nueva pero en un nuevo frame, de forma que cada vez que le pulsases se iban acumulando las ventanas una encima de otra. Esto no era lo que nosotros queríamos. Para llegar a que saliese en la misma ventana que en la que empezamos la primera partida, tuvimos que crear varios métodos, el NuevaPartida(), Panel(), Limpia(), …

- Para actualizar los paneles.

- Enganchar la GUI con el algoritmo del juego.

- Lectura externa desde un fichero.

à MEJORAS.

Siempre se puede mejorar. De hecho, podríamos estar años con él y años dedicaríamos a colocar mejoras, solventar errores,… Entonces, es imposible evitar que surja algún problema en algún momento de vida del juego, aunque nosotros hemos intentado controlarlos y dejarlo lo mejor posible.

Ideas que han ido surgiendo para innovar el juego a raíz de enseñárselo a gente y de jugar nosotros fueron:

  • Pistas, dar pistas sobre la palabra a adivinar.
  • Temas, colocar más temas a elegir.
  • Más oportunidades para acertar la palabra.

à CONCLUSIÓN.

En un principio no íbamos a hacer el juego, porque necesita emplear muchísimo tiempo y queremos aprobar el mayor número posible de asignaturas, pero cambiamos de opinión con la práctica 2, y nos decidimos a ello.

Programar el juego nos ha servido, más que nada, a aprender a utilizar SWING y a surfear por el API. A cualquier duda, mirábamos en el API. Fue un gran amigo nuestro. El mayor problema era el idioma, pero entre los dos y el diccionario como nuestro aliado, después de largos ratos, se podían sacar cosas en claro.

Hemos aprendido mucho sobre SWING.

Para mostrar como ha ido evolucionando nuestro juego y su código, y nosotros con él, hemos hecho “un viaje” en el tiempo. En el anexo2 se puede apreciar los pasos que hemos dado, desde nuestro aprendizaje a la actualidad.

Descargar Código y Documentación

Descargar ejecutable ( Ahorcado.jar)

PD: Para ejecutar el juego se necesita que este instalado JVM en tu ordenador. Si no lo tienes lo puedes descargar de aqui.

Creative Commons License

Posteado por: albalba | Mayo 30, 2008

EL SIMULACRO DE EXAMEN

Tchan, tchan, tchan, tchaaaaaaaaaaan!!

El simulacro no me ha ido muy bien. Yo necesito más tiempo para pensar las cosas. Además, mi visión espacial es nula y para imaginarme a la piececita dando vueltas, me ponía malaaaaaaaaaa…  ahora comprendo a los compañeros que están cursando de nuevo esta asignatura, cuando me decían que el juego del examen es imposible. Imposible no es, pero si creo que requiere de más tiempo para procesarlo, pensarlo, y escribirlo. También es bastante complicado el juego del examen. Para cuando quise empezar el segundo ejercicio SE ME HABÍA ACABADO EL TIEMPO. Lo desperdicié todo pensando en el dichoso tetris. Con lo que me gustaba a mi ese juego, hasta ahora que le he pillao’ “tirria”.

A Rodri le ha ido mejor, él tiene más visión espacial.

Yo me tiro todo el santo día con Java, es la asignatura a la que más hago caso, la que más empollo. Hay que tener en cuenta que para el simulacro de examen, nadie iba con todo aprendido a la perfección, pero con lo que he estudiado …. No era fallo de falta de estudio, de no saber sobre el tema, en mi opinión lo que a mi me pasó era falta de ideas, de comprender lo que piden y quedarte en ese bucle de: ”¿ésto se hará así? No, así no puede ser ¿entonces “asao”? ”, perdiendo todo el tiempo del mundo tan solo en pensar.

Ambos teníamos esperanzas de aprobar el examen de junio, pero con este simulacro nos hemos quedado por los suelos. Como dijo la delegada en clase cuando expuso su juego, esta es una asignatura que no se aprueba a la primera.

De la segunda parte, para que hablar. Por lo visto, dijeron los compañeros que en la solución que había por allí rondando, aparecía implementado con un vector. Pufff…vectores…

En clase nos preguntaron si volveríamos a hacer el simulacro de examen o no. En parte si y en parte no. En parte si, porque te enfrentas a lo que va a ser el examen para hacerte una idea. Pero por otra parte no porque salimos todos de allí muy desanimados. Los unos a los otros nos decíamos que si después de estudiar tanto no aprobábamos, una de dos, o nos pegábamos un tiro, nos íbamos a ADE o a un grado superior. Tanto currar para que después los de grado superior cobren más que nosotros y los prefieran. ¡¡¡¡QUÉ DESÁNIMO POR DIOS!!!!

Rezaremos para que una luz nos ilumine en medio de un aula oscura para poder aprobar…

Hasta pronto. Y suerte a todos.

 

Posteado por: albalba | Mayo 28, 2008

Práctica 12

Bienvenidos señoras y señores lectores a la …ÚLTIMA PRÁCTICA DE OCA!!! BIEEEEEEEEEEEEEEEN!!! Ahora tan solo… queda…empollarla….TAN SOLOOOOOOOOOOOOOO!!!

Pues bien, como anécdota comentar que tuvimos que empezar la clase a oscuras jaja, ya que se fue la luz en las aulas y el pasillo pero no en los ordenadores. Al poco tiempo, nuestras plegarias fueron escuchadas XD, Y SE HIZO LA LUZ. Momento en el que, aunque hubiese luz, llegó el infierno.

JMF nos resultó más raro que complicado. Resulta que es otra librería que hay que instalar aparte, e incluso tiene API PROPIO!!!

Lo primero que hicimos fue cotillear el API nuevo, muy parecido al de siempre, pero en el fondo, muy diferente.

Tras esto, para poder utilizarlo, hay que configurarlo en la máquina. Para ello se podía lograr de tres maneras.

Con javac –classpath…. se le dice a la máquina que use este nuevo classpath y se olvide del otro, pero el precio que pagamos es que esta sentencia, sólo vale para un uso del compilador.

En cambio, con la sentencia set CLASSPATH=”.;/ruta completa/jmf.jar;/ruta completa/sound.jar; la ventana se queda configurada mientras se encuentre abierta. En el momento que la cierres y la vuelvas a abrir deberás colocar la sentencia completa de nuevo.

La forma de dejarlo configurado ya para “jamás de los jamases”, es configurarlo en panel de control, bla bla bla, variables de entorno, variables de sistema, como cuando se instala el classpath de java desde cero. Ay (suspiro), aquella época en el que nuestro mayor problema era configurar el java en el ordena de nuestra casa, ay! Bueno, que como ya existe el classpath, le añadimos las nuevas rutas del JMF y santas pascuas.

El objetivo de la práctica, era crear mediante las herramientas que nos ofrece Java Media Framework, un VDCMediaPlayer, vamos, un programa que tenga botones de play, stop,… para el video, y un visualizador.

Para todo esto, hay que utilizar una ventana JFrame de Swing, extraer su contentPane y añadir sus componentes visuales mediante add. Es más fácil leerlo que hacerlo. En el API encontramos lo siguiente sobre él:

 

javax.media
Interface Player

All Known Subinterfaces:

Processor

All Known Implementing Classes:

MediaPlayer



public interface Player

extends MediaHandler, Controller

Player is a MediaHandler for rendering and controlling time based media data. Player extends the Controller interface. Player provides methods for obtaining AWT components, media processing controls, and a way to manage other Controllers.

How a Player Differs from a Controller

Player relaxes some restrictions that a Controller imposes on what methods can be called on a Started Controller. It also provides a way to manage groups of Controllers.

Methods Restricted to Stopped Players

The following methods cannot be invoked on a Started Player. If they are, ClockStartedError is thrown.

  • setTimeBase
  • syncStart
  • deallocate
  • addController
  • removeController

Methods Allowed on Started Players

Unlike a Controller, the following methods are legal on a Player in the Started state:

  • setMediaTime
  • setRate

Invoking these methods on a Started Player might initiate significant and time-consuming processing, depending on the location and type of media being processed. These methods might also cause the state of the Player to change. If this happens, the appropriate TransitionEvents are posted by the Player when its state changes.

For example, a Player might have to enter the Prefetching state to process a setMediaTime invocation. In this case, the Player posts a RestartingEvent, a PrefetchCompleteEvent, and a StartEvent as it moves from the Started state to Prefetching, back to Prefetched, and finally back to the Started state.

Methods that are Illegal on Unrealized Players

As with Controller, it is illegal to call the following methods on an Unrealized Player:

  • getTimeBase
  • setTimeBase
  • setMediaTime
  • setRate
  • setStopTime
  • getStartLatency

It is also illegal to call the following Player methods on an Unrealized Player:

  • getVisualComponent
  • getControlPanelComponent
  • getGainControl
  • addController
  • removeController

The Player throws a NotRealizedError if any of these methods are called while the Player is in the Unrealized state.

Start Method

As a convenience, Player provides a start method that can be invoked before a Player is Prefetched. This method attempts to transition the Player to the Started state from whatever state it’s currently in. For example, if the Player is Unrealized, start implicitly calls realize, prefetch, and Clock.syncStart. The appropriate TransitionEvents are posted as the Player moves through each state on its way to Started.

RestartingEvent

If setMediaTime or setRate cause a perceptible delay in the presentation of the media, the Player posts a RestartingEvent and transitions to the Prefetching state. The previous state and target state of a RestartingEvent is always Started. RestartingEvent is a subclass of StopEvent.

DurationUpdateEvent

Because a Player cannot always know the duration of the media it is playing, the Duration interface defines that getDuration returns Duration.DURATION_UNKNOWN until the duration can be determined. A DurationUpdateEvent is generated when the Player can determine its duration or the if its duration changes, which can happen at any time. When the end of the media is reached, the duration should be known.

Managing other Controllers

In some situations, an application might want to use a single Player to control other Players or Controllers. A single controlling Player can be used to invoke start, stop, setMediaTime, and other methods on the entire group. The controlling Player manages all of the state transitions and event posting.

It is also possible to construct a simple Controller to update animations, report on media time-line progress, or provide other timing-related functions. Such Controllers can operate in sync with a controlling Player.

Adding a Controller

To have a Player assume control over a Controller, use the addController method. A Controller can not be added to a Started  Player. If addController is called on a Started Player, a ClockStartedError is thrown. An Unrealized  or Started;Controller cannot be added to a Player; a NotRealizedError is thrown if the Controller is Unrealized; a ClockStartedError is thrown if the Controller is Started.

Once a Controller has been added, the Player:

  • Invokes setTimeBase on the Controller with the Player's TimeBase. If this fails, addController throws an IncompatibleTimeBaseException.
  • Synchronizes the Controller with the Player using setMediaTime, setStopTime, and setRate.
  • Takes the added Controller's latency into account when computing the Player's start latency. When getStartLatency is called, the Player returns the greater of: its latency before the Controller was added and the latency of the added Controller.
  • Takes the added Controller's duration into account when computing the Player's duration. When getDuration is called, the Player returns the greater of: its duration before the Controller was added and the duration of the added Controller. If either of these values is DURATION_UNKNOWN, getDuration returns DURATION_UNKNOWN. If either of these values is DURATION_UNBOUNDED getDuration returns DURATION_UNBOUNDED.
  • Adds itself as a ControllerListener for the added Controller so that it can manage the events that the Controller generates. (See the Events section below for more information.)
  • Invokes control methods on the added Controller in response to methods invoked on the Player. The methods that affect managed Controllers are discussed below.

Once a Controller has been added to a Player, methods should only be called on the Controller through the managing Player. It is not defined how the Controller or Player will behave if methods are called directly on an added Controller. You cannot place a controlling Player under the control of a Player that it is managing; the resulting behavior is undefined.

When a Controller is added to a Player, the Player does not transition the added Controller to new state, nor does the Player transition itself forward. The Player either transitions back to the realized state if the added Controller is realized or prefetching or it stays in the prefetched state if the both the Player and the added Controller are in the prefetched state. If the Player makes a state transition as a result of adding a Controller the Player posts a TransitionEvent.

Removing a Controller

To stop a Player from managing another Controller, call removeController. The managing Player must be Stopped before removeController can be called. A ClockStartedError is thrown if removeController is called on a Started Player.

When a Controller is removed from a Player's control, the Player:

  • Resets the Controller's TimeBase to its default.
  • Recalculates its duration and posts a DurationUpdateEvent if the Player's duration is different without the Controller added.
  • Recalculates its start latency.

Setting the Media Time and Rate of a Managing Player

When you call setMediaTime on a Player that’s managing other Controllers, its actions differ depending on whether or not the Player is Started. If the Player is not Started, it simply invokes setMediaTime on all of the Controllers it’s managing.

If the Player is Started, it posts a RestartingEvent and performs the following tasks for each managed Controller:

  • Invokes stop on the Controller.
  • Invokes setMediaTime on the Controller.
  • Invokes prefetch on the Controller.
  • Waits for a PrefetchCompleteEvent from the Controller.
  • Invokes syncStart on the Controller

The same is true when setRate is called on a managing Player. The Player attempts to set the specified rate on all managed Controllers, stopping and restarting the Controllers if necessary. If some of the Controllers do not support the requested rate, the Player returns the rate that was actually set. All Controllers are guaranteed to have been successfully set to the rate returned.

Starting a Managing Player

When you call start on a managing Player, all of the Controllers managed by the Player are transitioned to the Prefetched state. When the Controllers are Prefetched, the managing Player calls syncStart with a time consistent with the latencies of each of the managed Controllers.

Calling realize, prefetch, stop, or deallocate on a Managing Player

When you call realize, prefetch, stop, or deallocate on a managing Player, the Player calls that method on all of the Controllers that it is managing. The Player moves from one state to the next when all of its Controllers have reached that state. For example, a Player in the Prefetching state does not transition into the Prefetched state until all of its managed Controllers are Prefetched. The Player posts TransitionEvents normally as it changes state.

Calling syncStart or setStopTime on a Managing Player

When you call syncStart or setStopTime on a managing Player, the Player calls that method on all of the Controllers that it is managing. (The Player must be in the correct state or an error is thrown. For example, the Player must be Prefetched before you can call syncStart.)

Setting the Time Base of a Managing Player

When setTimeBase is called on a managing Player, the Player calls setTimeBase on all of the Controllers it’s managing. If setTimeBase fails on any of the Controllers, an IncompatibleTimeBaseException is thrown and the TimeBase last used is restored for all of the Controllers.

Getting the Duration of a Managing Player

Calling getDuration on a managing Player returns the maximum duration of all of the added Controllers and the managing Player. If the Player or any Controller has not resolved its duration, getDuration returns Duration.DURATION_UNKNOWN.

Closing a Managing Player

When close is called on a managing Player all managed Controllers are closed as well.

Events

Most events posted by a managed Controller are filtered by the managing Player. Certain events are sent directly from the Controller through the Player and to the listeners registered with the Player.

To handle the events that a managed Controller can generate, the Player registers a listener with the Controller when it is added. Other listeners that are registered with the Controller must be careful not to invoke methods on the Controller while it is being managed by the Player. Calling a control method on a managed Controller directly will produce unpredictable results.

When a Controller is removed from the Player's list of managed Controllers, the Player removes itself from the Controller's listener list.

Transition Events

A managing Player posts TransitionEvents normally as it moves between states, but the managed Controllers affect when the Player changes state. In general, a Player does not post a transition event until all of its managed Controllers have posted the event.

Status Change Events

The managing Player collects the RateChangeEvents, StopTimeChangeEvents, and MediaTimeSetEvents posted by its managed Controllers and posts a single event for the group.

DurationUpdateEvent

A Player posts a DurationUpdateEvent when it determines its duration or its duration changes. A managing Player's duration might change if a managed Controller updates or discovers its duration. In general, if a managed Controller posts a DurationUpdateEvent and the new duration changes the managing Player's duration, the Player posts a DurationUpdateEvent

CachingControlEvent

A managing Player reposts CachingControlEvents received from a Players that it manages, but otherwise ignores the events.

ControllerErrorEvents

A managing Player immediately reposts any ControllerErrorEvent received from a Controller that it is managing. After a ControllerErrorEvent has been received from a managed Controller, a managing Player no longer invokes any methods on the managed Controller; the managed Controller is ignored from that point on.

See Also:

Manager, GainControl, Clock, TransitionEvent, RestartingEvent, DurationUpdateEvent, Component



Fields inherited from interface javax.media.Controller

LATENCY_UNKNOWN, Prefetched, Prefetching, Realized, Realizing, Started, Unrealized

 

Fields inherited from interface javax.media.Clock

RESET

 

Fields inherited from interface javax.media.Duration

DURATION_UNBOUNDED, DURATION_UNKNOWN

 

Method Summary

 void

addController(Controller newController)
          Tells the
Player to assume control of another Controller.

 java.awt.Component

getControlPanelComponent()
          Gets the
Component that provides the default user interface for controlling this Player.

 GainControl

getGainControl()
          Gets the object for controlling this
Player's audio gain.

 java.awt.Component

getVisualComponent()
          Gets the display
Component for this Player.

 void

removeController(Controller oldController)
          Tells the
Player to stop controlling a Controller.

 void

start()
          Starts the
Player as soon as possible.

 

Methods inherited from interface javax.media.MediaHandler

setSource

 

Methods inherited from interface javax.media.Controller

addControllerListener, close, deallocate, getControl, getControls, getStartLatency, getState, getTargetState, prefetch, realize, removeControllerListener

 

Methods inherited from interface javax.media.Clock

getMediaNanoseconds, getMediaTime, getRate, getStopTime, getSyncTime, getTimeBase, mapToTimeBase, setMediaTime, setRate, setStopTime, setTimeBase, stop, syncStart

 

Methods inherited from interface javax.media.Duration

getDuration

 

 

 

 

Teníamos constancia, por la clase teórica de Rueda, de que existía la interfaz Player, que contenía los métodos getVisualComponent() y getControlPaneComponent(), los cuales utilizaremos para esta práctica:

 

getControlPanelComponent

public java.awt.Component getControlPanelComponent()

Gets the Component that provides the default user interface for controlling this Player. If this Player has no default control panel, getControlPanelComponent returns null.

Returns:

The default control panel GUI for this Player.

 

 

 

getVisualComponent

public java.awt.Component getVisualComponent()

Gets the display Component for this Player. The display Component is where visual media is rendered. If this Player has no visual component, getVisualComponent returns null. For example, getVisualComponent might return null if the Player only plays audio.

Returns:

The media display Component for this Player.

 

 

 

Para hacernos la vida más fácil también está la clase Manager. Clase que define métodos static y la puedo usar para apoyar el programa.

 

javax.media
Class Manager

java.lang.Object
  |
  +--javax.media.Manager


public final class Manager

extends java.lang.Object

Manager is the access point for obtaining system dependent resources such as Players, DataSources, Processors, DataSinks, the system TimeBase, the cloneable and merging utility DataSources.

A DataSource is an object used to deliver time-based multimedia data that is specific to a delivery protocol.

A Player is an object used to control and render multimedia data that is specific to the content type of the data.

A Processor is an object similar to a Player which is used to process data and output the processed data.

A DataSink is an object that takes a DataSource as input and renders the output to a specified destination.

A DataSource provides a Player, Processor or DataSink with media data; a Player, Processor or DataSink must have a DataSource. Manager provides access to a protocol and media independent mechanism for constructing DataSources, Players, Processors and DataSinks.

Creating Players and DataSources

Manager will createPlayers from a URL, a MediaLocator or a DataSource. Creating a Player requires the following:

  • Obtain the connected DataSource for the specified protocol
  • Obtain the Player for the content-type specified by the DataSource
  • Attach the DataSource to the Player using the setSource method.

Finding DataSources by Protocol

A MediaLocator defines a protocol for obtaining content. DataSources are identified by the protocol that they support. Manager uses the protocol name to find DataSource classes.

To find a DataSource using a MediaLocator, Manager constructs a list of class names from the protocol package-prefix list and the protocol name obtained from the MediaLocator. For each class name in the constructed list a new DataSource is instantiated, the MediaLocator is attached, and the DataSource is connected. If no errors have occurred, the procces is considered finished and the connected DataSource is used by Manager in any following operations. If there was an error then the next class name in the list is tried. The exact details of the search algorithm is described in the method documentation below.

Finding Players by Content Type

A Player is a MediaHandler. A MediaHandler is a an object that reads data from a DataSource. There are three types of supported MediaHandler: MediaProxy, Player and Processor.

MediaHandlers are identified by the content type that they support. A DataSource identifies the content type of the data it produces with the getContentType method. Manager uses the content type name to find instances of MediaHandler.

To find a MediaHandler using a content type name, Manager constructs a list of class names from the content package-prefix list and the content type name. For each class name in the constructed list a new MediaHandler is instantiated, and the DataSource is attached to the MediaHandler using MediaHandler.setSource.

If the MediaHandler is a Player and the setSource was successful the process is finished and the Player is returned. If the setSource failed, another name in the list is tried.

If the MediaHandler is a MediaProxy then a new DataSource is obtained from the MediaProxy, a new list is created for the content type the DataSource supports and the whole thing is tried again.

If a valid Player is not found then the whole procedure is repeated with “unknown” substituted for the content-type name. The “unknown” content type is supported by generic Players that are capable of handling a large variety of media types, often in a platform dependent way.

The detailed creation algorithm is specified in the methods below.

Creating a Realized Player

Versions of createRealizedPlayer calls are provided as an acceleration to create a Player. The returned player is in the Realized state. In addition to NoPlayerException and IOException, CannotRealizeException can be thrown if the Manager cannot realize the Player.

Creating Processors

Processors are created in the same way as Players as outlined above. Manager also provides an additional way to create a Processor via the createRealizedProcessor call. A ProcessorModel is used to fully identify the input and output requirements of a Processor. The createRealizedProcessor call takes a ProcessorModel as input and create a Processor that adheres to the given ProcessorModel. The returned Processor is in the Realized state. The method is a blocking call.

If the Manager fails to find a Processor that fits the ProcessorModel, a NoProcessorException is thrown. If there is a problem creating and realizing a Processor, it will throw an IOException or CannotRealizeException depending on the circumstances.

Creating DataSinks

DataSinks are created from an input DataSource MediaLocator. The MediaLocator identifies the protocol and content of the DataSink to be used. The search for the particular DataSink class to be created is similar to the process of creating a DataSource. The detail search and creation algorithm is described in the method documentation below.

Player and Processor Threads

Players and Processors process media data asynchronously from the main program flow. This implies that a Player or Processor must often manage one or more threads. The threads managed by the Player or Processor are not in the thread group of the application that calls createPlayer or createProcessor.

System Time Base

All Players need a TimeBase. Many use a system-wide TimeBase, often based on a time-of-day clock. Manager provides access to the system TimeBase through getSystemTimeBase.

Cloning and Merging DataSources

DataSources can be cloned or merged. If a DataSource is cloned, more than one MediaHandler can use it as input. Merging more than one DataSources will generate one DataSource which contains all the SourceStreams of the constituent DataSources

The Manager provides two methods: createCloneableDataSource and createMergingDataSource for such purpose.

Manager Hints

Using the setHint method, the preference for how the Manager creates the objects can be specified. However, a particular implementation of the Manager can choose to ignore the requested hints.

Since:

1.0 , new methods added in 2.0

See Also:

MAX_SECURITY, CACHING, LIGHTWEIGHT_RENDERER,

, URL, MediaLocator, PackageManager, DataSource, URLDataSource, MediaHandler, Player, Processor, MediaProxy, TimeBase



Field Summary

static int

CACHING
          Boolean hint to turn on/off the use of playback caching.

static int

LIGHTWEIGHT_RENDERER
          Boolean hint to turn on/off the use of light weight rendering.

static int

MAX_SECURITY
          Boolean hint to turn on/off maximum security.

static int

PLUGIN_PLAYER
          Boolean hint to request the
Manager to create Players that support PlugIns.

static java.lang.String

UNKNOWN_CONTENT_NAME
           

 

Method Summary

static DataSource

createCloneableDataSource(DataSource source)
          Creates a cloneable
DataSource.

static DataSink

createDataSink(DataSource datasource, MediaLocator destLocator)
          Create a
DataSink for the specified input Datasource and destination Medialocator.

static DataSource

createDataSource(MediaLocator sourceLocator)
          Create a
DataSource for the specified media.

static DataSource

createDataSource(java.net.URL sourceURL)
          Create a
DataSource for the specified media.

static DataSource

createMergingDataSource(DataSource[] sources)
          Creates a merged
DataSource from an array of sources.

static Player

createPlayer(DataSource source)
          Create a
Player for the DataSource.

static Player

createPlayer(MediaLocator sourceLocator)
          Create a
Player for the specified media.

static Player

createPlayer(java.net.URL sourceURL)
          Create a
Player for the specified media.

static Processor

createProcessor(DataSource source)
          Create a
Processor for the DataSource.

static Processor

createProcessor(MediaLocator sourceLocator)
          Create a
Processor for the specified media.

static Processor

createProcessor(java.net.URL sourceURL)
          Create a
Processor for the specified media.

static Player

createRealizedPlayer(DataSource source)
          Create a Realized
Player for the specified source.

static Player

createRealizedPlayer(MediaLocator ml)
          Create a Realized
Player for the specified media.

static Player

createRealizedPlayer(java.net.URL sourceURL)
          Create a Realized
Player for the specified media.

static Processor

createRealizedProcessor(ProcessorModel model)
          Create a Realized
Processor for the specified ProcessorModel.

static java.lang.String

getCacheDirectory()
          Retrieve the directory that’s used for playback caching.

static java.util.Vector

getDataSourceList(java.lang.String protocolName)
          Build a list of
DataSource class names from the protocol prefix-list and a protocol name.

static java.util.Vector

getHandlerClassList(java.lang.String contentName)
          Build a list of Player
Handler classes from the content-prefix-list and a content name.

static java.lang.Object

getHint(int hint)
          Retrieve the value of a hint set.

static java.util.Vector

getProcessorClassList(java.lang.String contentName)
          Build a list of Processor
Handler classes from the content-prefix-list and a content name.

static TimeBase

getSystemTimeBase()
          Get the time-base object for the system.

static java.lang.String

getVersion()
          Returns the version string for this revision of JMF.

static void

setHint(int hint, java.lang.Object value)
          Specify a hint for the
Manager to use.

 

Methods inherited from class java.lang.Object

clone, equals, finalize, getClass, hashCode, notify, notifyAll, toString, wait, wait, wait

 

 

 

 

Como no se puede hacer Player p = new Player(…); ¡¡ERROR!! ya que Player es una interfaz, por lo que nos ayudamos de la clase Manager para crear un objeto Player. Un método que contiene Manager es el createPlayer, al que se le puede pasar por parámetro un DataSource, un MediaLocator o una URL (un fichero local se puede escribir como URL), porque necesita un reproductor de algo.

 

createPlayer

public static Player createPlayer(MediaLocator sourceLocator)
                           throws java.io.IOException,
                                  NoPlayerException

Create a Player for the specified media.

The algorithm for creating a Player from a MediaLocator is:

1.      Get the protocol from the MediaLocator.

2.      Get a list of DataSource classes that support the protocol, using the protocol package-prefix-list.

3.      For each source class in the list:

1.      Instantiate a new DataSource,

2.      Call the connect method to connect the source.

3.      Get the media content-type-name (using getContentType) from the source.

4.      Get a list of MediaHandler classes that support the media-content-type-name, using the content package-prefix-list.

5.      For each MediaHandler class in the list:

1.      Instantiate a new MediaHandler.

2.      Attach the source to the MediaHandler by calling MediaHandler.setSource.

3.      If there are no failures, determine the type of the MediaHandler; otherwise try the next MediaHandler in the list.

4.      If the MediaHandler is a Player, return the new Player.

5.      If the MediaHandler is a MediaProxy, obtain a new DataSource from the MediaProxy, obtain the list of MediaHandlers that support the new DataSource, and continue searching the new list.

6.      If no MediaHandler is found for this source, try the next source in the list.

4.      If no Player is found after trying all of the sources, reuse the source list.
This time, for each source class in the list:

1.      Instantiate the source.

2.      Call the connect method to connect to the source.

3.      Use the content package-prefix-list to create a list of MediaHandler classes that support the “unknown” content-type-name.

4.      For each MediaHandler class in the list, search for a Player as in the previous search.

1.      If no Player is found after trying all of the sources, a NoPlayerException is thrown.

Parameters:

sourceLocator – A MediaLocator that describes the media content.

Returns:

A Player for the media described by the source.

Throws:

NoPlayerException – Thrown if no Player can be found.

java.io.IOException – Thrown if there was a problem connecting with the source.



createPlayer

public static Player createPlayer(DataSource source)
                           throws java.io.IOException,
                                  NoPlayerException

Create a Player for the DataSource.

The algorithm for creating a Player from a DataSource is:

1.      Get the media content-type-name from the source by calling getContentType.

2.      Use the content package-prefix-list to get a list of Player classes that support the media content-type name.

3.      For each Player class in the list:

1.      Instantiate a new Player.

2.      Attach the source to the Player by calling setSource on the Player.

3.      If there are no failures, return the new Player; otherwise, try the next Player in the list.

4.      If no Player is found for this source:

1.      Use the content package-prefix-list to create a list of Player classes that support the “unknown” content-type-name.

2.      For each Player class in the list:

1.      Instantiate a new Player.

2.      Attach the source to the Player by calling setSource on the Player.

3.      If there are no failures, return the new Player; otherwise, try the next Player in the list.

5.      If no Player can be created, a NoPlayerException is thrown.

Parameters:

DataSource – The DataSource that describes the media content.

Returns:

A new Player.

Throws:

NoPlayerException – Thrown if a Player can’t be created.

java.io.IOException – Thrown if there was a problem connecting with the source.

 

createPlayer

public static Player createPlayer(java.net.URL sourceURL)
                           throws java.io.IOException,
                                  NoPlayerException

Create a Player for the specified media. This creates a MediaLocator from the URL and then calls createPlayer.

Parameters:

sourceURL – The URL that describes the media data.

Returns:

A new Player.

Throws:

NoPlayerException – Thrown if no Player can be found.

java.io.IOException – Thrown if there was a problem connecting with the source.

 

 

 

Una URL se compone de protocolo://destino/recurso, en resumen, compañeros tenemos que escribir file://(la ruta completa). No es válido poner “video.mpg” ya que no sigue la sintaxis.

Uno de nuestro errores fue olvidarnos de Manager y de su createPlayer, de tal forma que dijimos implementamos Player y a saco. Pero era muy engorroso, pesado y nos daba bastantes errores al compilar. Hasta que le dijimos: “Profe, ayúdanos, esto no nos sale y nos estamos haciendo la picha un lío”. Son de estas frases típicas made in Spain, que no las piensas y las sueltas sin mas, a lo que él contestó sin ningún enfado (cosa que agradecemos): “Procura no mezclar las palabras profe y picha en la misma frase.”. Ahora lo recordamos y nos reímos, pero pensándolo fríamente…QUE HABRÁ PENSADO EL PROFE!!! Desde este honrado blog, las más sinceras disculpas.

Después de todo este berenjenal, nos ayudó a recordar, percatándonos de que la manera más fácil era escribir Player p = Manager.createPlayer(…);

 

En clase se nos recordó lo explicado en la teoría sobre que un Player tiene varios estados: unrealized, realized, prefetched y started. Realized es el estado en el que se encuentra un player cuando es creado por primera vez y no tiene ningún conocimiento acerca de la fuente de datos de entrada. Por esta razón, tardará un tiempo en arrancar desconocido para nosotros. Por lo que, antes de hacer el getVisualComponent, hay que colocar p.realized(), porque no podemos reservar un tamaño para el video si no sé su tamaño. p.realized() calcula varias cosas. Realized salta al estado “me he dado cuenta” y no puedo llamar al getVisualComponent() antes de pasar a realized. Para saber cuando ocurre llamo a un escuchador de eventos.

Al player le creamos un controllerListener y se lo añadimos al panel.

 

javax.media
Interface ControllerListener

All Known Implementing Classes:

ControllerAdapter



public interface ControllerListener

ControllerListener is an interface for handling asynchronous events generated by Controllers.

Java Beans Support

If implementations of this interface are going to be used with Java Beans they need to also implement either java.util.EventListener or sunw.util.EventListener.

See Also:

Controller



Method Summary

 void

controllerUpdate(ControllerEvent event)
          This method is called when an event is generated by a
Controller that this listener is registered with.

 

Method Detail

controllerUpdate

public void controllerUpdate(ControllerEvent event)

This method is called when an event is generated by a Controller that this listener is registered with.

Parameters:

event – The event generated.

 

 

 

ControllerListener sólo tiene un método, que hay que redefinir en nuestra clase. Éste se lanza cuando ocurre un evento. Es un método para todos los eventos, entonces, dentro del método, hay que distinguir entre los diferentes eventos que nos pueden aparecer. Cuando ocurra un evento llegará un objeto que hereda de MediaEvent.

 

javax.media
Class MediaEvent

java.lang.Object
  |
  +--java.util.EventObject
        |
        +--javax.media.MediaEvent

Direct Known Subclasses:

ControllerEvent, DataSinkEvent, GainChangeEvent, RTPEvent



public class MediaEvent

extends java.util.EventObject

MediaEvent is the base interface for media events.

This is similar to the JMF 1.0 MediaEvent interface, except it’s a class.

Java Beans support

In order to support the Java Beans event model an implementation of MediaEvent is required to sub-class java.util.EventObject. If an implementation is designed to support the 1.0.2 JDK then it may alternatively sub-class sunw.util.EventObject to provide the support appropriate support.

See Also:

Serialized Form



Fields inherited from class java.util.EventObject

source

 

Constructor Summary

MediaEvent(java.lang.Object source)
           

 

 

Methods inherited from class java.util.EventObject

getSource, toString

 

Methods inherited from class java.lang.Object

clone, equals, finalize, getClass, hashCode, notify, notifyAll, wait, wait, wait

 

Constructor Detail

MediaEvent

public MediaEvent(java.lang.Object source)

 

 

 

 

Podemos hacer un instanceOf de tal forma que si llega un evento, ya se puede actuar, y sacamos el getVisualComponent(). El evento que de verdad nos interesa es RealizeCompleteEvent.

 

javax.media
Class RealizeCompleteEvent

java.lang.Object
  |
  +--java.util.EventObject
        |
        +--javax.media.MediaEvent
              |
              +--javax.media.ControllerEvent
                    |
                    +--javax.media.TransitionEvent
                          |
                          +--javax.media.RealizeCompleteEvent


public class RealizeCompleteEvent

extends TransitionEvent

A RealizeCompleteEvent is posted when a Controller finishes Realizing. This occurs when a Controller moves from the Realizing state to the Realized state, or as an acknowledgement that the realize method was called and the Controller is already Realized.

See Also:

Controller, ControllerListener, Serialized Form



Fields inherited from class java.util.EventObject

source

 

Constructor Summary

RealizeCompleteEvent(Controller from, int previous, int current, int target)
           

 

 

Methods inherited from class javax.media.TransitionEvent

getCurrentState, getPreviousState, getTargetState, toString

 

Methods inherited from class javax.media.ControllerEvent

getSource, getSourceController

 

Methods inherited from class java.lang.Object

clone, equals, finalize, getClass, hashCode, notify, notifyAll, wait, wait, wait

 

Constructor Detail

RealizeCompleteEvent

public RealizeCompleteEvent(Controller from,
                            int previous,
                            int current,
                            int target)

 

 

 

 

Otra forma de hacerlo es con hilos. Se puede colocar entre el método realized() y el getVisualComponent(), un Thread.sleep(..) a un tiempo, el que se quiera. Este es el camino erróneo, porque con el tiempo que se coloque en el sleep, si cojo tiempo de más, desperdicio tiempo de espera, y si cojo de menos, nos sale el tiro por la culata. Además de que hilos da mucho problemas.

Esta es una mala solución. En lugar de esperar un tiempo arbitrario, lo mejor es programar con el modo de controlar los eventos, el escuchador,… y toda esa parrafada.

 

¡¡CUIDADO!! Advertencia, el formato de video que se puede usar en JMF depende de la versión del JMF instalado. Si se intenta usar uno que no está instalado, saltará una popi excepción, UnableToHandleFormatException, o algo parecido.

 

Teóricamente sabíamos lo que había que hacer, pero después estabas frente al ordenador como si tuvieses un toro delante. Después de intentar implementar la interfaz Player, usamos a Manager. Llegados a este punto, nos perdimos. No sabíamos como seguir. Miramos en el API, le dimos muchas vueltas,… nada sirvió.

No paraban de saltarnos errores. Al principio tonterías de que faltaba importar paquetes. Hasta que llegó un momento que ni comprendimos los errores que salían. Intentamos seguir las instrucciones que se nos explicaron durante la clase, pero no sacábamos nada en claro, tan solo errores.

 

Hay que tener especial cuidado con el control de excepciones. A nosotros nos salieron el famosísimo IOException y MediaException, los cuales eliminamos rápidamente con un try y catch.

 

javax.media
Class MediaException

java.lang.Object
  |
  +--java.lang.Throwable
        |
        +--java.lang.Exception
              |
              +--javax.media.MediaException

Direct Known Subclasses:

BadHeaderException, CannotRealizeException, ClockStoppedException, IncompatibleSourceException, IncompatibleTimeBaseException, NoDataSinkException, NoDataSourceException, NoPlayerException, ResourceUnavailableException, UnsupportedFormatException, UnsupportedPlugInException



public class MediaException

extends java.lang.Exception

A MediaException indicates an unexpected error condition in a JMF method.

See Also:

Serialized Form



Constructor Summary

MediaException()
           

 

MediaException(java.lang.String reason)
           

 

 

Methods inherited from class java.lang.Throwable

fillInStackTrace, getLocalizedMessage, getMessage, printStackTrace, printStackTrace, printStackTrace, toString

 

Methods inherited from class java.lang.Object

clone, equals, finalize, getClass, hashCode, notify, notifyAll, wait, wait, wait

 

 

 

 

No logramos programar la clase.

Cuando terminó la hora, una compañera nos ayudó muuuuuuuuuuuuuuuuucho. Ella nos explicó que debíamos hacer para resolverlo. Lo resolvió como decía el profe, como nosotros intentamos, pero hay veces que te atascas y no sale ni para atrás. Aun así, nos pareció una práctica muy rara más que complicada. Es que es como siempre, una vez que la ves hecho lo entiendes, pero sino, no sabes. Esperamos que con lo que aprendimos en el aula y con la ayuda de nuestra compi ahora seamos capaces de resolver los JMF.

 

PUES AHORA TOCA DESPEDIRSE…L

Hacer el blog ha sido de una gran ayuda. Nos dábamos cuenta, a la hora de intentar explicar las cosas, qué no sabíamos y qué debíamos estudiar más. Además del pique que nos traíamos para ver quien de la clase colgaba primero la práctica jaja, hubo gente que la colgó a la media hora de empezar la clase, sin ni si quiera acabar jaja.

Leer los blogs de los compañeros también ayuda bastante pero eso de dejarles comentarios, se nos olvidaba siempre la verdad.

 

El último post que colguemos será, seguramente para dar nuestras impresiones sobre el juego, y quizá, por qué no, sobre la asignatura. Quién sabe que futuro llegará a tener este blog.

HASTA PRONTO!!! J

Posteado por: albalba | Mayo 27, 2008

Práctica 11

Uff la última práctica de swing… NOS AUTO-DAMOS ÁNIMO Y TAMBIÉN LO MANDAMOS A LOS LECTORES!!! Ya están aquí los exámenes, así que estudiar bastante para estar todos juntos de nuevo el año que viene.

En fin, a lo que íbamos. Al hacer esta práctica nos percatamos de que SWING es un tema muy extenso, por esta razón es imposible aprenderse todos los métodos y clases que existen en este campo, que hace cada uno,… en definitiva, ES IMPOSIBLE APRENDER SWING PERFECTAMENTE!! Entonces, intentaremos enterarnos de todo lo que podamos hasta que ya nuestros cerebros digan: “Hasta aquí he llegao’ chabalita.”.

A continuación lo que entendimos y lo que no en esta sesión.

 

Esta práctica trataba básicamente de la programación orientada eventos. Para entenderlo hay que comprender que los medios que teníamos para comunicarnos con un programa no son ágiles, no son adecuados (el manejador de ventana, la entrada estándar, los argumentos,..), por ello se desarrollaron los eventos.

Volviendo al tema de los hilos. Los eventos salen del hilo raiz, main. El thread main ejecuta el programa. Todos los eventos que se van produciendo entran en una cola de eventos, los cuales salen de uno en uno hacia el hilo bifurcado, llamado Event-Dispaching Thread. El event-dispaching thread se activa en cuanto invocas a cualquiera de los métodos setVisible(), show(), o pack(). El hilo que procesa los eventos (event-dispaching thread) trabaja secuencialmente, mientras que el hilo del programa (main) procesa eventos secuencialmente también, pero son distintos.

Los eventos no hay que utilizarlos para cualquier cosa. JLabel es pasivo y no necesita un escuchador. En cambio, JButton es activo y sobre él puedo producir un evento el cual será procesado por un objeto actionListener. Hay que añadir un actionListener, uan interfaz, con un método actionPerformed. Tengo un objeto de la clase que implementa actionListener para el botón:

b.addActionListener(o);

Para el escuchador o bien:

- creo una clase interna,

- o una clase que lo implemente y mando el objeto.

Cuando sea necesario utilizar actionListener, hay que importar java.awt.event;

Uno de los errores que cometimos mientras programábamos, fue que queríamos importar este paquete entero con esa sentencia. ERROR!!! Recordad que para importar el paquete event entero hay que colocar la sentencia java.awt.event.*;

 

Al principio de la clase programamos una clase en la que se mostrase una ventana con un botón, cada vez se pulsase dicho botón debía aparecer un texto por la salida estándar. Después de habernos peleado con el juego esto fue fácil de hacer. Lo más cómodo para hacer una clase para que aparezca una ventana, es hacer que ésta herede de JFrame. También la hicimos implementar de la clase actionListener, el escuchador para el botón, así se provocar el evento deseado. Como implementamos de actionListener tan sólo hay que escribir un método, el único que tiene, actionPerformed:

 

java.awt.event
Interface ActionListener

All Superinterfaces:

EventListener

All Known Subinterfaces:

Action

All Known Implementing Classes:

AbstractAction, AWTEventMulticaster, BasicDesktopPaneUI.CloseAction, BasicDesktopPaneUI.MaximizeAction, BasicDesktopPaneUI.MinimizeAction, BasicDesktopPaneUI.NavigateAction, BasicDesktopPaneUI.OpenAction, BasicFileChooserUI.ApproveSelectionAction, BasicFileChooserUI.CancelSelectionAction, BasicFileChooserUI.ChangeToParentDirectoryAction, BasicFileChooserUI.GoHomeAction, BasicFileChooserUI.NewFolderAction, BasicFileChooserUI.UpdateAction, BasicInternalFrameTitlePane.CloseAction, BasicInternalFrameTitlePane.IconifyAction, BasicInternalFrameTitlePane.MaximizeAction, BasicInternalFrameTitlePane.MoveAction, BasicInternalFrameTitlePane.RestoreAction, BasicInternalFrameTitlePane.SizeAction, BasicOptionPaneUI.ButtonActionListener, BasicScrollBarUI.ScrollListener, BasicSliderUI.ActionScroller, BasicSliderUI.ScrollListener, BasicSplitPaneUI.KeyboardDownRightHandler, BasicSplitPaneUI.KeyboardEndHandler, BasicSplitPaneUI.KeyboardHomeHandler, BasicSplitPaneUI.KeyboardResizeToggleHandler, BasicSplitPaneUI.KeyboardUpLeftHandler, BasicTreeUI.ComponentHandler, BasicTreeUI.TreeCancelEditingAction, BasicTreeUI.TreeHomeAction, BasicTreeUI.TreeIncrementAction, BasicTreeUI.TreePageAction, BasicTreeUI.TreeToggleAction, BasicTreeUI.TreeTraverseAction, DefaultCellEditor.EditorDelegate, DefaultEditorKit.BeepAction, DefaultEditorKit.CopyAction, DefaultEditorKit.CutAction, DefaultEditorKit.DefaultKeyTypedAction, DefaultEditorKit.InsertBreakAction, DefaultEditorKit.InsertContentAction, DefaultEditorKit.InsertTabAction, DefaultEditorKit.PasteAction, DefaultTreeCellEditor, DropTarget.DropTargetAutoScroller, FormView, HTMLEditorKit.HTMLTextAction, HTMLEditorKit.InsertHTMLTextAction, JComboBox, List.AccessibleAWTList, MetalFileChooserUI.DirectoryComboBoxAction, StyledEditorKit.AlignmentAction, StyledEditorKit.BoldAction, StyledEditorKit.FontFamilyAction, StyledEditorKit.FontSizeAction, StyledEditorKit.ForegroundAction, StyledEditorKit.ItalicAction, StyledEditorKit.StyledTextAction, StyledEditorKit.UnderlineAction, TextAction, ToolTipManager.insideTimerAction, ToolTipManager.outsideTimerAction, ToolTipManager.stillInsideTimerAction



public interface ActionListener
extends EventListener

The listener interface for receiving action events. The class that is interested in processing an action event implements this interface, and the object created with that class is registered with a component, using the component’s addActionListener method. When the action event occurs, that object’s actionPerformed method is invoked.

Since:

1.1

See Also:

ActionEvent, Tutorial: Java 1.1 Event Model



Method Summary

 void

actionPerformed(ActionEvent e)
          Invoked when an action occurs.

 

Method Detail

actionPerformed

void actionPerformed(ActionEvent e)

Invoked when an action occurs.

 

 

En el método actionPerformed() escribimos lo que queremos que ocurra cuando pulsemos el botón, en este caso haremos que por la salida estándar se lea: Cotilleando.

El profesor nos dijo que si ya teníamos hecho esto, metiésemos hilos en el programa para ver lo que aparecía, por esta razón el Simple00GUI también implementa a la interfaz Runnable. En esta ocasión, no nos quedó más remedio que tirar de la práctica anterior.

El resultado final fue el siguiente:

 

 

Y por pantalla se podía apreciar:

 

 

 

 

Después programamos una clase que presentase una ventana con un botón y una etiqueta, y cuando se pulse el botón, cambiar el color de la etiqueta. Así que, cogimos el programa anterior con un copy paste XD y lo modificamos para lograr el objetivo. Para colocarlo pusimos un GridLayout con dos filas y cero columnas, de tal forma que se colocarían el botón y debajo de él la etiqueta. Tuvimos que modificar el actionPerformed(). En él dejamos la sentencia colocada anteriormente, y añadimos un setBackground, para poner un color de fondo a la etiqueta, y un setOpaque a true. Pero no está muy bien hecho, ya que tan solo cambia de color cuando se pulsa el botón la primera vez, las siguientes veces ya no cambia. No sabemos que hacer para que se nos cambie de color la etiqueta cada vez que se pulse el botón.

Así nos quedó:

 

 

El último programa al que llegamos en esta sesión, fue en el que había que programar una clase que presente lo que se pedía en el ejercicio anterior, pero además, también se cambie de color la etiqueta cada vez que se pulse enter. Esta ya si que no llegamos ni si quiera a que nos compilase. El profe, al ver nuestro código cuando le hicimos una pregunta (bueno, en realidad unas cuantas jeje) nos aconsejó que el constructor tan solo inicializásemos los atributos y la parrafada la colocásemos en otro lado, verdad como un templo, por lo que para hacer esta clase lo pusimos como él nos aconsejó.

Para solucionar ésto, volvimos a pensar en los dichosos hilos. Debíamos hacer que se ejecutasen en distintas partes la interfaz gráfica y el teclado. Para hacer lo del enter, a última hora de clase se nos desveló el secreto, se hacía con un BufferedReader, como toda la vida. Cuando se haya ejecutado el readLine(), cuando se haya pulsado el enter, en el label se cambiará el color. Esto cambio de color queremos que lo haga el event-dispaching thread, principalmente con el método run(). El hilo main ha estado inactivo hasta ahora. Así, el main lanza los eventos gráficos a la cola de eventos y se procesará donde debe, el event-dispaching thread  procesa los eventos gráficos.

Este ejercicio nos resultó bastante “complicaillo”, veremos si ahora que ya tenemos esa “luz”, somos capaces de solucionarlo.

YA TAN SOLO QUEDA UNA PRÁCTICA, YUJUUU!!! Digo, uy que penaa!!

Hasta la siguiente y última compañeros.

 

 

 

 

Posteado por: albalba | Mayo 19, 2008

Práctica 10

Hola!! Sentimos habernos atrasado un poco…pero ya se acercan los exámenes y entre prácticas, el estudio, el agobio,… todo se junta. Aunque no os preocupéis, porque dentro de poco vendrá el día 1 de julio, ya veréis que está a la vuelta de la esquina.

Pues bien, la mañana en la que hicimos esta práctica se comentó que era una práctica importante,

Lo primero sobre lo que hablamos fue de los procesos. Acto seguido escribiremos lo que entendimos sobre lo que se dijo en clase. En el administrador de tareas se ven los procesos de un ordenador. Los programas necesitan procesos para funcionar. Un programa, en realidad, no se diferencia en nada de un proceso, son lo mismo. Son líneas de código compilado que se está ejecutando. Si tenemos 50 procesos, por ejemplo, ejecutándose en un pc, en realidad no están ejecutándose a la vez. En un sistema operativo multitarea tengo muchos procesos. Entonces nos surgió la duda, ¿es posible ejecutar 5 procesos al mismo tiempo? No, tan solo ejecuta un proceso en cada momento, pero los ordenadores trabajan en milisegundos, por lo que ni notamos que van de uno en uno. Tenemos 5 procesos pero se ejecuta uno cada vez. Un ejemplo es el antivirus, está ejecutando el rastreo del ordenata pero también lo hace si se cierra la ventana.

Después, comenzamos analizando el código de la clase Ding, bastante sencillo. Lo compilamos y ejecutamos. Cada vez que le des al enter, aparece por pantalla el mensaje DING! Realizamos la misma operación con la clase Dong, es decir, compilamos y ejecutamos. Con este programita, cada segundo se imprimía DONG! por pantalla. A la hora de entender el código de este ya tuvimos  que tirar del API porque no sabíamos que significaba la línea: Thread.sleep(1000); Buscamos la palabra thread, y su significado era hilo. Apareció lo siguiente sobre Thread en el API:

 

java.lang
Class Thread

java.lang.Object
  java.lang.Thread

All Implemented Interfaces:

Runnable

 

public class Thread
extends Object
implements Runnable

A thread is a thread of execution in a program. The Java Virtual Machine allows an application to have multiple threads of execution running concurrently.

Every thread has a priority. Threads with higher priority are executed in preference to threads with lower priority. Each thread may or may not also be marked as a daemon. When code running in some thread creates a new Thread object, the new thread has its priority initially set equal to the priority of the creating thread, and is a daemon thread if and only if the creating thread is a daemon.

When a Java Virtual Machine starts up, there is usually a single non-daemon thread (which typically calls the method named main of some designated class). The Java Virtual Machine continues to execute threads until either of the following occurs:

  • The exit method of class Runtime has been called and the security manager has permitted the exit operation to take place.
  • All threads that are not daemon threads have died, either by returning from the call to the run method or by throwing an exception that propagates beyond the run method.

There are two ways to create a new thread of execution. One is to declare a class to be a subclass of Thread. This subclass should override the run method of class Thread. An instance of the subclass can then be allocated and started. For example, a thread that computes primes larger than a stated value could be written as follows:


     class PrimeThread extends Thread {
         long minPrime;
         PrimeThread(long minPrime) {
             this.minPrime = minPrime;
         }
 
         public void run() {
             // compute primes larger than minPrime
              . . .
         }
     }
 


The following code would then create a thread and start it running:

     PrimeThread p = new PrimeThread(143);
     p.start();
 

The other way to create a thread is to declare a class that implements the Runnable interface. That class then implements the run method. An instance of the class can then be allocated, passed as an argument when creating Thread, and started. The same example in this other style looks like the following:


     class PrimeRun implements Runnable {
         long minPrime;
         PrimeRun(long minPrime) {
             this.minPrime = minPrime;
         }
 
         public void run() {
             // compute primes larger than minPrime
              . . .
         }
     }
 


The following code would then create a thread and start it running:

     PrimeRun p = new PrimeRun(143);
     new Thread(p).start();
 

Every thread has a name for identification purposes. More than one thread may have the same name. If a name is not specified when a thread is created, a new name is generated for it.

Since:

JDK1.0

See Also:

Runnable, Runtime.exit(int), run(), stop()

 

 

 

Nos quedó claro que Thread y Runnable iban dadas de la mano, pero eso lo miraremos más adelante. Ahora había que averiguar cuál era la función del método sleep.

 

sleep

public static void sleep(long millis)throws InterruptedException

Causes the currently executing thread to sleep (temporarily cease execution) for the specified number of milliseconds, subject to the precision and accuracy of system timers and schedulers. The thread does not lose ownership of any monitors.

Parameters:

millis – the length of time to sleep in milliseconds.

Throws:

InterruptedException – if any thread has interrupted the current thread. The interrupted status of the current thread is cleared when this exception is thrown.

See Also:

Object.notify()

 

 

Entonces entendimos porque aparecía un 1000 como parámetro, había que pasarle por parámetro el tiempo que quieres que cese la ejecución del hilo en términos de milisegundos, y como todos sabemos, un segundo son 1000 milisegundos.

Y pensamos, ¿por qué llama al método directamente colocando el nombre de la clase en lugar de llamarlo con un objeto? Esto se puede hacer porque dicho método es static.

 

Seguimos con la clase DingDongPoor, la cual probamos y no funcionaba de acuerdo con lo esperado. Esperábamos que cada segundo saliese DONG! y que cada vez que pulsábamos el enter dijese DING! Pero no. Pulsábamos el enter y aparecía

DONG!

DING!

DONG!

y esperaba a que el usuario volviese a dar al enter, sino no hacía nada. Parece ser que no podía hacer las dos cosas al mismo tiempo.

Hay sistemas operativos que establecen prioridades en los distintos procesos como Linux. Se pueden tener dos hilos de ejecución asociados al mismo proceso.

Para hacer que DING! y DONG! se ejecuten a la vez hay que tener en cuenta lo siguiente. Se puede resolver este problema de dos maneras. Una es implementando la internaz Runnable en la clase principal, y la otra es mediante la clase interna Runnable.

Existe una interfaz llamada Runnable que tan solo tiene el método public void run( ), y se crean instancias de él como Runnable r = new Runnable( );

 

java.lang
Interface Runnable

All Known Subinterfaces:

RunnableFuture<V>, RunnableScheduledFuture<V>

All Known Implementing Classes:

AsyncBoxView.ChildState, FutureTask, RenderableImageProducer, SwingWorker, Thread, TimerTask


public interface Runnable

The Runnable interface should be implemented by any class whose instances are intended to be executed by a thread. The class must define a method of no arguments called run.

This interface is designed to provide a common protocol for objects that wish to execute code while they are active. For example, Runnable is implemented by class Thread. Being active simply means that a thread has been started and has not yet been stopped.

In addition, Runnable provides the means for a class to be active while not subclassing Thread. A class that implements Runnable can run without subclassing Thread by instantiating a Thread instance and passing itself in as the target. In most cases, the Runnable interface should be used if you are only planning to override the run() method and no other Thread methods. This is important because classes should not be subclassed unless the programmer intends on modifying or enhancing the fundamental behavior of the class.

Since:

JDK1.0

See Also:

Thread, Callable


Method Summary

 void

run()
          When an object implementing interface
Runnable is used to create a thread, starting the thread causes the object’s run method to be called in that separately executing thread.

 

Method Detail

run

void run()

When an object implementing interface Runnable is used to create a thread, starting the thread causes the object’s run method to be called in that separately executing thread.

The general contract of the method run is that it may take any action whatsoever.

See Also:

Thread.run()

 

 

 

De la primera manera, es crear una clase interna Runnable en la clase principal. Desde fuera de la clase no se ve, a no ser que sea un atributo public. r es un objeto más. Thread es el encargado de hacer la bifurcación.

Thread t = new Thread (r);

Ahora tenemos un hilo, que en principio no se ha lanzado, relacionado con un Runnable. Si hacemos t.start(); el código que se ejecuta es el método run. start hace dos tareas: 1- crea un thread (hilo) a parte, y 2- llama a run. Cuando start acaba, se acaba el hilo de ejecución. La raíz se puede hacer que funcione y matar la bifurcación, pero no al revés.

 

De la otra manera, dentro de la clase principal sobrescribo el método run porque implementa la interfaz Runnable. Comparada con la otra es la forma más cómoda.

 

 

public class Fulanita implements Runnable{

   start(); //la bifurcación la hace start

   run(); //tengo que implementarla

}

 

Luego en el main hago un start. Si dentro de Fulanita hago:

 

Thread t = new Thread (r);

t.run();

 

 

sólo funciona por el hilo raíz, no por la bifurcación. Un ejemplo es, que aunque cierres la ventana, sigue ejecutando el programa, de los dos hilos uno está vivo.

 

Esto último es lo que hace la clase TwoThreadsSimple.

 

Ahora desglosaremos la clase TwoThreads. Se crea un método static que no devuelve nada llamado PrintThreadName. Se invoca la método currentThread() y getName() de la clase Thread:

 

currentThread

public static Thread currentThread()

Returns a reference to the currently executing thread object.

Returns:

the currently executing thread.

 

getName

public final String getName()

Returns this thread’s name.

Returns:

this thread’s name.

See Also:

setName(String)

 

 

Entonces en:

Thread.currentThread() , currentThread devuelve la referencia del hilo que se está ejecutando, y

Thread.currentThread().getName() , getName devuelve un String con el nombre del hilo que se está ejecutando, la referencia del hilo que devuelve currentThread.

 

El siguiente método es el main, el cual todos muy bien conocemos, en el que se crea un objeto del tipo Runnable. Implementamos el único método de dicha interfaz, run, invocando al método anteriormente descrito, el PrintThreadName.

 

 public static void main(String args[]) {
        Runnable r = new Runnable() {
            public void run() {
                PrintThreadName();
            }
        };
 
        Thread t = new Thread(r);
        t.start();
 
        PrintThreadName();
  }

 

Acto seguido, se crea un hilo t relacionado con un Runnable, y después lo ejecutamos con el t.start( ).

Al ejecutar salen dos resultados:

main

Thread-0

esos son los dos threads que lanza. El método PrintThreadName se imprime ahora en el main, se declara un Runnable r, que llama al método PrintTrheadName. Una vez que le dices que es lo que tiene que hacer, se crea un hilo hacia ese Runnable y lo inicia. Como resultado este imprimirá el proceso del Runnable. Creemos que como el main es otro Runnable, como llama al método PrintThreadName, simplemente imprime el que esta en ese momento ejecutándose.

 

Volviendo a la clase TwoThreadSimple, este realiza la misma función que TwoThread. La diferencia es que TwoThread realiza las operaciones colocando una clase interna, Runnable. En cambio, TwoThreadSimple, realiza las operaciones implementando la interfaz Runnable. Hacen lo mismo pero de dos maneras distintas.

 

Otra forma, es como la que se hace en el programa TwoThreadSimple2, en la que se hereda de la clase Thread, y no crear un objeto del tipo Thread. El resultado por tanto es el mismo que en los otros dos TwoThread.

 

Continuando con la práctica, nos encontramos con las condiciones de carrera.

Si tenemos, por ejemplo, un fichero compartido entre varios hilos, ¿cuándo abro el fichero? Este es uno de los tantos conflictos que surgen cuando dos hilos intentan acceder a lo mismo, a un recurso compartido, a la vez. No sabemos cuál se va a ejecutar primero. Es un problema. A esto se le llama condición de carrera (Race Condition). Estrictamente se ejecutan por turnos, pero no sabemos cuál lo hará primero y cuál después. Para evitar esto, se le da un tiempo de espera, un sleep(), pero esta solución no vale. Siempre que se pueda hay que evitar el recurso compartido, evitar que los dos threads accedan a la salida estándar.

Esto se comprueba ejecutando el programa RaceCondition. Pensábamos que este programa imprimiría 10.000 ceros y seguido 10.000 unos, o al revés , pero no ocurrió así. Tanto unos como ceros aparecían mezclados a su libre albedrío, y juntos y revueltos. Cada vez que ejecutábamos el programa aparecían revueltos de maneras diferentes. Era como si se estuviesen riendo de nosotros los muy gamberros jeje. Aunque no lo hacían aposta.

Una solución es hacer que el programa principal chequea el estado del programa, y otro programa que ejecute el estado del programa. Vaya trabalenguas eh.

Se podría colocar como recurso compartido, también, una cola de eventos.

 

Si compilamos y ejecutamos la clase RaceConditionSolved, veréis como los problemas desaparecen, y no es un anuncio de la tele ¡qué va! Con él salen los 10000 ceros y los 10000 unos sin mezclarse entre ellos.

 

Y hasta aquí llegamos. Esta clase se nos hizo un poco espesa. No había que programar nada pero si entender muchos conceptos nuevos. Además tuvimos que mirar mucho el API, lo malo es que tardamos en traducir lo que dice y entenderlo. Pero seguiremos en nuestro empeño de algún día aprender inglés XD.

HASTA LA PRÁCTICA 11!!

 

PD: Esta semana por cuestion de tiempo no hemos puesto las imágnes de las ejecuciones y más cosas que es habitual.

Posteado por: albalba | Mayo 5, 2008

Práctica 9

Hola a todos. Llegados a este punto empieza lo más complicado de la asignatura.

Se comenzó la clase con un profesor nuevo que de ahora en adelante nos dará las 5 prácticas restantes que quedan.

A parte de lo que se explicaba en la práctica, hubo que dejar conceptos claros antes de nada, como el de que GUI (Interfaces Gráficas de Usuario) es una forma de comunicarse con el usuario. La comunicación puede ser gráfica, textual, sonora,… la definición de que salen imágenes por pantalla es errónea. El programa se comunica con el usuario de diferentes formas entonces: mediante texto (entrada y salida estándar), mediante sonido, o con un interfaz gráfico.

También nos pareció interesante la explicación sobre el comienzo de las interfaces gráficas y AWT. En definitiva, que todos los componentes de swing J(algo), ese (algo) tienen su correspondiente en AWT.

 

Comenzamos la práctica algo “acongojaillos”, pero por lo menos lo que hicimos en clase no fue tan mal. Ahora trataremos cómo nos fue con el primer ejercicio.

Nos encontramos con la clase HelloWorldGUI. De buenas a primeras no sabíamos muy bien con que nos íbamos a encontrar, por lo que compilamos y ejecutamos dicho código, entonces comprendimos mejor que hacía cada línea. Pero explicar cada línea lo piden más adelante, por lo que, SEGUID LEYENDO!!

Después enumeramos los nombres de las clases de los objetos que se crean en la ejecución de este programa. Era sencillo observar que se creaba un objeto JLabel, un JFrame y un JPanel. Acto seguido, enumeramos los métodos que se invocan en la ejecución de este programa. El primero, y el principal era el método main, el constructor de JFrame (perteneciente a la clase JFrame, obviamente), el setDefaultCloseOperation() lo llama un objeto JFrame, el getContentePane requiere de una JPanel, el método add() pertenece a la clase JPanel (como JPanel es de tipo contenedor, entonces al llamar al add() añadimos métodos al contenedor), y por último setSize() y setVisible() corresponden a la clase Window.

A la hora de decir a que clase pertenecía cada método patinamos bastante. A partir del getContentPane() ya nos costó más verlo, aunque era de cajón. Las que nos asombramos bastante fueron los dos últimos métodos, nosotros pensábamos que serían de la clase JFrame ya que se invocaban con un objeto de ese tipo, pero no. Miramos en el API y efectivamente pertenecían a la clase Window, son métodos que heredan de la clase java.awt.Component. No hace falta importarla porque Window hereda de java.awt.Container, ésta de java.awt.Component, y ésta de java.lang.Object. Como todos los objetos heredan de la clase Object, no es necesario importarla.

Escuchando al profesor, y acordándonos de lo que nos dijo Alberto, entendimos que para aprender a programar debíamos saber inglés. Así que comenzamos a leer el código observando lo que nos acababa de aparecer. Frame significa marco, entonces la primera línea, ERA PARA DARLE NOMBRE AL MARCO DE LA VENTANA Y DIBUJAR LA VENTANA. Al crearse un JFrame ya se crea un panel por defecto. La segunda línea llama al método setDefaultCloseOperation() mediante el objeto JFrame de la primera línea. Como parámetro del método setDefaultCloseOperation(), se pasa un atributo que está en JFrame (ya que no hace falta un objeto para su invocación y no lleva paréntesis, deducimos que era un atributo), EXIT_ON_CLOSE. Éste, por lo general, es un entero y tiene como modificador static final (la pista es que está escrito en mayúsculas). La finalidad de esta sentencia es la acción sobre la ventana de cerrar  la ventana cuando se da a la aspa. Hay otras formas de cerrar ventanas, por ejemplo, que aparezca otra ventana preguntando si de verdad de la buena deseas cerrar la ventana, o cierras la ventana y se minimiza a un lado como el Messenger.

En la tercera línea creamos un objeto JPanel. Aquí se extrae, se obtiene, un contentPane. El getContentPane() dice al JFrame:”oyeeeeeeee, se que tienes un JPanel con un contentPane, andaaaaaa pirataaaaaa devuélvelo.”. El JFrame representa la ventana (hereda de Window) y JPanel es algo relacionado con un contenedor (hereda de Container). Esta línea nos costó bastante verla. Nos fuimos al API, a ver en la clase JFrame:

 

 

javax.swing
Class JFrame

 

java.lang.Object

  java.awt.Component

      java.awt.Container

          java.awt.Window

              java.awt.Frame

                  javax.swing.JFrame

 

el método getContentPane():

 

getContentPane

public Container getContentPane()

Returns the contentPane object for this frame.

Specified by:

getContentPane in interface RootPaneContainer

Returns:

the contentPane property

See Also:

setContentPane(java.awt.Container), RootPaneContainer.getContentPane()

 

 

por lo que, se devuelve el panel que contiene el JFrame, pero en un objeto de la clase Container. Como Container es superclase de la clase JPanel, entonces hay que hacer un casting para convertirlo en un objeto tipo JPanel.

En la siguiente sentencia buscamos en el diccionario label, cuyo significado es etiqueta. Creamos una instancia de JLabel mediante el constructor y lo inicializamos pasando un String (el texto, la etiqueta). También se puede poner imágenes, un fondo, un texto, pero no sonido.

En la quinta línea tuvimos que buscar el método add() en el API. Miramos en la clase JPanel,

 

Class JPanel

java.lang.Object

  java.awt.Component

      java.awt.Container

          javax.swing.JComponent

              javax.swing.JPanel

 

 

 

 

De aquí averiguamos que add() es un método que hereda de la clase java.awt.Container. Pinchando en él encontramos:

 

add

public Component add(Component comp)

Appends the specified component to the end of this container. This is a convenience method for addImpl(java.awt.Component, java.lang.Object, int).

Note: If a component has been added to a container that has been displayed, validate must be called on that container to display the new component. If multiple components are being added, you can improve efficiency by calling validate only once, after all the components have been added.

Parameters:

comp – the component to be added

Returns:

the component argument

See Also:

addImpl(java.awt.Component, java.lang.Object, int), validate(), JComponent.revalidate()

 

el cual devuelve un Componente y se le pasa por parámetro un componente. Como JLabel hereda de Componente:

 

javax.swing
Class JLabel

java.lang.Object

  java.awt.Component

      java.awt.Container

          javax.swing.JComponent

              javax.swing.JLabel

 

 

 

Se lo pasamos por parámetro.

Nos pareció un poco lioso. Como nos tengamos que aprender de que hereda cada clase y cada método que tiene cada uno para saber cuál podemos utilizar, todo va a ser un cacao maravillao.

En la sexta línea miramos en el API la clase JFrame buscando el método setSize(). Encontramos éste, y también el setVisible(), que aparece en la línea número siete, que heredaban de la clase java.awt.Component:

 

setSize

public void setSize(int width, int height)

Resizes this component so that it has width width and height height.

Parameters:

width – the new width of this component in pixels

height – the new height of this component in pixels

Since:

JDK1.1

See Also:

getSize(), setBounds(int, int, int, int)

 

setVisible

public void setVisible(boolean b)

Shows or hides this component depending on the value of parameter b.

Parameters:

b – if true, shows this component; otherwise, hides this component

Since:

JDK1.1

See Also:

isVisible()

 

 

 

 

con setSize() ajustamos el tamaño del marco. Cuando la ventana se represente en pantalla será del tamaño que le hayamos dicho.

Con setVisible() se muestra por pantalla, si lo pones a true.

Al saber esto fue sencillo adivinar que había cambiar para que la ventana aumentase su tamaño cuatro veces. Tan solo hay que modificar los parámetros del método setSize(), multiplicándolos por cuatro.

Para crear una nueva clase en la que el fondo de la etiqueta tenga color azul, fuimos al API en la clase JLabel. Tras mucho buscar no lo logramos encontrar, el API es inmenso, además nosotros íbamos buscando algún método que realizase tal función, pero ahora que lo pensamos lo que más sentido tiene es un set. Hasta que no se dijo en clase no supimos que era necesario el setBackground(Color.BLUE);

 

setBackground

public void setBackground(Color bg)

Sets the background color of this component.

Overrides:

setBackground in class Component

Parameters:

bg – the desired background Color

See Also:

Component.getBackground()

 

Procedente de la clase JComponent, la cual es superclase de JLabel:

 javax.swing
Class JComponent

java.lang.Object
  

  java.awt.Component
      

  java.awt.Container
          

  javax.swing.JComponent

All Implemented Interfaces:

ImageObserver, MenuContainer, Serializable

Direct Known Subclasses:

AbstractButton, BasicInternalFrameTitlePane, Box, Box.Filler, JColorChooser, JComboBox, JFileChooser, JInternalFrame, JInternalFrame.JDesktopIcon, JLabel, JLayeredPane, JList, JMenuBar, JOptionPane, JPanel, JPopupMenu, JProgressBar, JRootPane, JScrollBar, JScrollPane, JSeparator, JSlider, JSpinner, JSplitPane, JTabbedPane, JTable, JTableHeader, JTextComponent, JToolBar, JToolTip, JTree, JViewport

 

 

Al colocarla nos saltaba un error extraño, y el profesor nos ánimo a que observásemos bien lo que teníamos frente a nosotros, el API abierto por esa última clase. Nosotros entonces dijimos: “ah!, tendremos que importar JComponent”. POS’ NO (AMPARO, MANUELA, PO’ NO…..¿tas’ fumao’ un porro? Jjaja), porque JLabel hereda de JComponent. Lo que era necesario importar era la clase Color, para poder llamar a su atributo BLUE. El objeto de la clase Color ya está hecho. Color tiene una colección de tipos static, cada uno es un color. Entonces, nosotros escribimos: import java.awt.*; La verdad es que no estaba mal, así importábamos la clase Color pero también todas las demás que se encuentran en el paquete awt, de esta manera gastamos memoria. Lo mejor es colocar: import java.awt.Color; tal y como nos aconsejó el profesor, así la próxima vez que nos ocurra el mismo tipo de error, sabremos de donde proviene. De esta manera evitamos lo que se dice más adelante en la práctica, pensar que todo se hace como por arte de magia, ya que si para cada momento importamos tan solo lo estrictamente necesario aprenderemos cómo y por qué funciona cada cosa.

Después tuvimos otro problema, sin saber el motivo no nos aparecía en la ventana el esperado color azul de la etiqueta. Esto era causado por la falta del método setOpaque() en la clase HelloWorldGUIColor.

 

setOpaque

public void setOpaque(boolean isOpaque)

If true the component paints every pixel within its bounds. Otherwise, the component may not paint some or all of its pixels, allowing the underlying pixels to show through.

The default value of this property is false for JComponent. However, the default value for this property on most standard JComponent subclasses (such as JButton and JTree) is look-and-feel dependent.

Parameters:

isOpaque – true if this component should be opaque

See Also:

isOpaque()

 

 

Tan solo con poner el parámetro a true, logramos que se pinte el componente y se vea por pantalla. Si no se pone el setOpaque(), no aparece el color. Deducimos entonces que los componentes pueden ser opacos o no, y las etiquetas pueden ser transparentes u opacas. Utilizar el método setBackground() sólo tiene sentido si el componente es opaco. Este método hereda de la clase JComponent:

 

javax.swing
Class JLabel

java.lang.Object
  

  java.awt.Component
      

  java.awt.Container
          

  javax.swing.JComponent
              

  javax.swing.JLabel

 

 

A continuación se nos presentaban con los manejadores de ventana, y entendimos que éste es lo que pinta la ventana. El programa no puede controlar todo lo que ocurre en la pantalla, hay distintas ventanas abiertas en la pantalla. Esto se soluciona con el concepto de ventanas. A cada programa que quiere utilizar una interfaz gráfica, se le da un rectángulo, una ventana, la cual crea el manejador de ventana. Un programa se puede comunicar con el usuario con color, texto, imágenes,… El usuario se comunica con el programa escribiendo (JText), JButtom,.. El programa se tiene que comunicar con el manejador de ventanas en algún momento para dibujarle la ventana. La barra superior de una ventana (la que contiene los botones minimizar, maximizar y cerrar) es del manejador de ventana (si das a la aspa, el manejador de ventana dice al programa que se tiene que cerrar, el programa tiene que ejecutar) y el panel es “responsabilidad” del programa. No es necesario que el usuario se comunique con el manejador de ventana, porque para eso esta el programa. En resumen, el manejador de ventana gestiona las ventanas y el manejador de ventana se comunica con el usuario. Un ejemplo es cuando pulsamos el alt+tabulador para elegir entre distintas ventanas de la barra de estado.

 

Basándonos en la clase que nos dan al comienzo de la práctica, hay que superar una serie de retos.

El primer reto con el que nos topamos (y no somos topos ;) ) dentro de este campo, fue realizar una clase en la que se ignorase el mensaje de cierre del manejador de ventana. En un principio pensamos en utilizar el método descrito en la práctica setDefaultCloseOperation(), pero después caímos en la cuenta de la tontería que íbamos a hacer. Para ignorar el mensaje de cierre, simplemente evitamos que llegue ese mensaje quitando la sentencia que lo provoca, es decir, quitando frame.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE); La diferencia es que ahora, puedes cerrar la ventana pinchando en el aspa, pero el programa sigue corriendo, sigue ejecutado. Para pararlo hay que pulsar ctrl+ C.

Le sigue el reto de hacer que no se utilice ninguna decoración del manejador de ventanas. Dedicamos un poco de nuestro tiempo a leer los métodos explicados en la práctica. Claramente lo vimos, su propio nombre lo describía, setUndecorated(). Al llamarlo se queda el panel con su fondo grisáceo y su etiqueta HelloWorld! sobre él, pero desaparece ese borde azul típico de las ventanas de Windows y su barrita superior con sus tres monos botoncitos, obviamente, tampoco aparece el título que se le daba a esa barra, HelloWorldGUI window title.

 

 

 

 

Por último, había que hacer una clase que utilizase el Look and Feel por defecto en Java en vez del que estés usando con el manejador de ventanas. No estábamos muy seguros de si sería el método setDefaultLookAndFeelDecorated(boolean b) [jeje] ya que no nos lo insinúan para nada en el enunciado jeje. Desde luego nos quedó claro de forma teórica solamente la función que desempeñaba tal método, porque de escribirlo a no, no encontramos ninguna diferencia al respecto.

 

Y HASTA AQUÍ NUESTRA SESIÓN DE PRÁCTICAS. ÁNIMO QUE YA FALTAN POCAS!! Y RECUERDA: NO OLVIDES VISITARNOS. TE ESPERAMOS!!   ;)

 

PD. Oh my Gosh, ahora también nos entra complejo de Anne Igartiburu, o como leches se escriba!!

 

 

Ejercicio 2

 

import javax.swing.JFrame;

   import javax.swing.JPanel;

   import javax.swing.JLabel;

 

 

   class HelloWorldGUIDeaf

  

   {

  

      public static void main(String args[])

     

      {

         JFrame frame = new JFrame(”HelloWorldGUI window title”);

         frame.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);

     

         JPanel contentPane = (JPanel) frame.getContentPane();

         JLabel label = new JLabel(”Hello World!”);

         contentPane.add(label);

     

         frame.setSize(100,50);

         frame.setVisible(true);

      }

   }

 

                         

 

 

 

import javax.swing.JFrame;

   import javax.swing.JPanel;

   import javax.swing.JLabel;

 

 

   class HelloWorldGUIUndecorated

  

   {

  

      public static void main(String args[])

     

      {

         JFrame frame = new JFrame(”HelloWorldGUI window title”);

         frame.setUndecorated(true);    

         frame.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);

     

         JPanel contentPane = (JPanel) frame.getContentPane();

         JLabel label = new JLabel(”Hello World!”);

         contentPane.add(label);

     

         frame.setSize(100,50);

         frame.setVisible(true);

      }

   }

 

 

 

 

 

 

import javax.swing.JFrame;

   import javax.swing.JPanel;

   import javax.swing.JLabel;

 

 

   class HelloWorldGUIDecorated

  

   {

  

      public static void main(String args[])

     

      {

         JFrame.setDefaultLookAndFeelDecorated(true );

         JFrame frame = new JFrame(”HelloWorldGUI window title”);    

         frame.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);

     

         JPanel contentPane = (JPanel) frame.getContentPane();

         JLabel label = new JLabel(”Hello World!”);

         contentPane.add(label);

     

         frame.setSize(100,50);

         frame.setVisible(true);

      }

   }

 

 

 

 

 

Ejercicio 3

 

import javax.swing.JFrame;

   import javax.swing.JPanel;

   import javax.swing.JLabel;

   import java.awt.Color;

 

   public class CompassSimple

   {

      public CompassSimple()

      {

         JFrame frame=new JFrame(”CompassSimple”);

         frame.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);

     

         JPanel contentPane=(JPanel) frame.getContentPane();

         JLabel label=new JLabel(”Hola mundo :) “);

         label.setForeground(Color.WHITE);

         label.setBackground(Color.BLACK);

         label.setOpaque(true);

         contentPane.add(label);

     

     

         frame.setExtendedState(JFrame.MAXIMIZED_BOTH);//maximiza la ventana

     

         frame.setVisible(true); 

     

      }

  

      public static void main (String[] args)

      {

         CompassSimple nuevo=new CompassSimple();

      }

  

  

   }

 

 

 

  import javax.swing.JFrame;

   import javax.swing.JLabel;

   import javax.swing.JPanel;

   import java.awt.Color;

   import java.awt.BorderLayout;//importante importar si no no podemos colocar (eso del WEST EAST SOUTH NORTH)

 

   public class CompassSimple_

   {

      public CompassSimple_()

      {

         JFrame frame=new JFrame(”CompassSimple”);

         frame.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);

     

         JPanel contentPane=(JPanel) frame.getContentPane();

     

     

         JLabel north=new JLabel(”North”);

         JLabel south=new JLabel(”South”);

         JLabel west=new JLabel(”West”);

         JLabel east=new JLabel(”East”);

     

         north.setForeground(Color.WHITE);//color de letra

         north.setBackground(Color.BLACK);//color de fondo

         north.setOpaque(true);//para poder ver los colores

         contentPane.add(north,BorderLayout.NORTH);//añadimos al panel la etiqueta y la horientacion

     

         south.setForeground(Color.BLACK);

         south.setBackground(Color.WHITE);

         south.setOpaque(true);

         contentPane.add(south, BorderLayout.SOUTH);

     

         west.setForeground(Color.BLUE);

         west.setBackground(Color.RED);

         west.setOpaque(true);

         contentPane.add(west, BorderLayout.WEST);

     

         east.setForeground(Color.RED);

         east.setBackground(Color.BLUE);

         east.setOpaque(true);

         contentPane.add(east,BorderLayout.EAST);

     

     

     

         frame.setSize(200,100);//esto es para cuando minimisamos no se achurrusque del todo

         frame.setExtendedState(JFrame.MAXIMIZED_BOTH);//maximiza la ventana

     

         frame.setVisible(true); 

     

      }

  

      public static void main(String[] args)

      {

         CompassSimple_ nuevo=new CompassSimple_();

      }

   }

 

 

 

import javax.swing.JFrame;

   import javax.swing.JPanel;

   import javax.swing.JLabel;

   import java.awt.Color;

   import java.awt.BorderLayout;

 

   public class CompassBetter

   {

      public CompassBetter ()

      {

     

         JFrame frame=new JFrame(”CompassSimple”);

         frame.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);

     

         JPanel contentPane=(JPanel) frame.getContentPane();//si queremos luego manipular en el panel tenemos q hacer esto si no hace cosas raras

         contentPane.setBackground(Color.BLACK);//poner el fondo del panel en negro no nos lo piden lo ponemos nosotros

     

     

         JLabel north=new JLabel(”North”,JLabel.CENTER);

         JLabel south=new JLabel(”South”,JLabel.CENTER);

         JLabel west=new JLabel(”West”);

         JLabel east=new JLabel(”East”);

     

         north.setForeground(Color.WHITE);//color de letra

         north.setBackground(Color.BLACK);//color de fondo

         north.setOpaque(true);//para poder ver los colores

         contentPane.add(north,BorderLayout.NORTH);//añadimos al panel la etiqueta y la horientacion

     

     

         south.setForeground(Color.WHITE);

         south.setBackground(Color.BLACK);

         south.setOpaque(true);

         contentPane.add(south, BorderLayout.SOUTH);

     

         west.setForeground(Color.WHITE);

         west.setBackground(Color.BLACK);

         west.setOpaque(true);

         contentPane.add(west, BorderLayout.WEST);

     

         east.setForeground(Color.WHITE);

         east.setBackground(Color.BLACK);

         east.setOpaque(true);

         contentPane.add(east,BorderLayout.EAST);

     

     

      /*esto no nos lo piden nosotros lo ponemos*/

         frame.setSize(250,250);//esto es para cuando minimisamos no se achurrusque del todo

         frame.setExtendedState(JFrame.MAXIMIZED_BOTH);//maximiza la ventana

     

         frame.setVisible(true); 

     

      }

  

  

      public static void main(String[] args)

     

      {

         CompassBetter  nuevo=new CompassBetter ();

      }

   }

 

 

 

import javax.swing.JFrame;

   import javax.swing.JLabel;

   import javax.swing.JPanel;

   import javax.swing.ImageIcon;

   import javax.swing.Icon;

   import java.awt.Color;

   import java.awt.BorderLayout;//importante importar si no no podemos colocar (eso del WEST EAST SOUTH NORTH)

 

   public class CompassMuchBetter  

   {

      public CompassMuchBetter ()

      {

         JFrame frame=new JFrame(”CompassSimple”);

         frame.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);

     

         JPanel contentPane=(JPanel) frame.getContentPane();//si queremos luego manipular en el panel tenemos q hacer esto si no hace cosas raras

         contentPane.setBackground(Color.BLACK);//poner el fondo del panel en negro no nos lo piden lo ponemos nosotros

     

         Icon imagen=new ImageIcon(”arrow.jpg”);  

        

         JLabel north=new JLabel(”North”,JLabel.CENTER);

         JLabel south=new JLabel(”South”,JLabel.CENTER);

         JLabel west=new JLabel(”West”);

         JLabel east=new JLabel(”East”);

         JLabel center=new JLabel(imagen);

     

     

         contentPane.add(center,BorderLayout.CENTER);

     

     

         north.setForeground(Color.WHITE);//color de letra

         north.setBackground(Color.BLACK);//color de fondo

         north.setOpaque(true);//para poder ver los colores

         contentPane.add(north,BorderLayout.NORTH);//añadimos al panel la etiqueta y la horientacion

     

     

         south.setForeground(Color.WHITE);

         south.setBackground(Color.BLACK);

         south.setOpaque(true);

         contentPane.add(south, BorderLayout.SOUTH);

     

         west.setForeground(Color.WHITE);

         west.setBackground(Color.BLACK);

         west.setOpaque(true);

         contentPane.add(west, BorderLayout.WEST);

     

         east.setForeground(Color.WHITE);

         east.setBackground(Color.BLACK);

         east.setOpaque(true);

         contentPane.add(east,BorderLayout.EAST);

     

     

      /*esto no nos lo piden nosotros lo ponemos*/

         frame.setSize(250,250);//esto es para cuando minimisamos no se achurrusque del todo

         frame.setExtendedState(JFrame.MAXIMIZED_BOTH);//maximiza la ventana

     

         frame.setVisible(true); 

     

      }