Me faltaba el quinto ejercicio.
Se me ha ocurrido hacer que cada posible respuesta, sea un panel que reciba el botón que ha de mostrar y un booleano para establecer un atributo que indique si esa respuesta es correcta o no lo es, cuando el usuario la marque.
De este modo, cada botón/opción es capaz de informar por si solo si es válido o no.
Como hay dos tipos de botones en el cuestionario,
JRadioButton y
JCheckBox, y necesito que este panel pueda construirse con ambos tipos, en el constructor pido recibir un
JToggleButton, que es la clase "madre" de estos botones, que son "hermanos".
Así gracias al polimorfismo (aplicar POO es crucial en todo) no tengo que escribir esta clase dos veces, una para cada tipo de botón. Me basta con una.
private class PanelRespuesta extends JPanel {
private boolean esCorrecta;
private JToggleButton boton;
/*
* JToggleButton es un "ancestro" de JcheckBox
* y de JRadioButton.
* Así este panel se puede construir con ambos elementos
*/
public PanelRespuesta(JToggleButton comp, boolean correcta) {
boton = comp;
esCorrecta = correcta; //En el constructor establecemos si es respuesta correcta o no
setLayout(new FlowLayout(FlowLayout.LEFT));
add(boton);
setBorder(BorderFactory.createLoweredBevelBorder());
}
}
Cada pregunta tiene una serie de respuestas a elegir, por tanto, voy a crear otra clase que agrupe los paneles respuesta asociados a una pregunta en concreto.
Así las respuestas de una misma pregunta están relacionadas. Además les asocio el JLabel que mostrará si se ha acertado o no la respuesta correcta.
Esta clase tendrá un método muy importante. Será el método que compruebe si se ha escogido la respuesta correcta.
La comprobación será distinta dependiendo de si se trata un botón radio o un checkbox, ya que en este último se permite la selección multiple. Así que no habrá solo una respuesta correcta, pueden haber varias y hay que comprobar que estén todas marcadas para indicar que la respuesta es correcta.
Este método lo he hecho boolean para que, a parte de comprobar si es correcta y actualizar el JLabel de "valor, quiero que informe de si el usuario ha elegido algo en esa serie de respuestas o no.
Si no ha seleccionado nada, quiero saberlo para lanzar un mensaje de advertencia en pantalla.
private class SerieRespuestas{
private PanelRespuesta[] respuestas;
private JLabel valor;
public SerieRespuestas(PanelRespuesta[] resp) {
respuestas = resp;
valor = new JLabel("Valor");
valor.setFont(new Font("Verdana", Font.BOLD, 14));
//Si son botones radio, los agrupamos
if (respuestas[0].boton instanceof JRadioButton) {
ButtonGroup grupo = new ButtonGroup();
for (int i = 0; i < respuestas.length; i++)
grupo.add(respuestas[i].boton);
}
}
/**
* Comprueba si ha habido respuesta seleccionada, y si es correcta.
* La etiqueta valor indicará el resultado, si es que se ha seleccionado algo.
* El metodo informa con true/false si ha habido alguna seleccion o no,
* para saberlo y poder mostrar un mensaje al usuario.
*
* @return True si ha habido selección (da igual si correcta o no),
* False si no se ha marcado ninguna respuesta.
*/
public boolean comprobarRespuesta() {
boolean haySeleccion = false; //Controlar si se ha seleccionado algo
boolean acertada = false; //Controla si la selección es correcta
/*
* Comprobar las respuestas es distinto si las respuestas son
* JRadioButton (solo una posible seleccion)
* o si son JCheckBox (varias selecciones posibles)
*/
if (respuestas[0].boton instanceof JRadioButton) {
for (PanelRespuesta respuesta: respuestas) {
if (respuesta.boton.isSelected()) {
haySeleccion = true;
acertada = respuesta.esCorrecta;
}
}
}
else { //Son JCheckBox
/*
* Empezamos considerandola como acertada.
* Se considerará erronea si:
* - Encontramos una respuesta correcta que NO esta seleccionada
* - Encontramos una respuesta seleccionadao que NO es correcta.
*/
acertada = true;
for (PanelRespuesta respuesta: respuestas) {
if (respuesta.esCorrecta && !respuesta.boton.isSelected())
acertada = false;
if (respuesta.boton.isSelected()) {
haySeleccion = true;
if (!respuesta.esCorrecta)
acertada = false;
}
}
}
//Actualizamos etiqueta valor, si es que ha habido seleccion
if (haySeleccion) {
if (acertada) {
valor.setText("Cierto");
valor.setForeground(Color.BLUE);
}
else {
valor.setText("Error");
valor.setForeground(Color.RED);
}
}
return haySeleccion; //Acertada o no, informamos si el usuario ha seleccionado algo
}
}
Una vez tenemos una clase que agrupa las respuestas de una misma pregunta, necesito otra clase que maquete un panel con las series de respuestas.
Esta clase va a hacer el trabajo "pesado" al iniciar el programa.
Va a crear todos los "paneles-respuesta", los agrupará en distintas "series" y ya entonces los maquetará en el panel.
Para que quede todo más o menos alineado, usaré un GridLayout.
Además tiene un método que al invocarlo, se recorren todas las "series de respuestas" para que estas comprueben si se ha seleccionado alguna respuesta, y si además son correctas.
Si alguna "serie" informa de que el usuario no ha elegido ninguna respuesta, será cuando se muestre el mensaje de advertencia
private class PanelRespuestas extends JPanel {
private SerieRespuestas[] series;
public PanelRespuestas() {
//construiremos todas las series respuestas
series = new SerieRespuestas[4];
//1era serie de respuestas
PanelRespuesta[] serie1 = new PanelRespuesta[4];
serie1[0] = new PanelRespuesta(new JRadioButton("58"), false);
serie1[1] = new PanelRespuesta(new JRadioButton("34"), false);
serie1[2] = new PanelRespuesta(new JRadioButton("31"), true);
serie1[3] = new PanelRespuesta(new JRadioButton("40"), false);
//2da serie
PanelRespuesta[] serie2 = new PanelRespuesta[4];
serie2[0] = new PanelRespuesta(new JRadioButton("Marte"), false);
serie2[1] = new PanelRespuesta(new JRadioButton("Jupiter"), true);
serie2[2] = new PanelRespuesta(new JRadioButton("Tierra"), false);
serie2[3] = new PanelRespuesta(new JRadioButton("Saturno"), false);
//3ra serie
PanelRespuesta[] serie3 = new PanelRespuesta[4];
serie3[0] = new PanelRespuesta(new JRadioButton("ola"), false);
serie3[1] = new PanelRespuesta(new JRadioButton("desición"), true);
serie3[2] = new PanelRespuesta(new JRadioButton("hay"), false);
serie3[3] = new PanelRespuesta(new JRadioButton("ahí"), false);
//4ta serie
PanelRespuesta[] serie4 = new PanelRespuesta[4];
serie4[0] = new PanelRespuesta(new JCheckBox("Sagaz"), true);
serie4[1] = new PanelRespuesta(new JCheckBox("Listo"), true);
serie4[2] = new PanelRespuesta(new JCheckBox("Lerdo"), false);
serie4[3] = new PanelRespuesta(new JCheckBox("Astuto"), true);
//Agrupamos las series en el array
series[0] = new SerieRespuestas(serie1);
series[1] = new SerieRespuestas(serie2);
series[2] = new SerieRespuestas(serie3);
series[3] = new SerieRespuestas(serie4);
//Listo, maquetamos el panel
setLayout(new GridLayout(4, 5));
for (int i = 0; i < 4; i++) {
for (int j = 0; j < 4; j++)
add(series[i].respuestas[j]);
JPanel pnValor = new JPanel();
pnValor.add(series[i].valor);
//pnValor.setBorder(BorderFactory.createLoweredBevelBorder());
pnValor.setBorder(BorderFactory.createEtchedBorder());
add(pnValor);
}
}
public void comprobarValores() {
boolean todasSeleccionadas = true;
for (SerieRespuestas serie: series)
if (!serie.comprobarRespuesta())
todasSeleccionadas = false;
if (!todasSeleccionadas)
JOptionPane.showMessageDialog(null, "Hay preguntas sin contestar",
"Comprobar respuestas", JOptionPane.WARNING_MESSAGE);
}
}
Ya tenemos un panel con todas las respuestas agrupadas y maquetadas. Y con el código necesario para comprobar por ellas mismas si se ha elegido la correcta.
Falta otro panel con las preguntas. Este será muy sencillo:
private class PanelPreguntas extends JPanel {
public PanelPreguntas() {
setLayout(new GridLayout(4,1,5,10));
FlowLayout fluyeIzq = new FlowLayout(FlowLayout.RIGHT);
JPanel preg1 = new JPanel(fluyeIzq);
preg1.add(new JLabel("4 * 6 + 3 + 2 * 2 = "));
JPanel preg2 = new JPanel(fluyeIzq);
preg2.add(new JLabel("Nombre del quinto planeta:"));
JPanel preg3 = new JPanel(fluyeIzq);
preg3.add(new JLabel("Palabra con error ortográfico:"));
JPanel preg4 = new JPanel(fluyeIzq);
preg4.add(new JLabel("Sinómimos de inteligente:"));
add(preg1);
add(preg2);
add(preg3);
add(preg4);
}
}
Y ya está. En el JFrame principal juntaremos ambos paneles y añadiremos otro en la parte inferior con el botón "mostrar".
La acción de este botón es muy sencilla y se la he declarado "en línea" en el constructor, pues todo lo que tiene que hacer es pedirle al panel de respuestas que invoque el método que revisa las series de respuestas.
Así que para una sola línea no he creado un clase ActionListener a parte como sí hice en los ejercicios anteriores.
Dejo a continuación el código completo.

public class Preguntas extends JFrame{
private PanelPreguntas pnPreguntas;
private PanelRespuestas pnRespuestas;
private JButton btMostrar;
public Preguntas() {
pnPreguntas = new PanelPreguntas();
pnRespuestas = new PanelRespuestas();
btMostrar = new JButton("Mostrar");
btMostrar.addActionListener(new ActionListener() {
@Override
public void actionPerformed(ActionEvent e) {
pnRespuestas.comprobarValores();
}
});
setLayout(new BorderLayout());
JPanel pnCentro = new JPanel();
pnCentro.add(pnPreguntas);
pnCentro.add(pnRespuestas);
add(pnCentro, BorderLayout.CENTER);
JPanel pnSur = new JPanel();
pnSur.add(btMostrar);
add(pnSur, BorderLayout.SOUTH);
setTitle("Cuestionario");
setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
pack();
setLocationRelativeTo(null);
setVisible(true);
}
/**
* Modela un pequeño panel que simplemente contendrá
* un JCheckBox o un JRadioButton, siendo una respuesta escogible
* por el usuario.
* Un atributo boolean indicará si es una respuesta correcta o incorrecta.
*/
private class PanelRespuesta extends JPanel {
private boolean esCorrecta;
private JToggleButton boton;
/*
* JToggleButton es un "ancestro" de JcheckBox
* y de JRadioButton.
* Así este panel se puede construir con ambos elementos
*/
public PanelRespuesta(JToggleButton comp, boolean correcta) {
boton = comp;
esCorrecta = correcta; //En el constructor establecemos si es respuesta correcta o no
setLayout(new FlowLayout(FlowLayout.LEFT));
add(boton);
setBorder(BorderFactory.createLoweredBevelBorder());
}
}
/**
* Esta clase agrupa toda una serie de respuestas asociada
* a una pregunta, y añade la etiqueta de "valor" que informa
* cuando la respuesta ha sido correcta o no.
* Incorpora un método para comprobar si se ha seleccionado
* respuesta correcta
*/
private class SerieRespuestas{
private PanelRespuesta[] respuestas;
private JLabel valor;
public SerieRespuestas(PanelRespuesta[] resp) {
respuestas = resp;
valor = new JLabel("Valor");
valor.setFont(new Font("Verdana", Font.BOLD, 14));
//Si son botones radio, los agrupamos
if (respuestas[0].boton instanceof JRadioButton) {
ButtonGroup grupo = new ButtonGroup();
for (int i = 0; i < respuestas.length; i++)
grupo.add(respuestas[i].boton);
}
}
/**
* Comprueba si ha habido respuesta seleccionada, y si es correcta.
* La etiqueta valor indicará el resultado, si es que se ha seleccionado algo.
* El metodo informa con true/false si ha habido alguna seleccion o no,
* para saberlo y poder mostrar un mensaje al usuario.
*
* @return True si ha habido selección (da igual si correcta o no),
* False si no se ha marcado ninguna respuesta.
*/
public boolean comprobarRespuesta() {
boolean haySeleccion = false; //Controlar si se ha seleccionado algo
boolean acertada = false; //Controla si la selección es correcta
/*
* Comprobar las respuestas es distinto si las respuestas son
* JRadioButton (solo una posible seleccion)
* o si son JCheckBox (varias selecciones posibles)
*/
if (respuestas[0].boton instanceof JRadioButton) {
for (PanelRespuesta respuesta: respuestas) {
if (respuesta.boton.isSelected()) {
haySeleccion = true;
acertada = respuesta.esCorrecta;
}
}
}
else { //Son JCheckBox
/*
* Empezamos considerandola como acertada.
* Se considerará erronea si:
* - Encontramos una respuesta correcta que NO esta seleccionada
* - Encontramos una respuesta seleccionadao que NO es correcta.
*/
acertada = true;
for (PanelRespuesta respuesta: respuestas) {
if (respuesta.esCorrecta && !respuesta.boton.isSelected())
acertada = false;
if (respuesta.boton.isSelected()) {
haySeleccion = true;
if (!respuesta.esCorrecta)
acertada = false;
}
}
}
//Actualizamos etiqueta valor, si es que ha habido seleccion
if (haySeleccion) {
if (acertada) {
valor.setText("Cierto");
valor.setForeground(Color.BLUE);
}
else {
valor.setText("Error");
valor.setForeground(Color.RED);
}
}
return haySeleccion; //Acertada o no, informamos si el usuario ha seleccionado algo
}
}
/**
* Este panel contendrá todas las series de respuestas
*/
private class PanelRespuestas extends JPanel {
private SerieRespuestas[] series;
public PanelRespuestas() {
//construiremos todas las series respuestas
series = new SerieRespuestas[4];
//1era serie de respuestas
PanelRespuesta[] serie1 = new PanelRespuesta[4];
serie1[0] = new PanelRespuesta(new JRadioButton("58"), false);
serie1[1] = new PanelRespuesta(new JRadioButton("34"), false);
serie1[2] = new PanelRespuesta(new JRadioButton("31"), true);
serie1[3] = new PanelRespuesta(new JRadioButton("40"), false);
//2da serie
PanelRespuesta[] serie2 = new PanelRespuesta[4];
serie2[0] = new PanelRespuesta(new JRadioButton("Marte"), false);
serie2[1] = new PanelRespuesta(new JRadioButton("Jupiter"), true);
serie2[2] = new PanelRespuesta(new JRadioButton("Tierra"), false);
serie2[3] = new PanelRespuesta(new JRadioButton("Saturno"), false);
//3ra serie
PanelRespuesta[] serie3 = new PanelRespuesta[4];
serie3[0] = new PanelRespuesta(new JRadioButton("ola"), false);
serie3[1] = new PanelRespuesta(new JRadioButton("desición"), true);
serie3[2] = new PanelRespuesta(new JRadioButton("hay"), false);
serie3[3] = new PanelRespuesta(new JRadioButton("ahí"), false);
//4ta serie
PanelRespuesta[] serie4 = new PanelRespuesta[4];
serie4[0] = new PanelRespuesta(new JCheckBox("Sagaz"), true);
serie4[1] = new PanelRespuesta(new JCheckBox("Listo"), true);
serie4[2] = new PanelRespuesta(new JCheckBox("Lerdo"), false);
serie4[3] = new PanelRespuesta(new JCheckBox("Astuto"), true);
//Agrupamos las series en el array
series[0] = new SerieRespuestas(serie1);
series[1] = new SerieRespuestas(serie2);
series[2] = new SerieRespuestas(serie3);
series[3] = new SerieRespuestas(serie4);
//Listo, maquetamos el panel
setLayout(new GridLayout(4, 5));
for (int i = 0; i < 4; i++) {
for (int j = 0; j < 4; j++)
add(series[i].respuestas[j]);
JPanel pnValor = new JPanel();
pnValor.add(series[i].valor);
//pnValor.setBorder(BorderFactory.createLoweredBevelBorder());
pnValor.setBorder(BorderFactory.createEtchedBorder());
add(pnValor);
}
}
public void comprobarValores() {
boolean todasSeleccionadas = true;
for (SerieRespuestas serie: series)
if (!serie.comprobarRespuesta())
todasSeleccionadas = false;
if (!todasSeleccionadas)
JOptionPane.showMessageDialog(null, "Hay preguntas sin contestar",
"Comprobar respuestas", JOptionPane.WARNING_MESSAGE);
}
}
private class PanelPreguntas extends JPanel {
public PanelPreguntas() {
setLayout(new GridLayout(4,1,5,10));
FlowLayout fluyeIzq = new FlowLayout(FlowLayout.RIGHT);
JPanel preg1 = new JPanel(fluyeIzq);
preg1.add(new JLabel("4 * 6 + 3 + 2 * 2 = "));
JPanel preg2 = new JPanel(fluyeIzq);
preg2.add(new JLabel("Nombre del quinto planeta:"));
JPanel preg3 = new JPanel(fluyeIzq);
preg3.add(new JLabel("Palabra con error ortográfico:"));
JPanel preg4 = new JPanel(fluyeIzq);
preg4.add(new JLabel("Sinómimos de inteligente:"));
add(preg1);
add(preg2);
add(preg3);
add(preg4);
}
}
public static void main(String[] args) {
SwingUtilities.invokeLater(new Runnable() {
@Override
public void run() {
new Preguntas();
}
});
}
}