JList wird nicht immer angezeigt

Larrywayn

Mitglied
Hallo mal wieder =)

Vorgeschichte:
Ich programmiere an einem recht großen Programm und habe dort bisher eine JTable eingesetzt ohne Probleme. Jedoch passt eine JList wesentlich besser in das Konzept und ist auch einfacher zu handhaben. Deshalb hab ich das Programm nun auf eine JList umgestellt.

Problem:
Das Problem ist nicht 100%ig reproduzierbar, deshalb muss man den angehängten Code eventuell mehrmals starten, bis man das Verhalten sieht.
Die JList, wird erzeugt und enthält auch alle Elemente, an der Konsolenausgabe zu erkennen. Es wird auch angezeigt, dass die Jlist sichtbar ist, aber das ist sie nicht immer.
Nach dem starten des Programms sieht man entweder die Liste, oder nix, manchmal sieht man sie auch kurz aufflackern. Fehler werden soweit keine geworfen, bzw. konnte ich nirgends einen Protokollieren.
Ich gehe stark davon aus, dass es etwas mit den 2 Threads, die an der JList arbeiten zu tun hat. Jedenfalls weißt das Verhalten und Unregelmässigkeit stark darauf hin.
Normalerweise wäre ja ein InvokeLater oder ähnliches angebracht, nur Arbeite ich ja nicht mit der Liste an sich, sondern nur mit dem Model.
Jegliche in/re/validate und repaints, sind rein spekulativ, falls es ein repaint Problem wäre.

Zusätzlich: Packt man die JList in die JScrollPane, sieht man sie eigentlich so gut wie nie.

Quelltext:
Eine zu Testzwecken erstellte Version, welche nur das Verhalten der Gui simuliert und nicht den Aufbau des original Codes komplett wiederspiegelt ^^
Java:
import java.awt.Color;
import java.awt.Component;
import java.awt.Container;
import java.awt.Graphics;
import java.awt.Rectangle;

import javax.swing.BoxLayout;
import javax.swing.DefaultListCellRenderer;
import javax.swing.DefaultListModel;
import javax.swing.Icon;
import javax.swing.JFrame;
import javax.swing.JLabel;
import javax.swing.JList;
import javax.swing.JPanel;

class Runner extends Thread {
	private DefaultListModel dlm;
	private JList list;
	private boolean begin;
	public Runner(JList list, DefaultListModel bigData) {
		this.dlm = bigData;
		this.list = list;
		this.begin = true;
	}
	public void run() {
		while(true) {
			if(begin) {
				int i = 0;
				while(i < 3) {	
					dlm.addElement(new JLabel("<html>"+i+"<br />BB<a href=\"mailto:CC\">CC</a></html>"));
					++i;
				}
				list.validate();
				begin = false;
			} else {
				System.out.println("Thread Slave -  Showing: "+list.isShowing()+" Vissible: "+list.isVisible()+" Valide: "+list.isValid());
				System.out.println("Thread Slave - Groesse Model: "+dlm.size());
				System.out.println("Thread Slave - Groesse Liste: "+list.getModel().getSize());
				//do something else;
			}
			try {
				Thread.sleep(500);
			} catch (InterruptedException e) {
				e.printStackTrace();
			}
		}
	}
}

public class Listtester {
	public static void main(String [] args) {
		JFrame f = new JFrame();	
		f.setSize(600,600);
		Container cp = new Container() {
			private static final long serialVersionUID = 1L;
			public void paint(Graphics g) {
				g.setColor(this.getBackground());
				g.fillRect(0, 0, this.getWidth(), this.getHeight());
				super.paint(g);
			}
		};
		JPanel pcp = new JPanel();
		JPanel pm = new JPanel();
		DefaultListModel dlm = new DefaultListModel();
		JList list = new JList(dlm);

		DefaultListCellRenderer r = new DefaultListCellRenderer() {
			private static final long serialVersionUID = 1L;
			public Component getListCellRendererComponent(
					JList  list,
					Object  value,
					int index,
					boolean isSelected,
					boolean cellHasFocus) {
				setComponentOrientation(list.getComponentOrientation());
				if (isSelected) {
					setBackground(list.getSelectionBackground());
					setForeground(list.getSelectionForeground());
				} else {
					setBackground(list.getBackground());
					setForeground(list.getForeground());
				}

				if (value instanceof Icon ) {
					setIcon((Icon)value);
					setText("");
				} else if(value instanceof JLabel) {
					setIcon(((JLabel) value).getIcon());
					setText(((JLabel) value).getText());
				} else {
					setIcon(null);
					setText((value == null) ? "Kein Inhalt" : value.toString());
				}

				setEnabled(list.isEnabled());
				setFont(list.getFont());
				return this;
			}
		};
		list.setCellRenderer(r);
		list.setOpaque(false);

		Rectangle rec = new Rectangle(0,0,f.getWidth()/2,f.getHeight()/2);
		cp.setLayout(null);
		cp.setBounds(rec);
		cp.setBackground(Color.green);
		pcp.setLayout(null);
		pcp.setBounds(rec);
		pcp.setOpaque(false);
		pm.setLayout(new BoxLayout(pm,BoxLayout.Y_AXIS));
		pm.setOpaque(false);
		pm.setBounds(rec);

		pcp.add(pm);
		cp.add(pcp);
		f.setContentPane(cp);
		f.setVisible(true);
		f.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);

		pm.removeAll();
		new Runner(list, dlm).start();
		//JScrollPane jsp = new JScrollPane(list);
		//jsp.setOpaque(false);
		//jsp.getViewport().setOpaque(false);
		//pm.add(jsp);
		pm.add(list);
		cp.validate();
		f.repaint();	
		while(true)
		{
			System.out.println("Thread Main -  Showing: "+list.isShowing()+" Vissible: "+list.isVisible()+" Valide: "+list.isValid());
			System.out.println("Thread Main - Groesse Model: "+dlm.size());
			System.out.println("Thread Main - Groesse Liste: "+list.getModel().getSize());
			try {
				Thread.sleep(500);
			} catch (InterruptedException e) {
				e.printStackTrace();
			}
		}
	}
}
 

bile

Mitglied
Naja... mitm Model zu arbeiten und von zwei Orten gleichzeitig reinzuschreiben und zu lesen ist so auch nicht gerade sicherer, als wenn du direkt an der Liste rumbasten würdest...
Ich nehme doch an, dass du den invokeLater() auch ausprobiert hast? - Obwohl es sicher schöner wäre das ganze Model Thread-safe zu machen. Dazu gibts ziemlich viel Literatur und früher oder später kommt man nicht drum rum, sowas hier zu lesen.
 

Larrywayn

Mitglied
Der Hauptthread arbeitet ja nicht mehr mit der JList, der startet nur das ganze GUI und die anderen Klassen/Threads übernehmen das Datenbeschaffen.
Aber ich denke, auch wenn es in einem Thread bliebe, liefe es ja trotzdem seperat in 2 Threads( Anzeige und Main).
thread safety ist mir schon klar, race condition, synchronized und co.
Deshalb wird ja auch nur in einem Thread einmalig am anfang in das Model geschrieben :)
Das InvokeLater hab ich bisher nicht ausprobiert, aus dem Grund, weil dann alle Variablen immer final sein müssen, dass immer etwas nervig, alles extra nochmal final zu machen dafür? Gibt es da eine andere Lösung eigentlich? o.o

edit: Ok mit invokeLater scheint es zu arbeiten, wieso ich das nicht selber probiert hatte, weiß ich auch nicht o.o
Aber man soll den Tag ja nicht vor dem Abend loben, da das Problem eh nur sporadisch auftrat.
 
Zuletzt bearbeitet:

bile

Mitglied
Also der hier ist sicher nicht Thread-safe:

Java:
new Runner(list, dlm).start();
        pm.add(list);
        cp.validate();
        f.repaint();

bei new Runner().start() fängst du an in die Liste zu schreiben:
Java:
int i = 0;
                while(i < 3) { 
                    dlm.addElement(new JLabel("<html>"+i+"<br />BB<a href=\"mailto:CC\">CC</a></html>"));
                    ++i;
                }
                list.validate();

da arbeiten zu 99% sicher zwei Threads mit Dingen aus der Liste...
 

Larrywayn

Mitglied
Naja macht der main-Thread aber auch, nur hab ich grad gesehen, dass dann das GUI solange blockiert ist ^^
War halt ein Denkfehler und wie gesagt nun geht es ja .. ich Depp^^
Dabei hab ich das schon in zig Programmen gemacht und im selben Programm ist es auch zu hauf schon genutzt.. das kommt davon wenn man ein paar Wochen nix macht :D
Das mit dem Final ist auch nur, wenn man Variablen außerhalb deklariert, aber die kann man der Methode, welche das invokeLater dann hat, dann direkt als final übergeben.. praktisch =)
Aber danke für den Denkanstoß o.o