A ver, hagamos un planteamiento nuevo.
Se mostrarán las 72 butacas en pantalla y el usuario podrá hacer click en ellas para reservar, o liberar reservas.
Previamente, el usuario se ha de registar si es nuevo o identificarse si ya está registrado.
Podemos hacer que las butacas cambien entre tres colores distintos.
VERDE = Butaca disponible
ROJO = Butaca reservada, por un cliente DISTINTO al que está logueado en ese momento
AZUL = Butaca reservada, por el cliente ACTUALMENTE logueado.
Así, el cliente no puede interactuar con las casillas rojas.
Solo podrá clickar las verdes para reservar, o las azules para deshacer la reserva.
Podemos estructurar el proyecto en tres packages: vista, modelo y controlador
Un cuarto package podría contener las dos imágenes que usaremos para mostrar las butacas.
Veamos primero el "Modelo".
Primero, la clase
Butaca.
Su atributo "tipo" lo controlamos con un simple int que puede variar entre 0(general) y 1(VIP)
De esta forma, nos sirve como índice para seleccionar directamente en un array uno de los dos precios según el tipo.
Con un String le construiremos un id, que será un número de butaca. Este id servirá para mostrarlo en pantalla pero también para hacer que cada Butaca sea única.
Luego un boolean para saber si está libre u ocupada.
Por constructor recibe el id y el tipo. El boolean se inicia como true, ya que al principio del programa todas las butacas estarán libres.
package modelo;
public class Butaca {
//Precios prefijados según tipo de Butaca
private final float[] PRECIOS = {8.5f, 10f};
private String id;
private int tipo; //Tipo Butaca. 0 = General / 1 = VIP
private boolean estaLibre; //True está libre, False está ocupada
public Butaca(String id, int tipo) {
this.id = id;
this.tipo = tipo;
estaLibre = true;
}
public String getId() {
return id;
}
public int getTipo() {
return tipo;
}
public boolean estaLibre() {
return estaLibre;
}
public float getPrecio() {
return PRECIOS[tipo];
}
public void setLibre(boolean esLibre) {
estaLibre = esLibre;
}
@Override
public boolean equals(Object obj) {
if (obj instanceof Butaca) {
Butaca otraBt = (Butaca) obj;
return id.equals(otraBt.id);
}
else
return false;
}
}
Veamos ahora la clase
Cliente.
Puesto que nos dicen que los clientes pueden hacer reservas, pero también anularlas, lo más cómodo y rápido para saber que butacas pertenecen a cada cliente es hacer que la clase
Cliente tenga un ArrayList con las butacas que vaya reservando.
Así podemos añadir o quitar reservas.
Otros atributo será un número de cedula y su nombre completo para poder dirigirnos a él.
Entre sus métodos habrá uno que se encargue de calcular el importe total de sus reservas
package modelo;
import java.util.ArrayList;
public class Cliente {
private String cedula;
private String nombreCompleto;
public ArrayList<Butaca> reservas;
public Cliente(String cedula, String nombreCompleto) {
this.cedula = cedula;
this.nombreCompleto = nombreCompleto;
reservas = new ArrayList<Butaca>();
}
public String getCedula() {
return cedula;
}
public String getNombreCompleto() {
return nombreCompleto;
}
public void hacerReserva(Butaca bt) {
reservas.add(bt);
}
public void liberarReserva(Butaca bt) {
reservas.remove(bt);
}
public float calculaImporte() {
float total = 0;
for(Butaca bt: reservas)
total += bt.getPrecio();
return total;
}
}
Bueno, en lo que respecta a la parte del "Controlador", podemos crear una clase para gestionar las dos clases del "Modelo" que acabamos de ver.
Esta clase tendrá un ArrayList para poder añadir clientes y una matriz con las 42 butacas divididas entre 6 filas y 7 columnas.
En su constructo, al inicializar cada objeto Butaca, le generamos un identificador concatenando el valor de su fila con el de su columna.
Además, decidiremos cuáles van a ser VIP. Podemos hacer que las centrales, las que están en las filas 2 y 3, sean VIP y el resto normales.
Esto ya que cada uno decida como quiere hacerlo.
Tiene un método para añadir un nuevo cliente, y otro para buscar un cliente mediante su número de cédula y retornarlo en caso de que exista.
También hay un getter para retornar la matriz de butacas al completo. Esto facilitará las cosas luego.
package controlador;
import java.util.ArrayList;
import modelo.*;
public class GestorCine {
private ArrayList<Cliente> clientes;
private Butaca[][] butacas;
public GestorCine() {
clientes = new ArrayList<Cliente>();
butacas = new Butaca[7][6];
//Inicializamos butacas. Filas centrales serán VIP
for(int i = 0; i < 7; i++)
for(int j = 0; j < 6; j++)
if(i == 2 || i == 3) //Filas centrales
butacas[i][j] = new Butaca(String.format("%d%d",i,j), 1);//VIP
else
butacas[i][j] = new Butaca(String.format("%d%d",i,j),0);//General
}
public void nuevoCliente(Cliente cli) {
clientes.add(cli);
}
public Cliente compruebaCliente(String cedula) {
//Comprobamos por cedula si ya existe este cliente
for (Cliente cl: clientes) {
if(cl.getCedula().equals(cedula))
return cl;
}
//Bucle finaliza sin retorna nada, no existe este cliente
return null;
}
public Butaca[][] getButacas() {
return butacas;
}
}
Sobre la "Vista"...
Queremos mostrar la imagen de una butaca, que será distinta según su tipo.
Además queremos rodear cada imagen con un borde de color para distinguir fácilmente las butacas ocupadas de las que están libres.
Vale, pues podemos crear una clase que herede de JPanel y llamarla
PanelButaca.
Cada uno de estos paneles, recibirá una referencia a una de las 72
Butacas con la que estará asociada.
Así sabrá qué número de butaca ha de mostrar, cuál color ha de ser su borde en cada momento y también qué imagen ha de mostrar según el tipo de butaca.
El color del borde también lo vamos a gestionar como un atributo más.
Así que a este JPanel le vamos a añadir dos JLabel en vertical.
Uno mostrará el número de butaca y el otro la imagen jpg que corresponda a su tipo de butaca.
package vista;
import java.awt.Color;
import java.awt.Font;
import javax.swing.BorderFactory;
import javax.swing.BoxLayout;
import javax.swing.ImageIcon;
import javax.swing.JLabel;
import javax.swing.JPanel;
import modelo.Butaca;
public class PanelButaca extends JPanel {
//Según tipo butaca, se cargará una de las dos imagenes
private final String[] RUTAS_IMAGEN = new String[] {
"img/butaca_general.jpg", "img/butaca_VIP.jpg"
};
private Butaca butaca;
private Color color;
public PanelButaca(Butaca butaca) {
this.butaca = butaca;
color = Color.GREEN;
setBackground(Color.WHITE);
setLayout(new BoxLayout(this, BoxLayout.Y_AXIS));
JLabel idButaca = new JLabel(butaca.getId());
idButaca.setFont(new Font("Consolas", Font.BOLD, 18));
idButaca.setForeground(Color.BLUE);
idButaca.setBorder(BorderFactory.createEmptyBorder(3, 3, 0, 20));
add(idButaca);
JLabel lbButaca = new JLabel(new ImageIcon(
this.getClass().getClassLoader().getResource(RUTAS_IMAGEN[butaca.getTipo()])));
lbButaca.setBorder(BorderFactory.createEmptyBorder(0, 10, 0, 10));
add(lbButaca);
cambiaBorde(color);
setToolTipText(setTip());
}
public Butaca getButacaAsociada() {
return butaca;
}
public Color getColor() {
return color;
}
private String setTip() {
if (butaca.estaLibre())
return "Precio: " + butaca.getPrecio();
else
return "Ocupada";
}
public void cambiaBorde(Color color) {
this.color = color;
setBorder(BorderFactory.createMatteBorder(10,10,10,10, color));
setToolTipText(setTip());
}
}
Ahora necesitamos otro panel, que contenga a estos 72
PanelButaca, como si fuera la sala de cine, así que se puede llamar
PanelSalaTambién tendrá una matriz de 7x6 para contener los 72
PanelButaca.
Estos
PanelesButaca requieren de las otras 72
Butacas del "Modelo" para inicializarse.
Así que por el constructor le haremos llegar la matriz del "Modelo" para poder crear la matriz de la "Vista.
Por constructor también recibirá un MouseListener que será quien le diga a cada
PanelButaca cómo ha de comportarse cuando el usuario haga click en ellas.
De momento, le vamos a dar dos métodos.
Uno de ellos recibe como argumento el cliente logueado y se va a encargar de marcar en azul las butacas reservadas por ESTE cliente.
Para ello cotejaremos el ArrayList de reservas que posee este cliente para saber cuáles hay que marcar en azul.
El otro método, se ejecutará cuando el cliente cierre sesión, y lo que hará será repasar todas las butacas y marcarlas en verde o rojo.
El color azul solo estará presente cuando haya un cliente con la sesión abierta.
package vista;
import java.awt.Color;
import java.awt.GridLayout;
import java.awt.event.MouseListener;
import javax.swing.BorderFactory;
import javax.swing.JPanel;
import modelo.*;
public class PanelSala extends JPanel {
private PanelButaca[][] butacas;
public PanelSala(Butaca[][] listaButacas, MouseListener escuchador) {
butacas = new PanelButaca[7][6];
setLayout(new GridLayout(7,6,2,4));
for (int i = 0; i < 7; i++)
for (int j = 0; j < 6; j++) {
butacas[i][j] = new PanelButaca(listaButacas[i][j]);
butacas[i][j].addMouseListener(escuchador);
add(butacas[i][j]);
}
setBorder(BorderFactory.createCompoundBorder(
BorderFactory.createEmptyBorder(10, 10, 10, 10),
BorderFactory.createBevelBorder(0)));
}
public void mostrarReservasCliente(Cliente cl) {
//Comprobamos cuáles butacas estan reservadas por este cliente
//para marcarlas en azul
for (int i = 0; i < 7; i++)
for (int j = 0; j < 6; j++)
if (cl.reservas.contains(butacas[i][j].getButacaAsociada()))
butacas[i][j].cambiaBorde(Color.BLUE);
}
public void actualizaColorButacas() {
for (int i = 0; i < 7; i++)
for (int j = 0; j < 6; j++)
if (butacas[i][j].getButacaAsociada().estaLibre())
butacas[i][j].cambiaBorde(Color.GREEN);
else
butacas[i][j].cambiaBorde(Color.RED);
}
}
Bien, ¿y como hará login un cliente?
Podemos añadir un panel en la parte inferior de la interfaz con un único botón que servirá para hacer Login y Logout.
Irá acompañado de un JLabel que irá mostrando en todo momento cuántas reservas tiene hechas el cliente y el importe total que suman.
package vista;
import javax.swing.BorderFactory;
import javax.swing.BoxLayout;
import javax.swing.JButton;
import javax.swing.JLabel;
import javax.swing.JPanel;
public class PanelLogin extends JPanel {
public JButton btLogin;
public JLabel mensaje;
public PanelLogin() {
btLogin = new JButton("Login");
JPanel pnBoton = new JPanel();
pnBoton.add(btLogin);
mensaje = new JLabel("Pulsa el botón para iniciar sesión");
JPanel pnMensaje = new JPanel();
pnMensaje.add(mensaje);
setLayout(new BoxLayout(this, BoxLayout.X_AXIS));
add(pnBoton);
add(pnMensaje);
setBorder(BorderFactory.createCompoundBorder(
BorderFactory.createEmptyBorder(5, 10, 5, 10),
BorderFactory.createBevelBorder(1)));
}
}
En la parte superior, pondremos otro panel con el nombre del cine.
No voy a invertir mucho esfuerzo en esto, con algo simple ya basta.
package vista;
import java.awt.Color;
import java.awt.Font;
import javax.swing.BorderFactory;
import javax.swing.JLabel;
import javax.swing.JPanel;
public class PanelTitulo extends JPanel {
public PanelTitulo(String titulo) {
JLabel lbTitulo = new JLabel(titulo);
lbTitulo.setForeground(Color.MAGENTA);
lbTitulo.setBackground(Color.WHITE);
lbTitulo.setOpaque(true);
lbTitulo.setFont(new Font("Impact", Font.BOLD, 40));
lbTitulo.setBorder(BorderFactory.createCompoundBorder(
BorderFactory.createEtchedBorder(Color.MAGENTA, Color.CYAN),
BorderFactory.createEmptyBorder(20, 45, 20, 45)));
add(lbTitulo);
}
}
Bien, con estos elementos ya podemos poner en marcha una versión preliminar del programa.
Para ello nos vamos al "controlador" y creamos una clase Main, la principal, que heredará de JFrame. Mostrará la interfaz, comunicará unas clases con otras y aquí escribiremos los Listener que podamos necesitar para cada función del programa.
Sus atributos serán los principales paneles, el gestor del cine y también un objeto Cliente que contendrá al cliente que haya iniciado sesión.
Si no hay ninguna sesión abierta, tendrá valor null.
Escribiremos un ActionListener para el botón de iniciar sesión.
Ahí se puede ver como tendrá dos comportamientos distintos, según si hay una sesión abierta o está cerrada.
También un MouseListener que será él que le haremos llegar a cada uno de los 72
PanelesButacaMediante este Listener es con el que el usuario podrá hacer reservas con solo clickar una butaca.
Por tanto, el comportamiento dependerá de si clicka en una casilla verde, azul, o roja.
Es en esta clase donde hay que prestar más atención, pues es donde se decide la mayor parte de la lógica del programa.
package controlador;
import java.awt.BorderLayout;
import java.awt.Color;
import java.awt.event.ActionEvent;
import java.awt.event.ActionListener;
import java.awt.event.MouseEvent;
import java.awt.event.MouseListener;
import javax.swing.JFrame;
import javax.swing.JOptionPane;
import javax.swing.SwingUtilities;
import modelo.*;
import vista.*;
public class Main extends JFrame {
private GestorCine gestor;
private PanelSala sala;
private PanelLogin login;
private Cliente cliente;
public Main() {
gestor = new GestorCine();
sala = new PanelSala(gestor.getButacas(), new AccionReservar());
login = new PanelLogin();
login.btLogin.addActionListener(new AccionLogin());
cliente = null;
setLayout(new BorderLayout());
add(new PanelTitulo("Cinema Poli"), BorderLayout.NORTH);
add(sala, BorderLayout.CENTER);
add(login, BorderLayout.SOUTH);
setTitle("Cinema Poli");
setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
pack();
setLocationRelativeTo(null);
setVisible(true);
}
private class AccionLogin implements ActionListener {
@Override
public void actionPerformed(ActionEvent e) {
if (login.btLogin.getText().equals("Login")) {
String cedula = JOptionPane.showInputDialog("Introduzca su cedula:");
cliente = gestor.compruebaCliente(cedula);
if (cliente == null) {
String nombre = JOptionPane.showInputDialog("Complete el nuevo registros con su nombre completo:");
cliente = new Cliente(cedula, nombre);
gestor.nuevoCliente(cliente);
}
JOptionPane.showMessageDialog(null, "Bienvenido " + cliente.getNombreCompleto() +
"\nLas casillas ROJAS indican que la butaca está reservada por otro cliente."
+ "\nLas VERDES indican que están disponibles."
+ "\nLas AZULES son las que tienes reservadas a tu nombre.");
sala.mostrarReservasCliente(cliente);
login.mensaje.setText(String.format("Hola %s. Tienes %d reservas. Import total: %.2f",
cliente.getNombreCompleto(), cliente.reservas.size(), cliente.calculaImporte()));
login.btLogin.setText("Logout");
}
else {
int confirma = JOptionPane.showConfirmDialog(null, "¿Cerrar sesión?", "Logout", JOptionPane.YES_NO_OPTION);
if (confirma == JOptionPane.YES_OPTION) {
cliente = null;
sala.actualizaColorButacas();
login.mensaje.setText("Pulsa el botón para iniciar sesión");
login.btLogin.setText("Login");
}
}
}
}
private class AccionReservar implements MouseListener {
@Override
public void mouseClicked(MouseEvent e) {
if (cliente == null)
JOptionPane.showMessageDialog(null, "Primero ha de iniciar sesión con el boton Login");
else {
PanelButaca bt = (PanelButaca)e.getSource();
if (bt.getButacaAsociada().estaLibre()) {
bt.getButacaAsociada().setLibre(false);
bt.cambiaBorde(Color.BLUE);
cliente.hacerReserva(bt.getButacaAsociada());
login.mensaje.setText(String.format("Hola %s. Tienes %d reservas. Importe total: %.2f",
cliente.getNombreCompleto(), cliente.reservas.size(), cliente.calculaImporte()));
}
else {
//Si no está libre, solo actuamos si su color es azul, porque es una reserva del cliente
//Al hacer click en su reserva, la butaca quedará liberada
if (bt.getColor().equals(Color.BLUE)) {
bt.getButacaAsociada().setLibre(true);
bt.cambiaBorde(Color.GREEN);
cliente.liberarReserva(bt.getButacaAsociada());
login.mensaje.setText(String.format("Hola %s. Tienes %d reservas. Importe total: %.2f",
cliente.getNombreCompleto(), cliente.reservas.size(), cliente.calculaImporte()));
}
}
}
}
@Override
public void mousePressed(MouseEvent e) {}
@Override
public void mouseReleased(MouseEvent e) {}
@Override
public void mouseEntered(MouseEvent e) {}
@Override
public void mouseExited(MouseEvent e) {}
}
public static void main(String[] args) {
SwingUtilities.invokeLater(new Runnable() {
@Override
public void run() {
new Main();
}
});
}
}
Si ejecutamos, veremos que podemos iniciar sesión con número de cedula.
Si somos nuevos, el sistema nos registrará.
Si ya estamos registrados, el sistema nos recuerda y nos mostrará las butacas que tenemos reservadas actualmente.
La interfaz no es bonita, pero cumple con su trabajo

Ahora faltaría añadir otro panel con botones para poder consultar las otras cosas que se solicitan
se debe contar con un menú (botones) para genera y mostrar la siguiente información: porcentaje de ocupación del cine, total de dinero recaudado,
número de sillas disponibles según su tipo (general o VIP), qué sillas se encuentran disponibles, especificando la fila y la columna de cada silla en este estado.
Pero esto tendrá que ser otro día.
Por cierto, adjunto un zip con mis packages del proyecto.
Un saludo.