Segundo:
El programa de Lugares.
Hablas de un programa donde hay 105 lugares disponibles, que tendrían dos estados posibles: "Libre" u "Ocupado".
Para pasar de un estado a otro nos servimos de un boton que posee cada uno de los 105 lugares y además contamos con unos contadores para tener en pantalla la cantidad de lugares libres y ocupados.
Eso he creído entender. Y más o menos, simplificandolo un poco, yo lo he representado así:
Para complicar el código lo menos posible, está todo escrito en un único archivo .java que contiente todos los métodos y subclases necesarias.
Ahora lo pongo completo, listo para ser copiado, compilado y ejecutado.
Pero luego vamos a ir comentándolo por partes:
package ocupados;
import java.awt.BorderLayout;
import java.awt.Color;
import java.awt.Dimension;
import java.awt.Font;
import java.awt.GridLayout;
import java.awt.event.ActionEvent;
import java.awt.event.ActionListener;
import java.lang.reflect.InvocationTargetException;
import javax.swing.BorderFactory;
import javax.swing.BoxLayout;
import javax.swing.JButton;
import javax.swing.JFrame;
import javax.swing.JLabel;
import javax.swing.JOptionPane;
import javax.swing.JPanel;
import javax.swing.SwingUtilities;
public final class LugaresOcupados {
private PanelLugares lugares = new PanelLugares();//Panel central con los botones para elegir Lugares
private PanelContador contador = new PanelContador();//Panel superior que informa de la cantidad de Lugares ocupados
public LugaresOcupados()
{
try {
SwingUtilities.invokeAndWait(new Runnable()
{
@Override
public void run()
{
JFrame ventana = new JFrame("Lugares Ocupados");
ventana.setLayout(new BorderLayout());
ventana.add(contador, BorderLayout.NORTH);//Colocamos el Panel del Contador en parte superior de la ventana
ventana.add(lugares, BorderLayout.CENTER);//Colocamos en Panel de Lugares en parte central de la ventana
ventana.pack();
ventana.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
ventana.setLocationRelativeTo(null);
ventana.setResizable(false);
ventana.setVisible(true);
}
});
} catch (InvocationTargetException | InterruptedException e) {
e.printStackTrace();
}
}
public static void main(String[] args) {
new LugaresOcupados();
}
/**
* Esta clase crea un panel que representa un Lugar.
* Cuenta con un boton para marcar el lugar como "ocupado" o "libre"
*/
private final class Lugar extends JPanel{
private JButton boton;
private JLabel etiqueta;
private boolean libre = true;//Para saber si el Lugar esta libre o no
private int numLugar;
public Lugar(int nLugar)
{
numLugar = nLugar;
boton = new JButton("Libre");
boton.addActionListener(new PulsarBoton());
boton.setPreferredSize(new Dimension(85, 26));//Como el texto del boton cambiará, le damos un tamaño suficiente para que quepa
setBorder(BorderFactory.createLoweredBevelBorder());
setLayout(new BoxLayout(this, BoxLayout.PAGE_AXIS));
etiqueta = new JLabel("Lugar #" + numLugar);
add(etiqueta);
add(boton);
setBackground(Color.GREEN);
}
/*
* Esta clase es para definir que ocurre cuando se pulsa el boton de un Lugar
*/
private class PulsarBoton implements ActionListener
{
@Override
public void actionPerformed(ActionEvent arg0) {
if (libre)
{
libre = false;
setBackground(Color.BLUE);
etiqueta.setForeground(Color.WHITE);//Cambiamos color texto etiqueta para que sea visible sobre el fondo azul
boton.setText("Ocupado");
}
else
{
libre = true;
setBackground(Color.GREEN);
etiqueta.setForeground(Color.BLACK);//Color texto etiqueta recupera color original
boton.setText("Libre");
}
contador.actualizarContador(lugares.calcularOcupados());//Actualizamos el contador de lugares ocupados
JOptionPane.showMessageDialog(null, "Se ha seleccionado el " + etiqueta.getText() +
"\nSu estado ahora es: " + boton.getText());
}
}
}
/**
* Esta clase es para definir el Panel central que albergará los 105 Lugares disponibles.
*/
private final class PanelLugares extends JPanel{
private Lugar[] lugares = new Lugar[105];//Array para agrupar los 105 objetos de la clase Lugar
public PanelLugares()
{
setLayout(new GridLayout(15, 7, 10, 10));//15 filas, 7 columnas = 105 celdas para colocar "Lugares"
//Los valores 10, 10 establece la separacion en pixeles entre los 105 paneles Lugar
//Creamos los 105 lugares y los vamos añadiendo al array y tambien al panel central
for (int i = 0; i < 105; i++)
{
lugares[i] = new Lugar(i);//Añadimos al array
add(lugares[i]);// Añadimos al Panel
}
}
/*
* Este metodo recorre todo el array preguntando a cada Lugar si esta libre o no.
* Si no lo está, es decir, que está ocupado, lo contará y luego devuelve el total de
* Lugares ocupados.
*/
public int calcularOcupados()
{
int ocupados = 0;
for (int i = 0; i < lugares.length; i++)
{
if (lugares[i].libre == false)
ocupados++;
}
return ocupados;
}
}
/**
* Esta clase crea el Panel superior donde estará el contador de lugares ocupados.
* Este contador se irá actualizando cada vez que se pulse un boton.
*/
private final class PanelContador extends JPanel{
private JLabel contadorLugares;
public PanelContador()
{
JLabel texto = new JLabel("Lugares ocupados: ");
texto.setFont(new Font("Tahoma", Font.PLAIN, 40));//Aumentamos tamaño del texto para mayor visibilidad
contadorLugares = new JLabel("0");
contadorLugares.setFont(new Font("Tahoma", Font.PLAIN, 40));
contadorLugares.setForeground(Color.RED);
add(texto);
add(contadorLugares);
}
/*
* Este metodo recibe el numero de lugares ocupados actualmente y
* con este valor actualiza la etiqueta que usamos como contador.
*/
public void actualizarContador(int valor)
{
contadorLugares.setText(String.valueOf(valor));
}
}
}
Esa ventana (JFrame) esta dividida en dos paneles (JPanel).
El panel superior es el "contador", que son tan solo dos etiquetas en la que una se irá actualizando cada vez que se pulse un boton indicando la cantidad de lugares ocupados.
El panel de abajo, ocupa practicamente toda la ventana y en realidad contiene 105 subpaneles, cada uno con su etiqueta y botón.
Cada uno de ellos es una clase llamada
Lugar que hereda de JPanel.
Esta es la clase más importante del programa, ya que es donde ocurre toda "la acción" del programa.
private final class Lugar extends JPanel{
private JButton boton;
private JLabel etiqueta;
private boolean libre = true;//Para saber si el Lugar esta libre o no
private int numLugar;
public Lugar(int nLugar)
{
numLugar = nLugar;
boton = new JButton("Libre");
boton.addActionListener(new PulsarBoton());
boton.setPreferredSize(new Dimension(85, 26));//Como el texto del boton cambiará, le damos un tamaño suficiente para que quepa
setBorder(BorderFactory.createLoweredBevelBorder());
setLayout(new BoxLayout(this, BoxLayout.PAGE_AXIS));
etiqueta = new JLabel("Lugar #" + numLugar);
add(etiqueta);
add(boton);
setBackground(Color.GREEN);
}
/*
* Esta clase es para definir que ocurre cuando se pulsa el boton de un Lugar
*/
private class PulsarBoton implements ActionListener
{
@Override
public void actionPerformed(ActionEvent arg0) {
if (libre == true)
{
libre = false;
setBackground(Color.BLUE);
etiqueta.setForeground(Color.WHITE);//Cambiamos color texto etiqueta para que sea visible sobre el fondo azul
boton.setText("Ocupado");
}
else
{
libre = true;
setBackground(Color.GREEN);
etiqueta.setForeground(Color.BLACK);//Color texto etiqueta recupera color original
boton.setText("Libre");
}
contador.actualizarContador(lugares.calcularOcupados());//Actualizamos el contador de lugares ocupados
JOptionPane.showMessageDialog(null, "Se ha seleccionado el " + etiqueta.getText() +
"\nSu estado ahora es: " + boton.getText());
}
}
}
Tiene 4 atributos:
Una etiqueta con el número del Lugar.
Un boton para realizar la accion de "Ocupar" o "Liberar"
Un boolean para que este panel pueda indicarnos si actualmente esta Libre o no.
Un int con su numero correspondiente, que puede ir del 0 al 104 (105 posiciones)
Su contructor recibirá el numero de Lugar que le corresponda y lo guardará en su atributo
numLugarDespues inicializa el botón con el texto "Libre" ya que de inicio todos los Lugares estarán libres.
Le agrega un ActionListener, declarado un poco más abajo, que contiene el código que se ha de ejecutar cuando se pulse el boton.
Además le establece al boton un tamaño "Preferido". Esto es porque al ocupar un Lugar, cambiaremos el texto del boton de "Libre" a "Ocupado". El boton, al arrancar el programa, su panel adopta un tamaño adecuado para el texto "Libre", pero luego el boton no tendrá sitio en su panel para crecer y que quepa el texto "Ocupado".
Si previamente le establecemos un tamaño adecuado, reservará espacion para poder crecer cuando el texto cambie a "Ocupado"
Al panel le ponemos un Borde, esto no es importante, es puramente estético.
Luego le indicamos el Layout, es decir, como queremos que se repartan los objetos dentro del panel. EL layout que le estamos indicando es que los coloque en vertical, es decir, primero la etiqueta y debajo el boton.
Despues inicializamos la etiqueta, incluyendo en su texto el numero de posición que hemos recibido por el contructor de la clase.
Añadimos los dos elementos al panel y listo.
Luego escribimos el código del ActionListener.
En tu codigo usabas el metodo isSelected() para preguntar en cada acción, a todos los botones si estan seleccionados o no.
Yo no lo he hecho así, aquí directamente al pulsar un boton, le preguntamos a su panel si esta libre, valiéndonos del atributo booleano.
Si está "libre", es que ha de cambiar a "ocupado", por lo cuál cambiará su color, cambiará el texto del botón, hará que el contador (el contador pertenece a otra clase) se actualice y mostrará un mensaje en pantalla.
Si está "ocupado", cambiará a "libre", haciendo los cambios necesarios para ello.
Bien, la siguiente clase que encontramos es
PanelLugares, que básicamente lo que hace es albergar en formar de cuadrícula, los 105 paneles correspondientes a la clase
Lugarprivate final class PanelLugares extends JPanel{
private Lugar[] lugares = new Lugar[105];//Array para agrupar los 105 objetos de la clase Lugar
public PanelLugares()
{
setLayout(new GridLayout(15, 7, 10, 10));//15 filas, 7 columnas = 105 celdas para colocar "Lugares"
//Los valores 10, 10 establece la separacion en pixeles entre los 105 paneles Lugar
//Creamos los 105 lugares y los vamos añadiendo al array y tambien al panel central
for (int i = 0; i < 105; i++)
{
lugares[i] = new Lugar(i);//Añadimos al array
add(lugares[i]);// Añadimos al Panel
}
}
/*
* Este metodo recorre todo el array preguntando a cada Lugar si esta libre o no.
* Si no lo está, es decir, que está ocupado, lo contará y luego devuelve el total de
* Lugares ocupados.
*/
public int calcularOcupados()
{
int ocupados = 0;
for (int i = 0; i < lugares.length; i++)
{
if (lugares[i].libre == false)
ocupados++;
}
return ocupados;
}
}
Aqui hace aparicion el array.
Es el único atributo de esta clase, un array de 105 elementos para los 105 paneles.
Lo útil de tenerlo en un array, como veremos luego, es que para actualizar el contador podemos recorrer el array y contar los paneles que indican que NO están libres (libre == false), es decir, que ESTAN OCUPADOS y una vez contados, actualizar el contador con este dato.
Esto podría haberse hecho sin usar una array, bastaría con incrementar o decrementar una variable int y habría sido más eficiente de hecho. Pero como es un ejercicio e interesa practicar con un array, pues aqui es donde mejor viene utilizarlo.
El constructor lo primero que hace es aplicar un Layout que divide el panel en 105 celdas, repartidas en 15 filas de 7 columnas. Con esta distribución conseguimos que todas las filas tengan el mismo numero de paneles.
Otra división valida sería 5 filas de 21 columnas, pero queda demasiado ancho y la ventana se saldría de los límites de la mayoría de pantallas de ordenador.
Este layout además establece una separacion de 10 pixeles, tanto en horizontal como en vertical, entre cada panel. Esto también es una decisión estética.
Despues, el constructor usa un bucle for para recorrer el array y crear un nuevo panel "Lugar", con su correspondiente numero de posición, y lo guarda en el array al mismo tiempo que lo añade a nuestro panel "PanelLugares".
Luego tenemos un método que como dije antes, mediante un bucle revisa que paneles "Lugar" están "ocupados, los cuenta y devuelve el valor.
Este valor lo recibirá el
PanelContador, que es la clase que comentamos ahora:
private final class PanelContador extends JPanel{
private JLabel contadorLugares;
public PanelContador()
{
JLabel texto = new JLabel("Lugares ocupados: ");
texto.setFont(new Font("Tahoma", Font.PLAIN, 40));//Aumentamos tamaño del texto para mayor visibilidad
contadorLugares = new JLabel("0");
contadorLugares.setFont(new Font("Tahoma", Font.PLAIN, 40));
contadorLugares.setForeground(Color.RED);
add(texto);
add(contadorLugares);
}
/*
* Este metodo recibe el numero de lugares ocupados actualmente y
* con este valor actualiza la etiqueta que usamos como contador.
*/
public void actualizarContador(int valor)
{
contadorLugares.setText(String.valueOf(valor));
}
}
Consta de dos etiquetas, una con un texto que no cambia nunca. La otra es la que muestra el valor numérico de espacios ocupados e irá cambiando cada vez que se pulse un boton.
En el constructor he aumentado el tamaño de su fuente para que sea más visible.
Luego cuenta con un pequeño método que es el que se encarga de recibir el valor de lugares ocupados (del cual informa la clase PanelLugares) y modifica la etiqueta con el nuevo valor.
Estas tres clases que hemos comentado, son "clases anidadas" en la clase principal
LugaresOcupadosEstán anidadas, es decir, declaradas dentro de la clase principal y no por separado, para facilitar la comunicación entre ellas.
Lo elegante sería escribirlas por separado, pero entonces tendríamos ciertas dificultades que habría que complicar un poco el código para solventarlos y ahora no merece la pena.
La clase LugaresOcupados:
public final class LugaresOcupados {
private PanelLugares lugares = new PanelLugares();//Panel central con los botones para elegir Lugares
private PanelContador contador = new PanelContador();//Panel superior que informa de la cantidad de Lugares ocupados
public LugaresOcupados()
{
try {
SwingUtilities.invokeAndWait(new Runnable()
{
@Override
public void run()
{
JFrame ventana = new JFrame("Lugares Ocupados");
ventana.setLayout(new BorderLayout());
ventana.add(contador, BorderLayout.NORTH);//Colocamos el Panel del Contador en parte superior de la ventana
ventana.add(lugares, BorderLayout.CENTER);//Colocamos en Panel de Lugares en parte central de la ventana
ventana.pack();
ventana.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
ventana.setLocationRelativeTo(null);
ventana.setResizable(false);
ventana.setVisible(true);
}
});
} catch (InvocationTargetException | InterruptedException e) {
e.printStackTrace();
}
}
public static void main(String[] args) {
new LugaresOcupados();
}
Es muy sencilla. Tiene dos atributos:
- un objeto de la clase
PanelLugares (que a su vez contiene los 105 objetos de la clase
Lugar)
- un objeto de la clase
PanelContadorSu contructor crea la ventana (JFrame) principal y le da un Layout un tanto especial.
Este layout digamos que crea una zona central llamada CENTER rodeada de 4 zonas de menor tamaño en cada punto cardinal NORTH, SOUTH, EAST y WEST.
En este caso uso la zona NORTE para colocar el panelContador ya que necesita poco espacio.
La zona CENTRO que es la de mayor tamaño, colocamos el panel de Lugares.
EL resto de zonas (SUR, ESTE y OESTE) no van a contener nada, así que no aparecerán en la ventana.
Luego le decimos a la ventana que calcule el tamaño necesario para albergar todos los paneles:
ventana.pack();
Le decimos que cierre el programa por completo cuando se pulse el boton X de la ventana:
ventana.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
Le decimos que muestre la ventana centrada en la pantalla:
ventana.setLocationRelativeTo(null);
Bloqueamos el tamaño de la ventana para que el usuario no lo modifique:
ventana.setResizable(false);
Y hacemos la ventana visible:
ventana.setVisible(true);
Luego su metodo main() no hace nada especial, tan solo instanciar la clase principal para que la ventana y todo su contenido se ponga en marcha.
Lamento hacer posts taaaan laaarrgos. Hay casi más explicaciones que código, no te preocupes si te duermes antes de leerlo todo xD... leelo en varios intentos .
Tan solo decir que hay muchas formas de realizar un programa. Esta es mi solución, no tiene por qué ser la mejor. Solo espero que sea lo más entendible posible y te ayude en algo.
Lo importante aquí no es solo aprender a valerse de arrays cuando hay que operar con muchos elementos de un mismo tipo.
Si no también que hay que intentar aplicar la POO al máximo posible y conseguir que cada elemento sea autosuficiente.
En lugar de hacer un programa que al pulsar un boton, se pregunte al resto de los 104 botones si ha sido pulsado o no...
hay que conseguir que al pulsar un boton, este haga lo que tenga que hacer por su propia cuenta sin tener que consultarle al resto de botones.