Tabla de contenidos
En este ejercicio debes finalizar la calculadora que empezaste a programar en la práctica anterior. Utiliza tu solución a dicha práctica como punto de partida. Si no tienes el código a mano, utiliza la solución oficial que se ha publicado para dicho ejercicio.
Para simplificar el problema, esta versión de la calculadora sólo debe trabajar con números enteros. Por tanto, deshabilita en la interfaz el botón ".", de tal forma que no sea pinchable.
Se te proporciona la lógica interna de la calculadora ya
programada en la clase CalculatorLogic
que se muestra
a continuación, pero necesitarás enlazarla adecuadamente con tu
interfaz gráfica. Esta clase dispone de métodos públicos para que,
cada vez que el usuario pulse un botón, se lo comuniques. Además,
tras comunicar cada pulsación debes invocar al método
getDisplay()
, que te indicará qué debes mostrar en la
pantalla de la calculadora.
/** * Class that implements the logic of a basic calculator for integer * numbers. * */ public class CalculatorLogic { public static final int OPERATOR_NONE = 0; public static final int OPERATOR_SUM = 1; public static final int OPERATOR_SUB = 2; public static final int OPERATOR_MUL = 3; public static final int OPERATOR_DIV = 4; private int accumulator; private int currentOperand; private int currentOperator; private boolean operandAvailable; /** * Creates a new instance of the calculator logic. In its initial * state the calculator displays just a zero, the value of its * internal accumulator is also zero and there is no operator * pending to be applied. * */ public CalculatorLogic() { pressReset(); } /** * Returns the information that should be shown at the display of * the calculator for its current state. * * @return The information to be shown at the display. * */ public String getDisplay() { String display; if (operandAvailable) { display = Integer.toString(currentOperand); } else { display = Integer.toString(accumulator); } return display; } /** * Notifies that the user has pressed a button with a digit. * * @param digit The digit the user has pressed (0 to 9). * */ public void pressDigit(int digit) { currentOperand = currentOperand * 10 + digit; operandAvailable = true; } /** * Notifies that the user has pressed a button with an operand. * * @param operator The operand expressed with one of the constants * OPERATOR_ADD, OPERATOR_SUB, OPERATOR_MUL or * OPERATOR_DIV. * */ public void pressOperator(int operator) { if (operandAvailable) { performPendingOperation(); } expectNewOperand(operator); } /** * Notifies that the user has pressed the "=" button. * */ public void pressEqual() { if (operandAvailable) { performPendingOperation(); } expectNewOperand(OPERATOR_NONE); } /** * Notifies that the user has pressed the "C" button, which removes * the last digit that was typed by the user. * */ public void pressDelete() { currentOperand /= 10; if (currentOperand == 0) { operandAvailable = false; } } /** * Notifies that the user has pressed the "AC" button, which * resets the state of the calculator to its initial state * (display 0, accumulator 0, no pending operators.) * */ public void pressReset() { accumulator = 0; expectNewOperand(OPERATOR_NONE); } private void expectNewOperand(int operator) { currentOperator = operator; currentOperand = 0; operandAvailable = false; } private void performPendingOperation() { switch (currentOperator) { case OPERATOR_NONE: accumulator = currentOperand; break; case OPERATOR_SUM: accumulator += currentOperand; break; case OPERATOR_SUB: accumulator -= currentOperand; break; case OPERATOR_MUL: accumulator *= currentOperand; break; case OPERATOR_DIV: accumulator /= currentOperand; break; } } }
En este ejercicio programarás una interfaz gráfica en que los ojos de un muñeco deben seguir la posición del cursor cuando este se encuentre dentro de la ventana. El programa debe tener una apariencia similar a la siguiente:
El contorno exterior del ojo se está dibujando como un círculo de radio 60. La pupila tiene radio 10. Su posición depende de la posición del cursor dentro de la ventana: debe estar siempre sobre la recta que une el cursor con el centro del ojo, a exactamente 30 píxeles de este último. A continuación se muestra un ejemplo:
Como puedes observar en el esquema que se muestra a continuación, si se conocen las coordenadas del centro del ojo (O), las coordenadas del cursor (C) y la distancia d = 30 píxeles, calcular la posición en que se debe situar la pupila (X) consiste en resolver un problema de semejanza de triángulos rectángulos:
En primer lugar, escribe el código necesario para que la aplicación funcione con respecto a un punto fijo. Esto es, en vez de seguir el cursor, las pupilas se alinearán con respecto a un punto fijo de prueba que definas. De esta forma, no necesitarás manejar eventos por el momento.
Puedes basarte en el código del ejercicio de dibujo de la práctica anterior. Para hacerte más fácil el dibujo de círculos, puedes añadir a tu código el método siguiente, que recibe las coordenadas del centro del círculo, su radio, el color a usar y un parámetro booleano que indica si se debe pintar el relleno o el borde. Para pintar un círculo con relleno blanco y borde negro, pinta primero un círculo relleno de color blanco y después un círculo sin relleno de color negro.
private void drawCircle(Graphics g, int x, int y, int radius, Color color, boolean filled) { int posX = x - radius; int posY = y - radius; g.setColor(color); if (filled) { g.fillOval(posX, posY, 2 * radius, 2 * radius); } else { g.drawOval(posX, posY, 2 * radius, 2 * radius); } }
Prueba la aplicación con distintos puntos fijos.
Ahora debes hacer que los ojos sigan la posición del
cursor. Para ello, debes hacer que tu panel de dibujo ejerza de
escuchador MouseMotionListener
para él mismo. Esto es, desde el propio panel debes manejar los
eventos que se producen cuando se mueve el cursor en el interior
del panel.
Cada vez que detectes que se ha movido el cursor debes recoger
las nuevas coordenadas del mismo y forzar que se actualice el
dibujo invocando al método repaint()
que tiene el panel. Cuando invocas a este método, estás pidiendo
al panel que se vuelva a dibujar. Como parte de dicha operación
de dibujo el panel volverá a invocar al método
paintComponent()
que has programado, que debe hacer
de nuevo el dibujo teniendo en cuenta la nueva posición del
cursor.
Ahora queremos que, cada vez que el usuario pulse el botón izquierdo del ratón, el programa conmute entre dos modos. En el primer modo hace seguimiento del ratón como en el apartado anterior. En el segundo modo no hace seguimiento del ratón, y las pupilas se colocan en posición de reposo en el centro del ojo.
Para detectar cuándo el usuario presiona un botón del ratón
debes hacer que tu panel también ejerza de MouseListener
para él mismo.
Ahora queremos que, cuando el cursor abandone el área del panel,
el muñeco muestre una cara de enfado como se ve en la imagen
siguiente, y que recupere la sonrisa cuando el cursor vuelva a
entrar. Examina la API de MouseListener
para saber
cómo detectar cuándo entra o sale el cursor.
En este ejercicio vamos a utilizar un juego conocido, el "Tres en Raya", como ejemplo de Interfaz Gráfica de Usuario (GUI) que integra la correspondiente Gestión de Eventos.
Implementa en Java el tablero de un tres en raya, tal y
como se presenta en la figura. Para ello crea una clase TicTacToe
y haz uso de las clases JFrame
, JPanel
, JLabel
y JButton
del paquete
javax.swing
y de las clases BorderLayout
y
GridLayout
del paquete java.awt
. Incluye un
atributo denominado player
en tu clase para mantener el
turno del jugador e implementa los métodos get
y
set
correspondientes. Finalmente, implementa un método
main
que te permita probar esta clase
Implementa en Java una clase TicTacToeListener
que recoja los eventos de los usuarios al pulsar los botones que
forman el tablero del tres en raya. Esta clase implementará la interfaz
ActionListener
y tendrá dos atributos, la instancia de la
clase principal TicTacToe
a usar, porque necesitarás acceder
a sus métodos desde el escuchador, y la instancia de la etiqueta en que
se indica quién es el siguiente jugador. El constructor de esta
clase se encargará de inicializar estos dos atributos.
El método actionPerformed
que tendrás que
implementar en esta clase debe incluir la lógica siguiente:
Deshabilitar el botón que fue pulsado
Mostrar una "X" si el jugador 1 pulsó el botón y una "O" si el jugador 2 pulsó el botón.
Actualizar el valor de player
para cambiar el turno de juego
Actualizar la etiqueta del tablero para mostrar el jugador que tiene el turno y su símbolo
Implementa en la clase TicTacToe
el método
boolean isWinner(String value)
que compruebe si un jugador
ha ganado a partir del valor de su símbolo. Ten en cuenta todas las
posibles combinaciones del tres en raya para que exista un ganador.
Invoca a este método desde actionPerformed
.
En caso de que algún usuario sea ganador muestra un mensaje por pantalla
utilizando la clase JOptionPane
.