Java - Ayuda, pequeño problema eliminando JPanel en tiempo real

 
Vista:
Imágen de perfil de Kabuto
Val: 3.428
Oro
Ha mantenido su posición en Java (en relación al último mes)
Gráfica de Java

Ayuda, pequeño problema eliminando JPanel en tiempo real

Publicado por Kabuto (1381 intervenciones) el 22/01/2019 02:17:57
Hola a todos.
Por practicar y tal... estoy haciendo un programita en Java en el cual dentro de un JScrollPane, añado y elimino JPanels en tiempo real.
Añadir puedo añadir los que quiera, se van colocando uno debajo de otro.
Cada JPanel tiene un boton "Eliminar" para eliminar este Jpanel en cuestión.

Pues bien, por algún extraño motivo.... si hay más de 3 JPanels mostrándose , sí puedo ir eliminandolos...
Pero cuando ya solo hay dos, o bien solo hay uno...no se eliminan..
Bueno, en realidad, creo que si se eliminan, solo que parece que no se actualiza correctamente el JSCrollPane y siguen mostrándose en pantalla, aunque en realidad si han sido eliminados.

Y es extraño que esto solo falle cuando hay uno o dos paneles.
Como digo, si hay más paneles si se eliminan y se actualiza la pantalla correctamente.
Luego, en principio el código que uso es correcto, pero no entiendo porque falla solo en esa situacion.

El código que uso, tanto para añadir paneles como para eliminar, es en realidad muy sencillo, sin complicaciones.
- Hago un removeAll()
- Genero nuevos JPanel a partir de los datos contenidos en un ArrayList y los voy añadiendo al contenedor correspondiente.
- Por último hago un .revalidate()

Este es el método que se encarga de este proceso:
1
2
3
4
5
6
7
8
9
10
11
12
public void actualizarEncargos(ArrayList<Encargo> encargos) {
    contenedor.removeAll();
    System.out.println("Encargos.size() --> " + encargos.size()); //Info para debug
    for (int i = 0; i < encargos.size(); i++)
    {
        PanelEncargo temp = new PanelEncargo(encargos.get(i));
        temp.botonEliminar.addActionListener(new AccionEliminarPanel(i));
        contenedor.add(Box.createRigidArea(new Dimension(0, 20)), -1);
        contenedor.add(temp);
    }
    contenedor.revalidate();
}

Os paso un zip el código fuente completo, pero antes, sabiendo que es extremadamente complicado interpretar el código escrito por otra persona, me gustaría explicaros en que consiste mi programa, lo más resumidamente posible sin daros demasiado la paliza.

Intento hacer un programita muy simple para gestionar Encargos de productos que soliciten clientes.
En mi trabajo, a veces los clientes nos piden que traigamos cierto producto, o bien que les avisemos si por casualidad llega un determinado producto que se está retrasando.
Actualmente, estos encargos se anotan a mano en una agenda, pero muchas veces olvidamos repasar dicha agenda como es debido, o se pierde durante varios dias, o nadie se acuerda de quien tomó nota de un determindo encargo,..

En fin, un desastre.
Y se me ha ocurrido, con mis escasos conocimientos, intentar hacer un programita en Java para gestionar esto desde el ordenador, de forma mas visual.
La idea es que se guarden y muestren en pantalla los datos referentes a cada encargo que nos pidan.
Cada encargo está representado por un JPanel rectangular y se irán colocando uno debajo del otro. Además el color del JPanel varía según la fecha de antigüedad, mostrandose en rojo los encargos muy antiguos y que por lo tanto requieren algún tipo de accion inmediata.

Bueno en fín. Para ello, uso principalmente:
- una clase Encargo con los datos de los encargos que se pidan.
- una clase PanelEncargo, que hereda de JPanel y recibe un objeto Encargo para mostrar sus datos en pantalla.

Estoy siguiendo una estructura Modelo-Vista-Controlador:

Así que en la clase Modelo tengo un ArrayList para almacenar los Encargos que se vayan realizando.
Más adelante, pondré funcionalidad para guardar y recuperar este ArrayList serializandolo en un archivo.

La Vista, obviamente, se encarga de construir el JSCrollPane donde se van añadiendo y quitando los objetos PanelEncargo. Este JScrollPane se pasa luego a la clase principal, que es donde esta el JFrame que muestra lo que va haciendo la Vista.

Y el Controlador, pues nada, hace de nexo entre la Vista y el Modelo.

Hay alguna clase más, como un JDialog para crear Encargos, pero no es relevante ahora.

Bien, paso a explicar como se crean los PanelEncargo.
Lo que hago es que cojo del Modelo el ArrayList de Encargos.
Por cada Encargo, creo un PanelEncargo.
Cada PanelEncargo, muestra los datos del Encargo y tiene dos botones:
- uno para Modificar (que ahora mismo no funciona, no tiene ninguna implementacion todavia)
- otro para Eliminar.

Al boton Eliminar de cada PanelEncargo, le agrego este ActionListener

1
2
3
4
5
6
7
8
9
10
11
12
13
14
private class AccionEliminarPanel implements ActionListener {
 
    int numeroPanel;
 
    public AccionEliminarPanel(int nPanel) {
        numeroPanel = nPanel;
    }
 
    @Override
    public void actionPerformed(ActionEvent e) {
        System.out.println("Eliminando panel numero: " + numeroPanel); //Info para debug
        controlador.eliminarEncargo(numeroPanel);
    }
}

Ese atributo numeroPanel, lo uso para guardar el numero de indice que ocupa cada Encargo en el ArrayList.
Es decir, el Encargo en la posicion 0, su correspondiente PanelEncargo tiene un boton Eliminar "personalizado" que ya sabe que cuando se pulse, hay que decirle al Controlador que de orden al Modelo de eliminar el Encargo en la posicion 0.

Una vez eliminado, el Controlador le enviará a la Vista el ArrayList que acaba de cambiar.
Y la Vista eliminará TODOS los PanelEncargo presentes en pantalla, volverá a crear nuevos con los datos actuales del ArrayList de Encargos y "revalidará" el JScrollPane con los nuevos componentes actuales.

Bien, todo este proceso, no se si es eficiente, pero en principio es eficaz.
Y además es una forma fácil de conseguir que todos los PanelEncargo tengan los botones "Eliminar" actualizados. Porque cada vez que borro un Encargo, los indices del ArrayList varían, y por tanto los botones "Eliminar" se han de actualizar o de lo contrario podrían dar ordenes de eliminar Encargos que ya no existen o que ahora pertenece a otros PanelEncargo distintos.

Bueno, como digo, esto "funciona" bien cuando hay muchos PanelEncargo mostrándose en pantalla.
Pero, no se porque, cuando solo hay uno o dos, la Vista no se actualiza correctamente.
Los componentes son eliminados, es decir, el .removeAll() hace su cometido. Pero "visualmente" no se percibe y se quedan en pantalla unos PanelEncargo que en realidad ya no están.

El ArrayList de Encargos se actualiza correctamente, con unos System.out voy controlando como su tamaño aumenta o decrece en cada accion.
Y los botones "Eliminar" también se actualizan correctamente, pues también controlo cuando los pulso que indice van a dar orden de eliminar en el ArrayLsit, y siempre es correcto.

Así que el problema tiene que ser alguna pijada del Swing que yo desconozca, o yo que se...

Os adjunto un zip con el código fuente. No es necesario que os estudieis el código completo.
Quien pueda/quiera ayudarme que centre su neuronas en la clase Vista, en su método actualizarEncargos() que es quien añade/elimina los PanelEncargo.
Y es muy fácil reproducir el fallo. Basta generar tres o cuatro encargos usando la opción del menú superior.
Veréis que se van mostrando correctamente en pantalla.

Cuanto tengais tres o cuatro creados, empezad a eliminar.
Veréis que se van eliminando correctamente, hasta cuando quedan dos en pantalla.
Entonces fallará y no se eliminarán, bueno, más bien no se va a mostrar que SI han sido eliminados.

PD:
No me juzguéis por lo fea que es la interfaz
Está en fase alpha todavía jeje, cuando funcione la haré un poco más bonita.
Aunque no tengo mucho arte yo para esto, así que seguirá siendo fea, solo que entonces no tendré excusa.

Gracias a quien al menos me haya leído, y un saludo.
Valora esta pregunta
Me gusta: Está pregunta es útil y esta claraNo me gusta: Está pregunta no esta clara o no es útil
1
Responder

Ayuda, pequeño problema eliminando JPanel en tiempo real

Publicado por Tom (1831 intervenciones) el 22/01/2019 09:10:15
Cuando estas cosas fallan es porque no es la manera correcta de hacerlas :)
En todo caso, añade un repaint() a tu revalidate(). Eso debería bastar para que el contenido de tu JScrollpane se actualice correctamente.
Valora esta respuesta
Me gusta: Está respuesta es útil y esta claraNo me gusta: Está respuesta no esta clara o no es útil
3
Comentar
Imágen de perfil de Kabuto
Val: 3.428
Oro
Ha mantenido su posición en Java (en relación al último mes)
Gráfica de Java

Ayuda, pequeño problema eliminando JPanel en tiempo real

Publicado por Kabuto (1381 intervenciones) el 22/01/2019 11:46:38
Jo... mira que le dí vueltas al asunto y en ningún momento se me ocurrió probar con un simple y puñetero repaint()..

Pues parece que con esto se soluciona.., vaya tela ja ja.., muchísimas gracias Tom.

Seguramente existirá alguna forma más avanzada, elegante y eficiente de elaborar lo que estoy haciendo, pero me tengo que apañar con lo poquito que se y voy descubriendo poco a poco...
De hecho, en realidad al principio pensaba que esto de quitar y poner JPanel en tiempo real me iba a resultar más difícil, pero no, salvo este pequeño tropiezo...

Gracias otra vez, y un saludo.
Valora esta respuesta
Me gusta: Está respuesta es útil y esta claraNo me gusta: Está respuesta no esta clara o no es útil
0
Comentar

Ayuda, pequeño problema eliminando JPanel en tiempo real

Publicado por Tom (1831 intervenciones) el 22/01/2019 15:54:33
Le he echado un vistazo muy rápido a tu código. Creo que los "novatos" en swing os liais un poco tratando de aplicar el patrón MVC. Los componentes swing ya implementan su propia forma de MVC. O sea, un componente swing ya gestiona la comunicación model-view-controller.
En mi opinión, tratar de diseñar toda tu aplicación swing con un modelo MVC estricto es un error, ya que puedes aplicar el patrón a cada componente (swing casi te obliga a hacerlo así) y no necesariamente a la aplicación en conjunto.
Valora esta respuesta
Me gusta: Está respuesta es útil y esta claraNo me gusta: Está respuesta no esta clara o no es útil
0
Comentar