JList uzupelnianie

0

Witam

Mam mały problem z uzupełnianiem JList.

W swoim programie tworze nowy JDialog który ma listę z elementami, która z kolei jest pobierana z servera.
Aby okno dialogu od razu się otwierało zamiast czekać na ściągnięcie elementów z servera wstawiłem wypełnianie do SwingWorkera.

Problem w tym, że raz na 10-15 razy lista jest pusta(na pewno się ładuje, widać mrugnięcie z danymi).
Repaint nie pomaga.

Może znacie najczęstsze tego przyczyny? Na co zwrócić uwagę?

0

Podejrzewam, że zmiany listy robisz w którejś funkcji, która nie jest w EDT. Stąd taki efekt, zależny jak się wątki będą realizować. Pokaż kod tego SwingWorker'a.

0

Troche śmieci jest bo próbuje i próbuje


	private class FillList extends SwingWorker<Integer, Integer> 
	{

		private JLabel loadLabel = null;
		private JDialog dialog = null;
		private JTabbedPane pane = null;
		
		  public FillList(JLabel loadLabel, JDialog dialog, JTabbedPane pane) 
		  {
			  this.loadLabel = loadLabel;
			  this.dialog = dialog;
			  this.pane = pane;
		  }

		  @Override
		  protected Integer doInBackground() throws Exception {
		    System.out.println( "GuiWorker.doInBackground" );
		   
		    //this.loadLabel.setVisible(true);
		    this.loadLabel.setIcon(load);
		    fillDeviceList();
		    return 0;
		  }

		  @Override
		  protected void done() {
		    System.out.println("done");
		    this.loadLabel.setIcon(null);
		    this.pane.repaint();
		    //this.pane.revalidate();
		    this.dialog.invalidate();
		    
		  }

		}

i wywoluje tak z jdialog:

EventQueue.invokeLater( new Runnable() {
	        @Override
	        public void run() {
	         
	           new FillList(loadLabel, thisDialog, tabbedPane).execute();

	        }
	      } );
1

Nie musi być invokeLater, może być samo new...execute(); (a o ile się nie mylę, to powinno być bez invokeLater)
Widzę, że w doInBackground robisz setIcon, a tak nie powinno być. Nie wiem co robi fillDeviceList, ale przypuszczam, że pisze do modelu listy, a to też nie powinno mieć miejsca. Jeżeli cała lista pobiera się od razu, todo modelu wpisz ją w done(), a jeżeli po kawałku (po itemie), to użyj metod publish -> process.

0

Poniżej kod metody fillDeviceList.
Listę mam od razu całą,ale przetwarzam, dodaje do modelu pokolei to co odczytam. Czy w takim wypadku powinno być w done czy uzywając process?

Próbowałem rzucić do done ale wtedy program przytnie aż metoda fillDeviceList się wykona.
W moim kodzie najpierw musi pobrać z servera więc przycinka troche trwa. Podzielić to na metody pobierające z servera i wypelniajace model? umieścić do doInBackground pobranie z servera, a to co ściągnie wrzucić do wypełnienia modelu w done() - wtedy powinno być chyba szybciej?

I jescze takie pytanie gdzie umiescic to setIcon lub jesli lepiej setVisible? Chce wyswietlac gif od ladowania.

private boolean fillDeviceList()
	{
		Document d = httpConnections.getDeviceList(this.login, this.password);
		
		if(d == null)
			return false;
			
		return this.fillDeviceList(d);
	}


private boolean fillDeviceList(Document d)
	{
		XPathFactory xpfactory = null;
		XPath path = null;
		String id = null;
		
		try
		{
			xpfactory = XPathFactory.newInstance();
			path = xpfactory.newXPath();
			
			int attrCount = ((Number)path.evaluate("count(devices/id)" , d, XPathConstants.NUMBER)).intValue();
			
			for(int i = 1; i <= attrCount; i++) // indeksy w XPath zaczynaja sie od 1
			{
				id = path.evaluate("devices/id["+ i +"]", d);
				this.deviceListModel.add(i-1, id); // indeksy w XPath zaczynaja sie od 1 w modelu od 0	
			}

			return true;
		}
		catch(XPathExpressionException e)
		{
			e.printStackTrace();
			return false;
		}
		catch(Exception e)
		{
			e.printStackTrace();
			return false;
		}
	}
1

Zrób taki SwingWorker
SwingWorker<Document, String> worker = new SwingWorker<Document, String>() {...
W metodzie Document doInBackground wywołaj Document d = httpConnections.getDeviceList(this.login, this.password);
i daj return d;
W metodzie done napisz Document d=get();
this.fillDeviceList(d);

W ten sposób pobieranie, które trwa długo jest w osobnym wątku, a wypełnianie listy jest w EDT.
Co do ustawiania ikonki to pierwszą zmianę daj przed wywołaniem execute, a drugą w done. Jest też opcja użycia addPropertyChangeListener, ale może nie jest tutaj konieczna.

0

Dzięki : ) Po poprawkach działa, przynajmniej koło 30 otwarć dobrze zadziałało jak na razie : )

Gdyby jednak okazało się, że wypełnienie modelu w done będzie czasochłonne z powodu ilości elementów to trzeba będzie przejść na publish process ?

0
Swr napisał(a):

Dzięki : ) Po poprawkach działa, przynajmniej koło 30 otwarć dobrze zadziałało jak na razie : )

Gdyby jednak okazało się, że wypełnienie modelu w done będzie czasochłonne z powodu ilości elementów to trzeba będzie przejść na publish process ?

Zasadniczo tak by trzeba zrobić.
Skoro działa, to dobrze. Teraz, żeby to wszystko miało sens, to powinieneś przemyśleć dorobienie możliwości przerwania tego wątku przez użytkownika, albo jakiś timeout na połączenie dać. No ale to już inna bajka, tu właściwie chodziło o to, że się czasem lista nie pokazywała i mam nadzieję, że to był powód i jest to rozwiązane.

0

w klasie od pobrania listy z servera HttpConnections mam shutdown który przerywa połączenie http, wiec wtedy kończy się chyba tez wątek bo tam nie ma żadnej pętli, mam to dorobione przy przycisku anuluj, chyba powinno wystarczyć. Niby nie jest to przerwanie SwingWorkera, ale chyba tak też może być?

0

Nie wiem, nie chcę cię wprowadzić w błąd, bo kwestii przerywania wątku nie testowałem. Najlepiej sprawdź, ale mam pewne obawy. Przycisk działa w innym wątku niż pobieranie więc może być tak, jak się zaczęło: 10 razy zadziała, a raz nie. Ale popróbuj. W takiej sytuacji wywoła się metoda done, więc tam trzeba zabezpieczyć na wypadek jak nie dostanie dokumentu.

0

W obu wątkach dostęp do tej samej zmiennej, biblioteka od połączen Apachea , a przycisk anuluj zamyka całe okno. Z tego co widze to wyrzuca mi ConnectionShutdown i wszystko idzie ok, czyli przerywa pobieranie danych, konczy sie metoda fillDeviceList.

Próbuję teraz rozbić to na process ale mam błąd ciągle przy każdym List<Integer> : "The type List is not generic, it cannot be parametrized with arguments<Integer>

Po usunięciu Integer wszędzie zostaje tylko błąd w process.


private class PrimeNumbersTask extends SwingWorker<List<Integer>, Integer> {
PrimeNumbersTask(JTextArea textArea, int numbersToFind) 
{ 
     //initialize 
}

 @Override
 public List<Integer> doInBackground() {

	 return null;
 }

 @Override
 protected void process(List<Integer> chunks) {

     }
}

Przykład z http://docs.oracle.com/javase/6/docs/api/javax/swing/SwingWorker.html

1

Zobacz jakiego List masz w importach, bo możliwe że zaimportowałeś złą klasę (tzn masz więcej niż jedną klasę o nazwie List na classpath'ie i wybrałeś złą).

0

dzięki : ) w sumie o tym myślałem ale byłem dość pewny że tego nie zrobiłem...okazało się, że miałem java.awt.*

0

Zrób SwingWorker<Document, Integer> To Document to już masz i to jest to, co zwraca doInBackground i tego nie zmieniaj. process też masz dobrze, tylko teraz w doInBackground daj
for(int i = 1; i <= attrCount; i++) // indeksy w XPath zaczynaja sie od 1
{
id = path.evaluate("devices/id["+ i +"]", d);
publish(id);

                    }

a w process
for(Integer id:chunks){
this.deviceListModel.addElement(id);
}

Pomijam tu indeks pętli indeksów XPath, bo do samego modelu listy to on nic nie wnosi. Ale jak ci jest potrzebny jakoś, to możesz sobie przerzucać jakiś swój obiekt albo tablicę, zamiast tego Integer.

1 użytkowników online, w tym zalogowanych: 0, gości: 1