JList mit einer großen Anzahl an Einträgen

  • Themenstarter Themenstarter GuestStar
  • Beginndatum Beginndatum
G

GuestStar

Gast
Hallo,

ich möchte eine JList mit vielen Einträgen füllen - so circa 500 bis 1.500 Stück (oben gerne offen) - und bekomme das nicht hin. Doch zunächst was ich schon habe:

* DefaultListModel, mit dem über "addElement(Object v)" meine Objekte gesetzt werden. D.h. ich habe intern eine ArrayList, die meine Objekte (Video-Objekte) beinhaltet und diese für das Programm zur Verfügung stellt. Über addElement setze ich diese Objekte dann "in die Liste".
* Einen eigenen DefaultListCellRenderer, der die Video-Objekte entgegen nimmt und die wichtigen Daten rendert (und auch ein Icon).
* Die interne ArrayList ist "fest", d.h. ist zwingend nötig. Es ändert sich relativ häfig etwas ... neues Video/Video-Status ändert sich.

Ich habe nun also versucht eine update-Methode zu schreiben, die alle 1000 ms ausgeführt wird und sich die (1) interne ArrayList holt, (2) alle Einträge der JList löscht und (3) die Objekte der ArrayList einfügt. Doch das geht nicht so toll (was bei der Menge an Daten ja nicht so verwunderlich ist); es kommt immer eine Exception:
Java:
Exception in thread "AWT-EventQueue-0" java.lang.ArrayIndexOutOfBoundsException: 466
	at javax.swing.plaf.basic.BasicListUI.updateLayoutState(Unknown Source)
	at javax.swing.plaf.basic.BasicListUI.maybeUpdateLayoutState(Unknown Source)
	at javax.swing.plaf.basic.BasicListUI.getCellBounds(Unknown Source)
	at javax.swing.plaf.basic.BasicListUI.getPreferredSize(Unknown Source)
	at javax.swing.JComponent.getPreferredSize(Unknown Source)
	at javax.swing.ViewportLayout.layoutContainer(Unknown Source)
	at java.awt.Container.layout(Unknown Source)
	at java.awt.Container.doLayout(Unknown Source)
	at java.awt.Container.validateTree(Unknown Source)
	at java.awt.Container.validateTree(Unknown Source)
	at java.awt.Container.validate(Unknown Source)
	at javax.swing.RepaintManager.validateInvalidComponents(Unknown Source)
	at javax.swing.SystemEventQueueUtilities$ComponentWorkRequest.run(Unknown Source)
	at java.awt.event.InvocationEvent.dispatch(Unknown Source)
	at java.awt.EventQueue.dispatchEvent(Unknown Source)
	at java.awt.EventDispatchThread.pumpOneEventForFilters(Unknown Source)
	at java.awt.EventDispatchThread.pumpEventsForFilter(Unknown Source)
	at java.awt.EventDispatchThread.pumpEventsForHierarchy(Unknown Source)
	at java.awt.EventDispatchThread.pumpEvents(Unknown Source)
	at java.awt.EventDispatchThread.pumpEvents(Unknown Source)
	at java.awt.EventDispatchThread.run(Unknown Source)
Der Inhalt der JList erscheint auch nur für kurze Zeit und ist dann wieder weg ...

Aber da muss es doch eine bessere Variante geben, um diese Problem in den Griff zu bekommen. Habt Ihr eine Idee?

Gruß,
GuestStar
 
Also, die interne ArrayList beinhaltet ja die Video-Objekte. Jedes Objekt hat einen Status. Und dieser soll immer aktuell in der JList angezeit werden. So dass der Benutzer auf die JList sehen kann und sich den aktuellen Status jedes Elements ansehen kann.

Wenn man alle paar Sekunden alle Elemente löscht und neu einfügt gibt es auch Probleme mit der Selektion, oder? Selektierte Elemente werden dann "deselektiert" ...

Gibt es da nicht einen besseren Weg? Auch mit einem Blick auf die Performance des ganzen. Denn wenn man die JList immer wieder neu "bestückt" ist das ja auch schon ein bisschen rechenintensiv ...
 
mit einem javax Timer kannst alle paar sekunden deine Objekte aktualisieren... Wenn diese ein Model haben über MVC und wenn nicht musst du halt deine liste druchgehen und den status aktualisieren...

Also ich würde in einem hintergrund job die daten holen (DB, File o.ä.) und dann die Daten aktualisieren und dann mit der GUI synchronisieren...
 
Synch mit der GUI:
SwingUtilities#invokeLater

Der javax.swing.Timer könnte hilfreich sein ...

und der SwingWorker für background jobs und sync mit der GUI!!!
 
Hallo,

ich habe eine Teillösung gefunden. Hier steht etwas (Lists with dynamic content): Advanced JList Programming. Wenn ich das modifiziere, sieht das so aus (wobei "cb.getList()" die besage ArrayList zurück gibt):
Java:
		ListModel bigData = new AbstractListModel() {
		    public int getSize() { 
		    	return cb.getList().size();
		    }
		    
		    public Object getElementAt(int index) {
		    	return cb.getList().get(index);
		    }
		};
Ich habe das auch schon mit ca. 625 echten Daten getestet: funktioniert prima, ohne irgendwelche "Ruckler" etc. Doch es gibt noch ein Problem:

Es wird keine vertikale Scrollbar angezeigt. Zwar kann ich sie über:
Java:
	    JScrollPane sp = new JScrollPane(this.dq);
		//sp.setHorizontalScrollBarPolicy(JScrollPane.HORIZONTAL_SCROLLBAR_NEVER);
		sp.setVerticalScrollBarPolicy(JScrollPane.VERTICAL_SCROLLBAR_ALWAYS);
		w.add(sp, BorderLayout.CENTER);
"erzwingen", doch es werden nur die Pfeiltasten gazeichnet und diese bringen nichts ...

GuestStar
 
Mach mal ein kurzes selbstständig komplierbares Bsp! Ob ScrollBars angezeigt werden hängt von der PreferredSize der Komponente im Viewport des ScrollPanes ab.
 
Hallo,

ich bin dem Problem auf der Spur. In diesem Code-Abschnitt wird der "Fehler" verursacht?
Java:
        ListModel bigData = new AbstractListModel() {
            public int getSize() { 
                return cb.getList().size();
            }
            
            public Object getElementAt(int index) {
                return cb.getList().get(index);
            }
        };
Wenn man Zeile 3 und 7 ändert; z.B. eine statische Size zurückgibt und ein statisches Video erzeugt und immer zurückgibt funktioniert alles. Auch wenn ich eine ArrayList erstelle die nur Strings enthält und in der Erzeugungsmethode von der JList ist, funktioniert alles super. Doch wenn ich versuche auf die ArrayList mit den Videos zuzugreifen, schlägt das fehl.

Also die ArrayList mit den Videos läuft in einem anderen Thread und wird mir über "cb.getList()" zurückgegeben. Ich habe mir mal ausgeben lassen, welche Rückgabewerte so zurück gegeben werden: es wird immer nur die - korrekte - größe der List gelesen. Dann passiert nichts mehr. Keine Aufrufe von "getElementAt".

Sobald ich jdeoch eine "Referenz" auf ein Objekt in der Klasse in die zwei Methoden einbaue funktioniert es prima.

GuestStar
 
Also, nun habe ich mal eine Demo geschrieben, in der alles super funktioniert. Dann habe ich die in mein Projekt eingebaut und in den zwei Methoden wieder zwei "Referenzen" auf die Hauptliste in dem anderen Thread gesetzt. Und schon wird wieder keine Scrollbar angezeigt ...
 
Hallo,

ich habe die Demo fertig! Sie ist alleine funktionsfähig und zeigt das Problem. Zur Sicherheit: es sollte so aussehen - http://s2.imgimg.de/uploads/demo1730cffapng.png - wenn Ihr die Demo startet.

Interessant ist auch, dass garnichts mehr angezeigt wird, wenn man diese Zeilen raus nimmt:
Java:
	    // Set the item width
	    this.list.setFixedCellWidth(this.list.getSize().width);

	    // Set the item height
	    int cellHeight = 50;
	    this.list.setFixedCellHeight(cellHeight);


GuestStar
Java:
package de.nyx.test;

import java.awt.BorderLayout;
import java.awt.Color;
import java.awt.Component;
import java.awt.Dimension;
import java.awt.event.ActionEvent;
import java.awt.event.ActionListener;
import java.util.ArrayList;
import java.util.Random;

import javax.swing.AbstractListModel;
import javax.swing.JButton;
import javax.swing.JFrame;
import javax.swing.JLabel;
import javax.swing.JList;
import javax.swing.JPanel;
import javax.swing.JScrollPane;
import javax.swing.ListCellRenderer;
import javax.swing.ListModel;
import javax.swing.SwingUtilities;
import javax.swing.UIManager;
import javax.swing.border.EmptyBorder;

public class BugJList extends JFrame {

	private static final long serialVersionUID = 1L;

	private JList list;

	private TestThread cb;
	
	public BugJList() {
		super("Bug in JList scrollbars");
		
		this.cb = new TestThread();
		this.cb.start();
		
		setLayout(new BorderLayout());
		
		this.initComponents();
		
		JScrollPane sp = new JScrollPane(this.list);
//		sp.setHorizontalScrollBarPolicy(JScrollPane.HORIZONTAL_SCROLLBAR_NEVER);
//		sp.setVerticalScrollBarPolicy(JScrollPane.VERTICAL_SCROLLBAR_ALWAYS);
		add(sp, BorderLayout.CENTER);
		
		JButton upd = new JButton("Update");
		upd.addActionListener(new ActionListener() {
			public void actionPerformed(ActionEvent e) {
				updateList();
			}
		});
		add(upd, BorderLayout.SOUTH);
	}
	
	
	
	private void initComponents() {

		
		
		ListModel bigData = new AbstractListModel() {
		    public int getSize() { 
		    	return cb.getList().size();
		    }
		    
		    public Object getElementAt(int index) {
		    	return cb.getList().get(index);
		    }
		};

		
		
		this.list = new JList(bigData);
		this.list.setCellRenderer(new DemoRenderer());
		
		
	    // Set the item width
	    this.list.setFixedCellWidth(this.list.getSize().width);

	    // Set the item height
	    int cellHeight = 50;
	    this.list.setFixedCellHeight(cellHeight);
	}
	
	
	private class DemoRenderer extends JPanel implements
			ListCellRenderer {

		private static final long serialVersionUID = 1L;

		private JLabel title;

		public DemoRenderer() {
			setOpaque(true);
			setLayout(new BorderLayout());
			setBorder(new EmptyBorder(4, 4, 4, 4));
			
			this.title = new JLabel();
			this.title.setPreferredSize(new Dimension(0, 50));
			add(this.title, BorderLayout.CENTER);
		}

		public Component getListCellRendererComponent(JList list, Object value,
				int index, boolean isSelected, boolean cellHasFocus) {
			if (isSelected) {
				this.setBackground(Color.black);
				this.title.setForeground(Color.white);
			} else {
				this.setBackground(Color.white);
				this.title.setForeground(Color.black);
			}
			this.title.setText((String) value);
			return this;
		}

	}
	
	public void updateList() {
		this.list.repaint();
	}
	
	
	public static void main(String[] args) {
		try {
			UIManager.setLookAndFeel(
				"com.sun.java.swing.plaf.nimbus.NimbusLookAndFeel");
		} catch (Exception e) {
			e.printStackTrace();
		}
		
		SwingUtilities.invokeLater(new Runnable() {
			public void run() {
				BugJList bjl = new BugJList();
				bjl.setSize(new Dimension(320, 200));
				bjl.setVisible(true);
				bjl.updateList();
			}
		});
	}
	
	
	private class TestThread extends Thread {
		
		private ArrayList<String> defaultData = new ArrayList<String>();
		
		
		public void run() {
			
			try {
				Thread.sleep(1000);
			} catch (Exception e) { }
			
			// generate demo data
			for (int i = 0; i < 625; i++) {
				this.defaultData.add(BugJList.rs(14));
			}
			
			updateList();
			
			while (!Thread.interrupted()) {
				
				if (Math.random() > 0.5) {
					defaultData.remove(4);
				} else {
					this.defaultData.add(BugJList.rs(14));
				}
				
				updateList();
				
				try {
					Thread.sleep(3000);
				} catch (Exception e) { }
			}
			

		}

		public ArrayList<String> getList() {
			return defaultData;
		}
		
	}
	
	
	
	public static String rs(int l) {
		String ac = "0123456789abcdefghijklmnopqrstuvwxyzAB" +
				"CDEFGHIJKLMNOPQRSTOVWXYZ";
		Random r = new Random();
		return randomString(ac, r, l);
	}

	private static String randomString(String ac, Random r, int l) {
		int max = ac.length();
		StringBuffer buffer = new StringBuffer();
		for (int i = 0; i < l; i++) {
			int value = r.nextInt(max);
			buffer.append(ac.charAt(value));
		}
		return buffer.toString();
	}
	
}
 
Er code ist aber nicht so prickelnd ^^...
Schau dir doch echt lieber mal den SwingWorker an anstatt den Thread zu benutzen...
Du machst im Hintergrund irgendwelcge GUI Ändeurngen z.B. repaint das sollte im EDT gemacht werden.
 
Bei dem Thread handelt es sich ja um einen Teil der Programmlogik, die nichts mit der GUI zutun hat und deshalb in einem Thread abgearbeitet wird.
Es ist ja nur ein schnell zusammengeschraubter Code - klar, dass der nicht schön ist ... aber er demonstriert das Problem ...

Wenn ihr mal das Beispiel im Anhang unten startet, tritt wieder das Problem auf. Doch, wenn man nun die Methode "public void sync(String[] listnew)" auskommentiert und den Count im Construktor von 2 auf 20 erhöht, dann funktioniert es ...


Java:
package de.nyx.test;

import java.awt.BorderLayout;
import java.awt.Color;
import java.awt.Component;
import java.awt.Dimension;
import java.awt.event.ActionEvent;
import java.awt.event.ActionListener;
import java.util.ArrayList;
import java.util.Random;

import javax.swing.AbstractListModel;
import javax.swing.JButton;
import javax.swing.JFrame;
import javax.swing.JLabel;
import javax.swing.JList;
import javax.swing.JPanel;
import javax.swing.JScrollPane;
import javax.swing.ListCellRenderer;
import javax.swing.ListModel;
import javax.swing.SwingUtilities;
import javax.swing.UIManager;
import javax.swing.border.EmptyBorder;

public class BugJList extends JFrame {

	private static final long serialVersionUID = 1L;

	private JList list;

	private TestThread cb;

	private MyListModel lm;
	
	public BugJList() {
		super("Bug in JList scrollbars");
		
		this.cb = new TestThread();
		this.cb.start();
		
		setLayout(new BorderLayout());
		
		this.initComponents();
		
		JScrollPane sp = new JScrollPane(this.list);
//		sp.setHorizontalScrollBarPolicy(JScrollPane.HORIZONTAL_SCROLLBAR_NEVER);
//		sp.setVerticalScrollBarPolicy(JScrollPane.VERTICAL_SCROLLBAR_ALWAYS);
		add(sp, BorderLayout.CENTER);
		
		JButton upd = new JButton("Update");
		upd.addActionListener(new ActionListener() {
			public void actionPerformed(ActionEvent e) {
				updateList();
			}
		});
		add(upd, BorderLayout.SOUTH);
	}
	
	
	
	private void initComponents() {

		
		this.lm = new MyListModel();

		
		
		this.list = new JList(this.lm);
		this.list.setCellRenderer(new DemoRenderer());
		
		
	    // Set the item width
	    this.list.setFixedCellWidth(this.list.getSize().width);

	    // Set the item height
	    int cellHeight = 50;
	    this.list.setFixedCellHeight(cellHeight);
	}
	
	
	
	public class MyListModel extends AbstractListModel { 
		
		private ArrayList<String> list; 
		
		public MyListModel() {
			list = new ArrayList<String>();
			for (int i = 0; i < 2; i++) {
				list.add("element " + i);
			}
		}

		public void clear() {
			list.clear();
		}

		public Object getElementAt(int row) {
			return list.get(row);
		}

		public int getSize() {
			return list.size();
		}
		
		public void sync(String[] listnew) {
			System.out.println("to import: " + listnew.length);
			//list.clear();
			for (int i = 0; i < listnew.length; i++) {
				list.add(listnew[i]);
			}
		}

    
	}
	// temp.toArray(new File[temp.size()]);
	
	
	
	
	
	
	
	
	
	
	private class DemoRenderer extends JPanel implements
			ListCellRenderer {

		private static final long serialVersionUID = 1L;

		private JLabel title;

		public DemoRenderer() {
			setOpaque(true);
			setLayout(new BorderLayout());
			setBorder(new EmptyBorder(4, 4, 4, 4));
			
			this.title = new JLabel();
			this.title.setPreferredSize(new Dimension(0, 50));
			add(this.title, BorderLayout.CENTER);
		}

		public Component getListCellRendererComponent(JList list, Object value,
				int index, boolean isSelected, boolean cellHasFocus) {
			if (isSelected) {
				this.setBackground(Color.black);
				this.title.setForeground(Color.white);
			} else {
				this.setBackground(Color.white);
				this.title.setForeground(Color.black);
			}
			this.title.setText((String) value);
			return this;
		}

	}
	
	public void updateList() {
		this.lm.sync(this.cb.getList().toArray(new String[this.cb.getList().size()]));
		this.list.repaint();
	}
	
	
	public static void main(String[] args) {
		try {
			UIManager.setLookAndFeel(
				"com.sun.java.swing.plaf.nimbus.NimbusLookAndFeel");
		} catch (Exception e) {
			e.printStackTrace();
		}
		
		SwingUtilities.invokeLater(new Runnable() {
			public void run() {
				BugJList bjl = new BugJList();
				bjl.setSize(new Dimension(320, 200));
				bjl.setVisible(true);
				bjl.updateList();
			}
		});
	}
	
	
	private class TestThread extends Thread {
		
		private ArrayList<String> defaultData = new ArrayList<String>();
		
		
		public void run() {
			
			try {
				Thread.sleep(1000);
			} catch (Exception e) { }
			
			// generate demo data
			for (int i = 0; i < 625; i++) {
				this.defaultData.add(BugJList.rs(14));
			}
			
			updateList();
			
			while (!Thread.interrupted()) {
				
				if (Math.random() > 0.5) {
					defaultData.remove(4);
				} else {
					this.defaultData.add(BugJList.rs(14));
				}
				
				updateList();
				
				try {
					Thread.sleep(3000);
				} catch (Exception e) { }
			}
			

		}

		public ArrayList<String> getList() {
			return defaultData;
		}
		
	}
	
	
	
	public static String rs(int l) {
		String ac = "0123456789abcdefghijklmnopqrstuvwxyzAB" +
				"CDEFGHIJKLMNOPQRSTOVWXYZ";
		Random r = new Random();
		return randomString(ac, r, l);
	}

	private static String randomString(String ac, Random r, int l) {
		int max = ac.length();
		StringBuffer buffer = new StringBuffer();
		for (int i = 0; i < l; i++) {
			int value = r.nextInt(max);
			buffer.append(ac.charAt(value));
		}
		return buffer.toString();
	}
	
}
 

Neue Themen


Zurück
Oben