Eventos de ratón y teclado

Los eventos son los que hacen cualquier programa interactivo funcionar. Sin ellos no hay interacción con el usuario, pero tampoco con el sistema operativo, o la red. El programación, sin ellos hay muy poco que hacer.

De hecho, os habréis fijado que aunque aún no hemos dedicado ningún capítulo a ellos, hemos estado usando eentos en casi todos nuestros ejemplos, bien para detectar el paso del tiempo (detectando cada vez que se pinta un fotograma), bien para detectar un click del ratón. Nigun programa se puede llamar interactivo sin ellos.

ActionScript posee un sistema de eventos similar al de javascript y otros lenguajes, y aunque hay algunos otros sistemas diferentes, si entendemos este, seremos capaces de entender todos los demás sin problema.

Así pues, presten atención, porque en este capítulo vamos a tratar con uno de los temas más importantes del curso.

Volver al ͍ndice

Event Listeners o Suscriptores de un evento

Es difícil traducir el nombre del mecanismo por el que se detectan eventos en as3. Traducido literalmente (en plan cutre, como toda traducción literal) sería algo así como “escuchadores de eventos”, aaaaarrrggggh! Sí suena muy mal.

Sólo si entendemos bien dicho mecanismo se nos puede ocurrir la traducción buena, que es la que además de trasladar una expresión de un idioma a otro, expresa con claridad su significado.

Exacto, los eventos funcionan mediante un sistema de suscripción, que utiliza un patrón muy conocido en programación orientada a objetos que se llama Patrón Observador.

Como la mayor parte de este patrón permanece oculto dentro del reproductor de flash (o de la máquina virtual de AIR cuando se trata de una aplicación) la mayoría de los programadores no necesitan saber como funciona para utilizarlo,… o eso creen.

La realidad es que si no se entiende el mecanismo es muy fácil cometer errores que son incomprensibles cuando se producen. Por otro lado entender como funciona, lo cual es bien sencillo, nos facilita mucho la vida y nos permite ahorrar muchas líneas de código.

Suscribirse a un evento es como …

… suscribirse a un periódico o a una revista.
Todas las editoriales grandes tienen sistemas de suscripción. Tú te diriges a ellas por carta o correo electrónico y les dices a que publicación te quieres apuntar y les das tus datos para que te encuentren y te entreguen el periódico, la revista o el libro bien calentito en cuanto salen de la imprenta.

El sistema de eventos de flash funciona exactamente igual, con la salvedad de que las editoriales son los objetos o instancias que pueden disparar eventos; el suscriptor, en vez de ser una persona con una dirección de correo, es una función con nombre; y el periódico o revista es un objeto especial llamado evento, que contiene toda la información que necesita la función suscriptora para funcionar.
eventos

Esta publicación puede ser periódica (cambio de un frame a otro, temporizador,…) o esperar a que algo ocurra para dispararse (pulsación de una tecla, un click de ratón, un archivo que acaba de descargarse, …)

Ahora bien, ¿Qué objetos son los que publican? es decir ¿Quienes disparan los eventos?

Las editoriales o emisores de eventos (Event dispatchers)

Nosotros hemos estado trabajando sólo con la escena (stage), cuando registrábamos eventos, por evitar complicaciones innecesarias,… hasta ahora. Pero lo cierto es que casi cualquier objeto de actionscript es o puede convertirse en una editorial o emisor de eventos.

Entonces, si hay una gran cantidad de objetos que disparan eventos cada vez que ocurre algo ¿Cómo distinguimos los que nos interesan de los que no?
Para empezar, un objeto solo “publica eventos” si tiene suscriptores. Y los suscriptores que creamos (funciones manejadoras de eventos) sólo se enteran cuando se produce el evento al que se han suscrito del emisor con el que se han suscrito.

Estamos hablando todo el rato de objetos que emiten eventos. Recordad que un objeto es una estructura de programación, propia del lenguaje o creada por nosotros, que contiene sus propias variables (o propiedades) y sus propias funciones (o métodos). Además, como estamos viendo en este capítulo, pueden ser emisores de eventos.

En lo que al curso respecta, la mayor parte del tiempo los objetos que vamos a estar utilizando son instancias de MovieClip o la propia escena (stage).

Los suscriptores

Los suscriptores a la publicación de eventos son siempre funciones que nosotros definimos. Dentro de estas funciones escribiremos los comandos que queramos que se ejecuten cuando se dispare el evento al que se suscriban.
Por ejemplo:

function clickEnBoton(evento:MouseEvent):void{
	boton.gotoAndPlay("click");
}

Estas funciones reciben automáticamente el evento al que se han suscrito, por eso hay que definirlo como parámetro de la función (evento:MouseEvent) y no devuelven nada, por lo que se declaran de tipo void.
Más adelante veremos como se usa el evento recibido y lo útil que puede llegar a ser.

Como se suscribe una función a un evento de un objeto

Todos los emisores de eventos poseen un método que se llama addEventListener.

function clickEnBoton(evento:MouseEvent):void{
	boton.gotoAndPlay("click");
}
boton.addEventListener(MouseEvent.CLICK, clickEnBoton);

En la línea 4 estamos suscribiendo la función clickEnBoton() a los eventos MouseEvent.CLICK que emite boton. addEventListener también es una función, en este caso predefinida en todos los emisores de objetos, que añade a una lista la función que se le pasa como segundo parámetro (clickEnBoton). Cuando se produce el evento, el emisor recorre esa lista y va ejecutando cada una de las funciones que hay en ella una a una, pasándoles el evento como parámetro.

El evento

El evento es un objeto que puede ser de varios tipos (Event, MouseEvent, KeyboardEvent, etc.) y que contiene una serie de propiedades (variables) que pueden resultar muy útiles.
Por ejemplo, casi todos los tipos de evento contienen la propiedad target que es una referencia al objeto que disparó el evento. Así en el código de la función del ejemplo anterior, podemos sustituir boton por evento.target y quedaría así:

function clickEnBoton(evento:MouseEvent):void{
	evento.target.gotoAndPlay("click");
}

Aquí puede no ser una gran ventaja, pero si tuviéramos varios botones, lo sería, porque no tendríamos que escribir una función para cada uno de ellos:

function clickEnBoton(evento:MouseEvent):void{
	if (evento.target.name == "boton1") {
		// codigo de las acciones a realizar si se ha pulsado el boton1
	} else if (evento.target.name == "boton2") {
		// codigo de las acciones a realizar si se ha pulsado el boton2
	} else if (evento.target.name == "boton3") {
		// codigo de las acciones a realizar si se ha pulsado el boton3
	} else {
		// código a ejecutar si se ha pulsado otro botón
	}
}

Claro que para que esto funcione todas las instancias del botón tienen que tener el nombre definido.
Más adelante veremos aplicaciones reales de esta técnica.

Eventos de ratón

Los eventos de ratón son, con diferencia, los más utilizados. En la siguiente tabla se resumen los más importantes.

tablaMouseEvent

Botones y eventos de ratón

De momento vamos a utilizar los tres más comunes: MouseEvent.CLICK, MouseEvent.ROLL_OVER y MouseEvent.ROLL_OUT. Y para ir introduciendo los tipos básicos de interactividad vamos a utilizar como ejemplo el tipo de botón más sencillo.

[gigya src=”https://dl.dropboxusercontent.com/u/3646945/newbeForever/esne/botonSencilloMejor.swf” width=”550px” height=”100px”]
Fijaos bien en el comportamiento del ejemplo. Este es el comportamiento que queremos. Es muy recomendable modelizar el comportamiento de cualquier elemento interactivo que queramos programar con un diagrama que se llama diagrama de estados. En este caso sencillo quizás sería innecesario, pero para aprender a usar estos diagramas es mejor empezar con un ejemplo naïve.

Este sería el diagrama:
diagramaEstadosBotonSencillo
El botón tiene 3 estados, que llamaremos normal, encima y presionado. Una vez dibujados en el diagrama, debemos pensar cómo se pasa de uno a otro, es decir en lo que se denominan las transiciones entre estados. Cada una de ellas se representa con una flecha. Además, conviene comentar en el diagrama lo que está pasando.

Lo que sí es muy importante es que se se pasa de un estado a otro porque se ha disparado un evento, especifiquemos al lado de la flecha que evento es. ¿Por qué es tan importante? Porque esas transiciones son las que tenemos que programar necesariamente con código. Las transiciones que no lleven un evento al lado podremos implementarlas con código en la línea de teimpo (ver más adelante).

Este diagrama nos sirve para planificar nuestro botón. Vamos a crearlo en Flash como un símbolo tipo clip de película (recordad que os dije que ibamos ignorar los botones nativos de flash). Aquí os lo dejo preparado para que os lo bajeis y sigais la explicación con el fla abierto delante.

Descargar el archivo .fla para el ejemplo de botón

Encontraréis en la escena una instancia del símbolo boton. Haced doble click sobre ella y observad la línea de tiempo de este símbolo. Como tenemos tres estados, lo más sencillo es crearlos en tres fotogramas clave diferentes y marcarlos con etiquetas. Cuando queramos pasar de un estado a otro, sólo tendremos que utilizar por ejemplo este comando:

//...
boton.gotoAndStop("encima");
//...

para mandar el botón al estado "encima".

El estado “normal” es nuestro estado inicial, y debe quedarse ahí hasta que se produzca el evento MouseEvent.ROLL_OVER, tal y como especificamos en nuestro diagrama de estados. Si no añadiéramos nada de código a la línea de tiempo del símbolo botón, esa línea de tiempo se reproduciría en bucle una y otra vez (ese es el comportamiento por defecto de todas las animaciones en línea de tiempo, ¿recordáis?).

Así que una vez terminada la parte gráfica y añadidas las etiquetas, lo primero que debemos hacer es introducir un fotograma clave en el primer fotograma de una capa (que llamamos, como siempre,as3) e introducir el código stop();. Para que al publicar el boton se pare en el estado, o etiqueta, “normal” y se quede ahí hasta que se produzca el evento MouseEvent.ROLL_OVER.

Todavía tenemos que añadir otro comando as3 a nuestro botón. Si os fijáis en el diagrama de nuevo, veréis que no hay evento para la transición de “presionado” a “encima”. Aunque esta transición se podría conseguir con código desde nuestro programa principal (en el primer fotograma de la escena), lo más sencillo para este tipo de transiciones es hacerlo desde la línea de tiempo del símbolo. Así que nos vamos al último fotograma del estado “presionado” y en la capa as3 creamos un fotograma clave e introducimos gotoAndStop("encima") (recordad que como estamos dentro del MovieClip no hay que decirle a que MovieClip nos referimos). En realidad a este tipo de estados prodríamos llamarlos transitorios por que cuando se llega a ellos después de un tiempo o cuando termina una animación, saltan automáticamente a otro estado, sin esperar a que se produzca ningún otro evento.

Ahora ya sólo nos queda pegarnos con el código, sin olvidar que para que este funcione, tendremos que haberle puesto a la instancia del símbolo boton, el nombre de instancia boton. No pasa nada porque tengan el mismo nombre, ya que los símbolos de la biblioteca no son visibles para el programa y no hay conflicto (no confuncir el nombre del símbolo con el nombre de clase que se le da cuando se explorta para actionscript. ¡¡Ahí sí que hay que tener cuidado con los conflictos!!):

function punteroEncima(evento:MouseEvent):void{
	boton.gotoAndStop("encima");
}

function punteroFuera():void{
	boton.gotoAndStop("normal");
}

function cuandoHagaClick(evento:MouseEvent):void {
	boton.gotoAndPlay("presionado");
}

boton.addEventListener(MouseEvent.ROLL_OVER,punteroEncima);
boton.addEventListener(MouseEvent.ROLL_OUT,punteroFuera);
boton.addEventListener(MouseEvent.CLICK,cuandoHagaClick);

Sólo queda decir, que boton es el objeto emisor de eventos, o editorial, al que nosotros pedimos la suscripción mediante el comando addEventListener. En ese comando hay que especificar el evento y la función que se quiere suscribir al mismo.

Cuando definimos las funciones, estas deben quedar preparadas para recibir el eevento al que se ha suscrito, por eso se pone entre paréntesis (evento:MouseEvent).

Y, por último, fijaos en que en la función suscrita al evento MouseEvent.CLICK no paramos el botón en la etiqueta "presionado" sino que lo reproducimos desde el primer fotograma clave de esa etiqueta, de forma que cuando llega al final, se encuentra en el íultimo fotograma con el comando gotoAndStop("encima") y vuelve al estado “encima” para quedarse ahí hasta que el botón dispare otro evento.

Averiguar el emisor del evento con la propiedad target del evento

Lo anterior está muy bien pero qué hacemos si queremos tener varios botones. Para empezar cono lo que hemos visto, si queremos que cada uno tenga un texto diferente tendríamos que crear un símbolo diferente para cada uno. Esto no es muy práctico. Lo ideal sería tener un sólo símbolo.

¿Cómo conseguimos esto?, utilizando lo que sabemos de campos de texto dinámicos (Ver Trabajar con texto con Flash y actionscript) y haciendo que el texto del botón sea dinámico. Para poder utilizarlo desde el código le tendremos que poner un nombre de instancia a dicho campo, por ejemplo textoBoton.
Si ahora exportamos para actionscript el símbolo boton con el nombre de cláse MiBoton podemos utilizar el siguiente código para crear instancias del mismo:

var boton1:MovieClip =  new MiBoton();
addChild(boton1);
boton1.x = 100;
boton1.y = 100;
var boton2:MovieClip =  new MiBoton();
addChild(boton2);
boton2.x = 100;
boton2.y = 200;

Y si queremos (que sería lo lógico), que cada uno tenga un texto distinto:

var boton1:MovieClip =  new MiBoton();
boton1.nombreBoton.text = "Aceptar";
addChild(boton1);
boton1.x = 100;
boton1.y = 100;
var boton2:MovieClip =  new MiBoton();
boton2.nombreBoton.text = "Cancelar";
addChild(boton2);
boton2.x = 100;
boton2.y = 200;

Ahora la cuestión es como suscribir y utilizar nuestras funciones del ejemplo anterior para que los dos botones reaccionen a los eventos:
Fijaos que en las funciones suscritas a los eventos de ratón se está definiendo que se recibe como parámetro el evento MouseEvent. Pues bien, MouseEvent, tiene una proiedad que se llama target, que contiene (mejor dicho: a la que está asignada) el objeto que disparó el evento (en nuestros ejemplos: el botón).
Así que podríamos sustituir, por ejemplo, la función cuandoHagaClick(), por

function punteroEncima(evento:MouseEvent):void {
	evento.target.gotoAndStop("presionado");
}

y la función funcionaría con los dos botones. Probadlo.

El código completo quedaría:

function punteroEncima(evento:MouseEvent):void{
	evento.target.gotoAndStop("encima");
}

function punteroFuera():void{
	evento.target.gotoAndStop("normal");
}

function cuandoHagaClick(evento:MouseEvent):void {
	evento.target.gotoAndPlay("presionado");
}

var boton1:MovieClip =  new MiBoton();
boton1.nombreBoton.text = "Aceptar";
addChild(boton1);
boton1.x = 100;
boton1.y = 100;
boton1.addEventListener(MouseEvent.ROLL_OVER,punteroEncima);
boton1.addEventListener(MouseEvent.ROLL_OUT,punteroFuera);
boton1.addEventListener(MouseEvent.CLICK,cuandoHagaClick);

var boton2:MovieClip =  new MiBoton();
boton2.nombreBoton.text = "Cancelar";
addChild(boton2);
boton2.x = 100;
boton2.y = 200;
boton2.addEventListener(MouseEvent.ROLL_OVER,punteroEncima);
boton2.addEventListener(MouseEvent.ROLL_OUT,punteroFuera);
boton2.addEventListener(MouseEvent.CLICK,cuandoHagaClick);

Pero, un momento, esto no funciona!!!. A veces, o nunca se detecta el evento MouseEvent.CLICK (Depende de si el campo de texto cubre todo el botón o no).

Esto ocurre porque los campos de texto dinámico capturan el evento click y no dejan que el botón lo recoja. En el capitulo sobre la Lista de Visualización se explicará esto en más de talle, pero demomento baste saber que la forma de evitar este problema es utilizar la propiedad mouseChildren de MovieClip para decirle a los botones que desactiven la captura de eventos de todos elemnentos gráficos que tienen dentro.
Así que lo siguiente corregiría el problema:

function punteroEncima(evento:MouseEvent):void {
	evento.target.gotoAndStop("presionado");
}

y la función funcionaría con los dos botones. Probadlo.

El código completo quedaría:

function punteroEncima(evento:MouseEvent):void{
	evento.target.gotoAndStop("encima");
}

function punteroFuera():void{
	evento.target.gotoAndStop("normal");
}

function cuandoHagaClick(evento:MouseEvent):void {
	evento.target.gotoAndPlay("presionado");
}

var boton1:MovieClip =  new MiBoton();
boton1.nombreBoton.text = "Aceptar";
boton1.mouseChildren = false;
addChild(boton1);
boton1.x = 100;
boton1.y = 100;
boton1.addEventListener(MouseEvent.ROLL_OVER,punteroEncima);
boton1.addEventListener(MouseEvent.ROLL_OUT,punteroFuera);
boton1.addEventListener(MouseEvent.CLICK,cuandoHagaClick);

var boton2:MovieClip =  new MiBoton();
boton2.nombreBoton.text = "Cancelar";
boton2.mouseChildren = false;
addChild(boton2);
boton2.x = 100;
boton2.y = 200;
boton2.addEventListener(MouseEvent.ROLL_OVER,punteroEncima);
boton2.addEventListener(MouseEvent.ROLL_OUT,punteroFuera);
boton2.addEventListener(MouseEvent.CLICK,cuandoHagaClick);

Eliminar suscriptores. Como saber si un objeto tiene un listener suscrito

A veces, necesitamos eliminar suscriptores de la lista de un evento. Esto ocurre cuando ya no queremos que una función siga escuchando y se jecute cada vez que se produzca un determinado evento.
Para ello utilizamos la función removeEventListener().

Veamos, si queremos desactivar por completo los eventos del botón después de que se haya clickado sobre el por primera vez:

function punteroEncima(evento:MouseEvent):void{
	event.target.gotoAndStop("encima");
}

function punteroFuera():void{
	event.target.gotoAndStop("normal");
}

function cuandoHagaClick(evento:MouseEvent):void {
	event.target.gotoAndPlay("presionado");
	desactivarEventosDe(event.target);
}

function desactivarEventosDe(instancia:MovieClip):void {
	instancia.removeEventListener(MouseEvent.CLICK,cuandoHagaClick);
	instancia.removeEventListener(MouseEvent.ROLL_OVER,punteroEncima);
	instancia.removeEventListener(MouseEvent.ROLL_OUT,punteroFuera);
}

boton.addEventListener(MouseEvent.ROLL_OVER,punteroEncima);
boton.addEventListener(MouseEvent.ROLL_OUT,punteroFuera);
boton.addEventListener(MouseEvent.CLICK,cuandoHagaClick);

No es aplicable a este ejemplo, pero a veces sólo queremos eliminar la suscripción al evento si el emisor tiene suscriptores. Para ello utilizamos la función hasEventListener() que acepta como parámetro el nombre del evento y devuelve true si el evento del emisor tiene suscriptores en su lista y false si no.

if (boton.hasEventListener(MouseEvent.CLICK)){
	boton.removeEventListener(MouseEvent.CLICK, cuandoHagaClick);
}

El código anterior eliminaría cuandoHagaClick() de la lista de suscriptores al evento MouseEvent.CLICK de boton, sólo si el evento MouseEvent.CLICK de boton tiene suscriptores.

Suficientes eventos de ratón por ahora, veamos que podemos hacer con los eventos de teclado.

Eventos de teclado

Estos eventos son más simples. Sólo hay dos tipos KeyboardEvent.KEY_DOWN, que se dispara cuando se presiona una tecla, y KeyBoardEvent.KEY_UP, que se dispara cuando se suelta la misma.

eventosTeclado

Averiguar que tecla es la que se ha presionado es un poco más complicado, pero no demasiado.

Aquí es fundamental utilizar el evento que recibe la función suscriptora del mismo. Los eventos KeyboardEvent tienen dos propiedades que necesitamos para esto: keyCode y charCode. Las dos son numéricas y codifican que tecla es la que se ha pulsado de dos formas distintas.

Seguramente os preguntaréis por qué no hay una propiedad del evento que nos diga la tecla que se ha presionado o soltado y ya está. La verdad es que eso estaría muy bien, pero lo cierto es que hay muchos tipos de teclados, en varios idiomas y, lo que es más importante con una distribución de teclas diferente (teclados QWERTY, AZERTY, etc). Como todo lenguaje de programación que se precie ha de ser multilenguaje se ha tenido que inventar un mecanismo para que sea el sistema operativo el que nos de la información necesaria sobre el idioma y la distribución del teclado.

Este mecanismo es el método String.fromCharCode() y se utiliza de la siguiente forma para obtener el charCode o el keyCode de cada tecla.


El siguiente ejemplo ilustra el método, además de servir como utilidad para averiguar los keyCodes o charCodes de la tecla presionada.

import flash.events.KeyboardEvent;

function cuandoPulsesTecla(evento:KeyboardEvent):void{
	trace("Se ha presionado una tecla:");
	trace("   keyCode:", evento.keyCode);
	trace("   charCode:", evento.charCode);
	trace("El charCode se corresponde con:", String.fromCharCode(evento.charCode) );
}

stage.addEventListener(KeyboardEvent.KEY_DOWN, cuandoPulsesTecla);

Otra pregunta lógica sería ¿por qué tenemos dos propiedades que aparentemente hacen lo mismo, darnos un código para las teclas? Porque en realidad keyCode nos da un código para la tecla, mientras que charCode nos lo da para el caracter. Fijaos en la diferencia de pulsar 1 en el teclado principal y pulsar 1 en el teclado numérico.
Los dos tienen el mismo charCode (49), pero el primero tiene keyCode 49 y el segundo 97.

Volver al ͍ndice

Deja un comentario

Tu dirección de correo electrónico no será publicada. Los campos obligatorios están marcados con *