23. Gráficos con Swing¶
23.1 Java beans¶
Para que una clase se considere un Bean en Java, debe seguir ciertas convenciones de nomenclatura y estructura, incluyendo:
Debe tener una clase pública sin argumentos constructores. Las propiedades deben ser definidas como variables de instancia privadas con métodos "getter" y "setter" públicos que proporcionan acceso a las propiedades. Los nombres de las propiedades deben seguir ciertas convenciones de nomenclatura, como "nombrePropiedad". Los métodos de evento deben seguir ciertas convenciones de nomenclatura, como "addXxxListener" y "removeXxxListener".
Ejemplo:
public class Persona {
private String nombre;
private int edad;
public Persona() { }
public String getNombre() { return nombre; }
public void setNombre(String nombre) { this.nombre = nombre; }
public int getEdad() { return edad; }
public void setEdad(int edad) { this.edad = edad; }
// Convención de nomenclatura para eventos
public void addPersonaListener(PersonaListener listener) {
// Código para agregar un listener
}
// Convención de nomenclatura para eventos
public void removePersonaListener(PersonaListener listener) {
// Código para remover un listener
}
}
23.2 Bean data binding¶
The beans binding library simplifies and standardizes all of this. You can merely write a few lines of code to establish which properties of which components need to be kept in sync, and the beans binding library handles the rest. In the NetBeans IDE, beans binding features are integrated in the GUI Builder, so you can quickly get the behavior of your application coded soon after you have established the visual design.
Ver tutorial Oracle
23.3 Swing Data binding¶
En el contexto de Swing, Binding Data se refiere a la capacidad de conectar componentes Swing con objetos de datos utilizando la biblioteca de enlace de datos de Swing (Swing Data Binding). Esta biblioteca permite vincular componentes de Swing con objetos de datos, y se puede utilizar para implementar patrones de diseño como el patrón Modelo-Vista-Controlador (MVC) y el patrón Modelo-Vista-Presentador (MVP).
23.4 Ejemplo 1¶
Supongamos que tenemos una clase "Persona" con los atributos "nombre" y "edad":
public class Persona {
private String nombre;
private int edad;
public String getNombre() { return nombre; }
public void setNombre(String nombre) { this.nombre = nombre; }
public int getEdad() { return edad; }
public void setEdad(int edad) { this.edad = edad; }
}
Queremos construir una interfaz de usuario utilizando Swing para permitir al usuario ingresar el nombre y la edad de una persona y mostrarlos en la interfaz.
Primero, creamos una instancia de la clase Persona:
Persona persona = new Persona();
JTextField nombreTextField = new JTextField();
Bindings.bind(nombreTextField, BeanProperty.create("text"), persona, BeanProperty.create("nombre"));
Este código establece una conexión bidireccional entre el campo de texto y el atributo "nombre" de la instancia de Persona, lo que significa que cualquier cambio realizado en el campo de texto se actualizará automáticamente en el atributo "nombre" de la instancia de Persona, y viceversa.
Podemos hacer lo mismo para el campo de edad:
JTextField edadTextField = new JTextField();
Bindings.bind(edadTextField, BeanProperty.create("text"), persona, BeanProperty.create("edad")); De esta manera, hemos vinculado dos campos de texto con los atributos "nombre" y "edad" de la instancia de Persona.
Finalmente, podemos agregar los campos de texto a la interfaz de usuario y mostrarlos:
JPanel panel = new JPanel(new GridLayout(2,2));
panel.add(new JLabel("Nombre:"));
panel.add(nombreTextField);
panel.add(new JLabel("Edad:"));
panel.add(edadTextField);
JFrame frame = new JFrame();
frame.getContentPane().add(panel);
frame.pack();
frame.setVisible(true);
23.5 Ejemplo 2: enlazamos una clase y dos paneles¶
public class Pregunta {
private String enunciado;
private String respuesta;
private final PropertyChangeSupport pcs = new PropertyChangeSupport(this);
public String getEnunciado() {
return enunciado;
}
public void setEnunciado(String enunciado) {
String oldEnunciado = this.enunciado;
this.enunciado = enunciado;
pcs.firePropertyChange("enunciado", oldEnunciado, enunciado);
}
public String getRespuesta() {
return respuesta;
}
public void setRespuesta(String respuesta) {
String oldRespuesta = this.respuesta;
this.respuesta = respuesta;
pcs.firePropertyChange("respuesta", oldRespuesta, respuesta);
}
public void addPropertyChangeListener(PropertyChangeListener listener) {
pcs.addPropertyChangeListener(listener);
}
public void removePropertyChangeListener(PropertyChangeListener listener) {
pcs.removePropertyChangeListener(listener);
}
}
public class PreguntaPanel extends JPanel {
private final JTextField enunciadoField;
private final JTextField respuestaField;
public PreguntaPanel() {
setLayout(new GridLayout(2, 2));
add(new JLabel("Enunciado:"));
enunciadoField = new JTextField();
add(enunciadoField);
add(new JLabel("Respuesta:"));
respuestaField = new JTextField();
add(respuestaField);
}
public void bind(Pregunta pregunta) {
BindingGroup bindingGroup = new BindingGroup();
// Binding enunciado
Binding enunciadoBinding = Bindings.createAutoBinding(UpdateStrategy.READ_WRITE,
pregunta, BeanProperty.create("enunciado"),
enunciadoField, BeanProperty.create("text"));
bindingGroup.addBinding(enunciadoBinding);
// Binding respuesta
Binding respuestaBinding = Bindings.createAutoBinding(UpdateStrategy.READ_WRITE,
pregunta, BeanProperty.create("respuesta"),
respuestaField, BeanProperty.create("text"));
bindingGroup.addBinding(respuestaBinding);
bindingGroup.bind();
}
}
Creamos la app
public class Main {
public static void main(String[] args) {
SwingUtilities.invokeLater(() -> {
Pregunta pregunta = new Pregunta();
pregunta.setEnunciado("¿Cuál es la capital de España?");
pregunta.setRespuesta("Madrid");
PreguntaPanel preguntaPanel = new PreguntaPanel();
preguntaPanel.bind(pregunta);
JFrame frame = new JFrame("Data Binding Ejemplo");
frame.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
frame.add(preguntaPanel);
frame.pack();
frame.setLocationRelativeTo(null);
frame.setVisible(true);
});
}
}
Para conectar el PreguntaPanel con un nuevo JPanel ListaPreguntas que liste y permita seleccionar preguntas, sigue los siguientes pasos:
Crea la clase ListaPreguntas que extienda de JPanel y contenga una lista de preguntas y un JList para mostrarlas:
public class ListaPreguntas extends JPanel {
private final DefaultListModel<Pregunta> modeloPreguntas;
private final JList<Pregunta> listaPreguntas;
public ListaPreguntas() {
setLayout(new BorderLayout());
modeloPreguntas = new DefaultListModel<>();
listaPreguntas = new JList<>(modeloPreguntas);
JScrollPane scrollPane = new JScrollPane(listaPreguntas);
add(scrollPane, BorderLayout.CENTER);
}
public void agregarPregunta(Pregunta pregunta) {
modeloPreguntas.addElement(pregunta);
}
public Pregunta getPreguntaSeleccionada() {
return listaPreguntas.getSelectedValue();
}
public void addPreguntaSeleccionadaListener(ListSelectionListener listener) {
listaPreguntas.addListSelectionListener(listener);
}
}
addPreguntaSeleccionadaListener
sigue la siguiente convención de nombres:
add<NombreDelEvento>Listener
Esta convención de nombres es común en la API de Java Swing, donde encontrarás métodos como addActionListener, addItemListener, addKeyListener, addMouseListener, entre otros.
Aunque no es obligatorio seguir esta convención de nombres, es recomendable para mantener la consistencia y facilitar la comprensión del código por parte de otros desarrolladores.
y la clase app
public class Main {
public static void main(String[] args) {
SwingUtilities.invokeLater(() -> {
// Crear preguntas de ejemplo
Pregunta pregunta1 = new Pregunta();
pregunta1.setEnunciado("¿Cuál es la capital de España?");
pregunta1.setRespuesta("Madrid");
Pregunta pregunta2 = new Pregunta();
pregunta2.setEnunciado("¿Cuál es la capital de Francia?");
pregunta2.setRespuesta("París");
// Crear el panel de la lista de preguntas y agregar preguntas
ListaPreguntas listaPreguntas = new ListaPreguntas();
listaPreguntas.agregarPregunta(pregunta1);
listaPreguntas.agregarPregunta(pregunta2);
// Crear el panel de preguntas
PreguntaPanel preguntaPanel = new PreguntaPanel();
// Conectar la selección en listaPreguntas con preguntaPanel
listaPreguntas.addPreguntaSeleccionadaListener(e -> {
if (!e.getValueIsAdjusting()) {
Pregunta preguntaSeleccionada = listaPreguntas.getPreguntaSeleccionada();
preguntaPanel.bind(preguntaSeleccionada);
}
});
// Crear y configurar el marco principal
JFrame frame = new JFrame("Data Binding Ejemplo");
frame.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
JSplitPane splitPane = new JSplitPane(JSplitPane.HORIZONTAL_SPLIT, listaPreguntas, preguntaPanel);
splitPane.setDividerLocation(250);
frame.add(splitPane);
frame.pack();
frame.setLocationRelativeTo(null);
frame.setVisible(true);
});
}
}
En el siguiente fragmento de código del ejemplo anterior, se crea un ListSelectionListener utilizando una expresión lambda y se agrega al modelo de selección de listaPreguntas:
listaPreguntas.addPreguntaSeleccionadaListener(e -> {
if (!e.getValueIsAdjusting()) {
Pregunta preguntaSeleccionada = listaPreguntas.getPreguntaSeleccionada();
preguntaPanel.bind(preguntaSeleccionada);
}
});
// Esta expresión lambda es equivalente a crear un objeto ListSelectionListener anónimo e implementar el método valueChanged:
java
Copy code
ListSelectionListener listSelectionListener = new ListSelectionListener() {
@Override
public void valueChanged(ListSelectionEvent e) {
if (!e.getValueIsAdjusting()) {
Pregunta preguntaSeleccionada = listaPreguntas.getPreguntaSeleccionada();
preguntaPanel.bind(preguntaSeleccionada);
}
}
};
listaPreguntas.getSelectionModel().addListSelectionListener(listSelectionListener);
En ambos casos, el resultado es el mismo: el método valueChanged se ejecutará cuando cambie la selección en listaPreguntas. Sin embargo, el uso de expresiones lambda proporciona una sintaxis más breve y limpia, especialmente cuando se trabaja con interfaces funcionales.