Autor Tema: Ejercicio resuelto Java inventario con clase Stack en Netbeans push pop pregunta  (Leído 1831 veces)

Sergio Perez

  • Sin experiencia
  • *
  • APR2.COM
  • Mensajes: 14
    • Ver Perfil
Hola buenas tardes de momento tengo este código:


Clase con el main

Código: [Seleccionar]
package clases;

import ventanas.Inventario;

public class main {

   
    public static void main(String[] args) {
       
        Inventario e = new Inventario();
        e.setVisible(true);
       
    }
}


Clase Preguntas

Código: [Seleccionar]
package clases;

public class Preguntas {
   
    String[] preguntas = {
        "1. Torpe o entumecido", "2. Acalorado", "3. Con temblor en las piernas",
        "4. Incapaz de relajarse", "5. Con temor a que ocurra lo peor","6. Mareado, o que se le va la cabeza",
        "7. Con latidos del corazón fuertes y acelerados","8. Inestable","9. Atemorizado o asustado",
        "10. Nervioso","11. Con sensación de bloqueo","12. Con temblores en las manos",
        "13. Inquieto, inseguro","14. Con miedo a perder el control","15. Con sensación de ahogo",
        "16. Con temor a morir","17. Con miedo","18. Con problemas digestivos",
        "19. Con desvanecimientos","20. Con rubor facial","21. Con sudores, frios o calientes"
    };
   
   public String getPregunta(int posicion){
        return preguntas[posicion];
    }
   
}


Clase Respuestas

Código: [Seleccionar]
package clases;

import java.util.StringTokenizer;

public class Respuestas {

    String[] respuestas =  {
      "[0]En Absoluto","[1]Levemente","[2]Moderadamente","[3]Severamente"
    };
   
    public String getRespuesta(int posicion){
        return respuestas[posicion];
    }
   
    public String[] separar(String cadena, String separador){
        StringTokenizer token = new StringTokenizer(cadena,separador);
       
        String[] a = new String[21];
        int i = 0;
       
        while(token.hasMoreTokens()){
            a[i] = token.nextToken(); // obtenido el token
            i++;
        }
       
        return a;
    }
   
    public String[] setRespuestas(int posicion){
        String s1 = respuestas[posicion];
        String[] s2 = separar(s1,",");
        return s2;
    }
   
}

Y lo quería usar en un Frame

Solo quiero saber cómo puedo aplicarle la clase Stack, ya que en respuesta no necesito ya el

Citar
[0]En Absoluto,[1]Levemente,[2]Moderadamente,[3]Severamente


por que los puedo poner con un Label, si no que sea String repuestas[21] y lea números enteros en el TextField desde teclado para que al terminar el Test me dé un promedio.

Y de ser posible que el botón "Avanzar" y "Regresar" funcionen con "push" y "pop" del stack  :-[
« Última modificación: 12 de Marzo 2022, 14:25 por Ogramar »

Kabuto

  • Moderador Global
  • Experto
  • *******
  • Mensajes: 989
    • Ver Perfil
Re: Inventario con "Stack" en Netbeans
« Respuesta #1 en: 03 de Septiembre 2021, 00:50 »
Vale, creo que entiendo lo que pides.

A ver, esta sería mi clase Preguntas
Es un array con los String de cada pregunta.
Uso una variable como  índice para apuntar a la pregunta que actualmente se estará mostrando en la interfaz.
Dispone de unos métodos para apuntar a la pregunta siguiente o a la anterior, siempre controlando que el índice se mantenga dentro de los límites.
Otro método retorna la pregunta a la que se está apuntando actualmente.

Código: [Seleccionar]
public class Preguntas {

private final String[] PREGUNTAS = {
"1. Torpe o entumecido", "2. Acalorado", "3. Con temblor en las piernas",
"4. Incapaz de relajarse", "5. Con temor a que ocurra lo peor","6. Mareado, o que se le va la cabeza",
"7. Con latidos del corazón fuertes y acelerados","8. Inestable","9. Atemorizado o asustado",
"10. Nervioso","11. Con sensación de bloqueo","12. Con temblores en las manos",
"13. Inquieto, inseguro","14. Con miedo a perder el control","15. Con sensación de ahogo",
"16. Con temor a morir","17. Con miedo","18. Con problemas digestivos",
"19. Con desvanecimientos","20. Con rubor facial","21. Con sudores, frios o calientes"
};

public int actual; //Índice de la pregunta que actualmente se estará mostrando

public Preguntas() {
actual = 0; //Comienza por la primera pregunta
}

public void siguiente() {
//Incrementamos índice
actual++;
//Comprobamos que no hemos rebasado el límite del array de PREGUNTAS
if (actual == PREGUNTAS.length)
actual--;
}

public void anterior() {
//Decrementamos índice
actual--;
//Comprobamos que no estamos en índice negativo
if (actual < 0)
actual = 0;
}

public String getActual() {
return PREGUNTAS[actual];
}
}

Luego he creado una clase llamada PanelInventario que hereda de JPanel.
Va a modelar la interfaz que se ve en pantalla, pero además va a llevar todo el peso de la aplicación.
Así que entre sus atributos va a tener un objeto de la clase Preguntas que se encargará de suministrar las preguntas a un JLabel.
Tendrá también un Stack<Integer> para almacenar las respuestas que el usuario vaya dando en el JTextField.

Al JTextField, le he añadido un KeyListener para que haga un filtrado de lo que se teclea. Está programado para aceptar solo un dígito, y solo uno, entre 0 y 3.
Cualquier otro carácter será ignorado y NO se podrá introducir dos o más caracteres, solo uno.
De esta manera nos aseguramos que el Stack solo recibirá respuestas válidas.

Los botones de Regresar y Avanzar son los que hacen pop() y push(), respectivamente.
Cuando se pulsa Avanzar se hace push() con el valor introducido y se pasa a la siguiente pregunta.
Si se pulsa Regresar, se hace pop() para sacar el último valor introducido y se retrocede a la pregunta anterior.
En ambos casos se comprueba que botones conviene que se activen o desactiven.

Por ejemplo, si ya estamos en la última pregunta, el botón Avanzar se desactiva y se activa el botón Terminar, pero el de Regresar permanece activo.
Si regresamos, el botón Avanzar se reactiva y el de Terminar vuelve a quedar desactivado.

El botón de Terminar lo que hace es "popear" todos los valores del Stack y sumarlos para calcular un promedio que se mostrará en pantalla.
Tras esto, el inventario queda reseteado para poder repetir de nuevo el cuestionario.

Código: [Seleccionar]
public class PanelInventario extends JPanel {

private final String[] RESPUESTAS = {"[0]En Absoluto", "[1]Levemente",
"[2]Moderadamente","[3]Severamente"};

private Preguntas preguntas;
private Stack<Integer> respuestas;

private JLabel jlPregunta;
private JTextField jtRespuesta;
private JButton btRegresar;
private JButton btAvanzar;
private JButton btTerminar;

public PanelInventario() {
iniciarComponentes();

setLayout(new BoxLayout(this, BoxLayout.Y_AXIS));
setBorder(BorderFactory.createCompoundBorder(
BorderFactory.createTitledBorder("Inventario de Ansiedad de Beck(BAI)"),
BorderFactory.createEmptyBorder(25, 25, 25, 25)));

JPanel pnPregunta = new JPanel();
pnPregunta.setLayout(new FlowLayout(FlowLayout.LEFT));
pnPregunta.add(jlPregunta);

JPanel pnRespuestas = new JPanel();
pnRespuestas.setLayout(new GridLayout(1, 4, 20, 0));
for (String resp: RESPUESTAS)
pnRespuestas.add(new JLabel(resp));
pnRespuestas.setBorder(BorderFactory.createEmptyBorder(10, 25, 10, 0));

JPanel pnRespuesta = new JPanel();
pnRespuesta.add(new JLabel("Responde [0-3]: "));
pnRespuesta.add(jtRespuesta);

JPanel pnSeleccion = new JPanel();
pnSeleccion.setLayout(new GridLayout(1,2));
JPanel pnReg = new JPanel();
pnReg.add(btRegresar);
JPanel pnAva = new JPanel();
pnAva.add(btAvanzar);
pnSeleccion.add(pnReg);
pnSeleccion.add(pnAva);

JPanel pnTer = new JPanel();
pnTer.add(btTerminar);

add(pnPregunta);
add(new JSeparator());
add(pnRespuestas);
add(new JSeparator());
add(pnRespuesta);
add(pnSeleccion);
add(pnTer);
}

private void iniciarComponentes() {
preguntas = new Preguntas();
respuestas = new Stack<Integer>();
jlPregunta = new JLabel(preguntas.getActual());
jlPregunta.setFont(new Font("Verdana", Font.BOLD, 20));
jtRespuesta = new JTextField(4);
jtRespuesta.addKeyListener(new SoloNumeros());
btRegresar = new JButton("<< Regresar");
btRegresar.addActionListener(new AccionRegresar());
btRegresar.setEnabled(false);
btAvanzar = new JButton("Avanzar >>");
btAvanzar.addActionListener(new AccionAvanzar());
btTerminar = new JButton("Terminar Test");
btTerminar.addActionListener(new AccionTerminar());
btTerminar.setEnabled(false);
}

/*
* Hace que el campo de texto únicamente pueda aceptar
* un único digito entre 0 y 3
*/
private class SoloNumeros implements KeyListener{
@Override
public void keyTyped(KeyEvent e) {
char caracter = e.getKeyChar();
switch (caracter) {
case '0':
case '1':
case '2':
case '3':
//Aceptable, pero solo si el campo esta vacío
if (!jtRespuesta.getText().isEmpty())
e.consume(); //Cosumimos, no queremos más caracteres.
else
jtRespuesta.setText(String.valueOf(caracter)); //Dígito aceptado
default:
e.consume(); //Consumimos, no es un dígito entre 0 y 3
}
}
@Override
public void keyPressed(KeyEvent e) {}
@Override
public void keyReleased(KeyEvent e) {}
}

private class AccionRegresar implements ActionListener {
@Override
public void actionPerformed(ActionEvent e) {
//Extraemos última respuesta y la colocamos en pantalla
jtRespuesta.setText(respuestas.pop().toString());
//Recuperamos pregunta anterior
preguntas.anterior();
jlPregunta.setText(preguntas.getActual());
//Comprobamos que el boton avanzar queda activado
btAvanzar.setEnabled(true);
//El botón terminar queda obligatoriamente desactivado
btTerminar.setEnabled(false);
//El boton regresar, se desactiva si estamos en la primera
//pregunta, es decir, si el stack esta vacío
btRegresar.setEnabled(!respuestas.isEmpty());
jtRespuesta.requestFocus();
}
}

private class AccionAvanzar implements ActionListener {
@Override
public void actionPerformed(ActionEvent e) {
//Recogemos respuesta, si es que la hay
if (jtRespuesta.getText().isBlank())
//No hay
JOptionPane.showMessageDialog(null, "Ha de dar una respuesta",
"Responder", JOptionPane.WARNING_MESSAGE);
else {
//Sí hay respuesta, la guardamos en el Stack
respuestas.push(Integer.parseInt(jtRespuesta.getText()));
//Comprobamos si el stack ya está completo
if (respuestas.size() == 20) {
//Completo, se desactiva el botón avanzar y se activa terminar
btAvanzar.setEnabled(false);
btTerminar.setEnabled(true);
btTerminar.requestFocus();
}
else {//No está completo
//Siguiente pregunta
preguntas.siguiente();
jtRespuesta.setText(null);
jlPregunta.setText(preguntas.getActual());
//Nos aseguramos que el botón regresar está activo
btRegresar.setEnabled(true);
jtRespuesta.requestFocus();
}
}

}
}

private class AccionTerminar implements ActionListener {
@Override
public void actionPerformed(ActionEvent e) {
//Calculamos el promedio
int suma = 0;
//Vamos vaciando el stack y sumando sus valores
while(!respuestas.isEmpty())
suma += respuestas.pop();

float promedio = suma / 21f;

//Mostramos
JOptionPane.showMessageDialog(null, String.format("Promedio: %.2f",promedio),
"Terminar Test", JOptionPane.INFORMATION_MESSAGE);
//Reinicia panel
preguntas.actual = 0;
jlPregunta.setText(preguntas.getActual());
jtRespuesta.setText(null);
btTerminar.setEnabled(false);
btAvanzar.setEnabled(true);
btRegresar.setEnabled(false);
jtRespuesta.requestFocus();
}
}

}

Por último, una clase Main, que no tiene nada de especial.
Tan solo modela el JFrame principal, muestra un objeto de PanelInventario, que es quien hace todo el trabajo importante, y pone la aplicación en marcha.

Código: [Seleccionar]
public class Main extends JFrame{

public Main() {

JPanel pnPrincipal = new JPanel();
pnPrincipal.add(new PanelInventario());
pnPrincipal.setBorder(BorderFactory.createEmptyBorder(20, 20, 20, 20));
setContentPane(pnPrincipal);
setTitle("Inventario con Stack");
setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
pack();
setLocationRelativeTo(null);
setVisible(true);
}

public static void main(String[] args) {
SwingUtilities.invokeLater(new Runnable() {
@Override
public void run() {
new Main();
}
});
}
}

No he hecho ninguna clase Respuestas. Podría haberse hecho, y que gestionara ella el Stack de respuestas.
Pero requiere de tan poca funcionalidad (hacer push y pop) que lo he puesto directamente todo en la clase PanelInventario, para ir más rápido.
Habría quedado más elegante, eso sí.


Creo que con los comentarios que tiene el código se entiende. Cualquier duda solo tienes que preguntar.
O si hay algo que debiera ser distinto, coméntalo por aquí.

Un saludo.
NO respondo dudas por mensaje privado
Publicando vuestras dudas en el foro público conseguimos:
- Que más gente aporte respuestas mejores o complementarias.
- Que otras personas puedan aprender de vuestras dudas.

Mejor en PÚBLICO que en privado. Gracias

 

Sobre la educación, sólo puedo decir que es el tema más importante en el que nosotros, como pueblo, debemos involucrarnos.

Abraham Lincoln (1808-1865) Presidente estadounidense.

aprenderaprogramar.com: Desde 2006 comprometidos con la didáctica y divulgación de la programación

Preguntas y respuestas

¿Cómo establecer o cambiar la imagen asociada (avatar) de usuario?
  1. Inicia sesión con tu nombre de usuario y contraseña.
  2. Pulsa en perfil --> perfil del foro
  3. Elige la imagen personalizada que quieras usar. Puedes escogerla de una galería de imágenes o subirla desde tu ordenador.
  4. En la parte final de la página pulsa el botón "cambiar perfil".