Pożegnanie z blogiem WP

Czerwiec 19, 2012 Dodaj komentarz

Otworzyłem nowy blog

Reklamy
Kategorie:Bez kategorii

Archipelagi pełne <div>-ów. Czyli o html i semantyce słów kilka.

Marzec 30, 2010 4 Komentarze

W związku z tym, że obecnie siłą rzeczy mam dużo styczności z HTML zainteresowałem się z nie rozerwalnie z nim związaną semantyką.

Temat daleki od Java, ale zawsze to coś technicznego, a i myślę, że jest warte poruszenia. Z tego co zauważyłem dla większości webmajstrów w naszym kraju tworzenie nowocześnie, tworzenie wg standardów W3C to głównie totalna anihilacja większości mających semantycznego znaczenie dozwolonych znaczników na rzecz chyba obdarzanego jakimś kultem <div>,a dla przykładu <table> to samo zło. Poza tym mało kto posiada aktualną wiedzę na temat HTML, no bo po co się go uczyć skoro jest taki prosty. Oprócz tego i sam walidator nie jest w stanie sprawdzić strony pod kątem semantyczności, a więc sama wlepka na stronie potwierdzająca zgodność to w dużej części przypadków pic na wodę fotomontaż, bo standardy to nie tylko valid w3c.

W polskim internecie ciągle mało jest stron poprawnych semantycznie, nie wspominając już o zgodności z takim standardem jak WAI, których praktycznie u nas nie ma

Pomysł na ten post wziął się całkiem przypadkiem. Otóż przeglądałem sobie pewną stronę i podglądnąłem sobie kod źródłowy takiego o to kawałka tejże strony:
4 ostatnie fotki

Kiedy zobaczyłem jakie spaghetti kryje z taką pierdołą, jak wyświetlenie 4 obrazków…

<div class="last_rows"><div class="thumb">
          <div class="outer">
              <div class="middle">
                <div class="inner">
                  <div class="avatar_photo">
<a href="">
<img alt="miniaturka zdjęcia" class="thumb" src=""></a>
                 </div>
                </div>
              </div>
            </div><div class="desc"><div class="author">
<a href="/profile/31521844">XXXXX YYYYYY</a><br>dzisiaj 20:31</div></div>
</div>
<div class="thumb">
            <div class="outer">
              <div class="middle">
                <div class="inner">
                  <div class="avatar_photo"><a href="/profile/22620947/gallery/39">
<img alt="miniaturka zdjęcia" class="thumb" src="http://photos.nasza-klasa.pl/22164904/39/thumb/44d11daf6a.jpeg"></a>
                  </div>
                </div>
              </div>
            </div><div class="desc"><div class="author">
<a href="/profile/22620947">LLLLLLLL ### ~~~ CCCCCCCC ~~~</a><br>dzisiaj 20:31</div></div>
</div>
<div class="thumb">
            <div class="outer">
              <div class="middle">
                <div class="inner">
                  <div class="avatar_photo"><a href="/profile/19763376/gallery/145">
<img alt="miniaturka zdjęcia" class="thumb" src="http://photos.nasza-klasa.pl/19984140/145/thumb/3719f89981.jpeg"></a>
                  </div>
                </div>
              </div>
            </div><div class="desc"><div class="author"><a href="/profile/19763376">***SLODKA  BLONDI***</a><br>dzisiaj 20:31</div></div>
</div>
<div id="last_photo_td"><div class="thumb">
           <div class="outer">
              <div class="middle">
                <div class="inner">
                  <div class="avatar_photo"><a href="">
<img alt="miniaturka zdjęcia" class="thumb" src=""></a>
                  </div>
                </div>
              </div>
            </div><div class="desc"><div class="author">
<a href="">XXXXXX KKKKKK (95 SUPPORT)</a><br>dzisiaj 20:31</div></div>
            </div>
</div> 41 linii
<div class="clear"></div></div></div>

…to wiedziałem, że poruszę w końcu ten temat.
Całą tę stronę trafią skazy o których wspomina Jeffrey Zeldman w swojej książce Designing With Web Standards, a mianowice classitis i divitis w jednym. A chyba jedna z najpopularniejszych stron w polskim internecie powinna trzymać poziom, zwłaszcza jeśli chodzi o tę część na którą twórcy mają największy wpływ, bo sama zawartość to inna kwestia. Tymczasem semantyka leży, ułatwienie dostępu też praktycznie tam nie istnieją, ale to już temat na osobny post.
Zresztą nie tylko o semantykę tu chodzi, taka nadmiarowość znaczników to zapchane łącze itd, co w przypadku tejże strony ma ogromne znaczenie. Prawdopodobnie spokojnie dałoby się zmniejszyć liczbę znaczników całej strony o połowę.

Czas na refaktoryzację

Dokładnie, skoro można zrobić to z kodem źródłowym stricte programowym to czemu nie z html?
Ekspertem w tej dziedzinie to ja na pewno jestem, ale postanowiłem posprzątać ten syf i sprawdzić czy mi się to uda dla własnej satysfakcji, a jak:). Całą strukturę obrazków postanowiłem oprzeć na liście definicji <dl>, wg fachowców od semantyki (m.in. Dan Cederholm w książce Kuloodporne strony internetowe str. 101), może być ona wykorzystywana do tworzenia galerii, zresztą od początku wiedziałem, że ją wykorzystam, chociaż nawet i zwykłe, tak znienawidzone <table> byłoby wydajniejsze niż ten makaron co tam jest…

Struktura

        <div>
            <dl>
                <dt><a href="#"><img src="img/nk1.jpeg" alt="miniaturka-zdjęcia" /></a></dt>
                <dd>
                    <a href="#">Xxxxx Yyyyyyyyyyy</a><br />Dzisiaj 17:44
                </dd>
            </dl>
            <dl>
                <dt><a href="#"><img src="img/nk2.jpeg" alt="miniaturka-zdjęcia" /></a></dt>
                <dd>
                	<a href="#">Uuuuuu Nnnnn (Jjjjjj)</a> <br />Dzisiaj 17:44
                </dd>
            </dl>
            <dl>
                <dt><a href=""><img src="img/nk3.jpeg" alt="miniaturka-zdjęcia" /></a></dt>
                <dd>
                	<a href="#">Aaaa Kkkkkkkkkk</a> <br />Dzisiaj 17:44
                </dd>
            </dl>
            <dl>
                <dt><a href=""><img src="img/nk4.jpeg" alt="miniaturka-zdjęcia" /></a></dt>
                <dd>
               		<a href="">Uuuuu Ppppppp</a> <br />Dzisiaj 17:44
               </dd>
            </dl>
        </div>

Co nam daje:

galeria bez styli

Pierwsze szlify CSS

* {
	padding: 0;
	margin: 0;
}
body {
	font-size: small;
	font-family: Tahoma, Geneva, sans-serif;
	padding: 50px;
	text-align:center;
}
div, dd, dl, dt {
	float: left;
}
div {
	border: 1px solid #ccf;
}
dd {
	clear: left;
	font-size: 75%;
}
dl {
	padding: 7px 14px;
}
a {
	text-decoration: none;
	font-size: 110%;
	font-weight: bold;
	color: #369;
	border: none;
}
img {
	padding: 5px;
	border: 1px solid #ccf;
}

Co tu się dzieje, znającym style nie muszę wyjaśniać. Najważniejszą częścią jest ustawienia elementy <dd>, w którym znajduje się podpis pod zdjęciem, aby znajdował się poniżej elementu <dt>, który to przechowuje nam zdjęcie. Aby uzyskać poziomy kierunek obrazków użycie <dl> z deklaracją float: left załatwia sprawę.

Efekt pierwszych zabiegów CSS

rezultat pierwszych szlifów css

Następnie dodaje deklarację z wziętym wymiarem bezpośrednio z nk:

dt {
	height: 112px;
}

Ustawienie wysokości znacznika przechowującego obrazek, powoduje, że podpisy pod obrazkami znajdują w jednej linii:
wyrównanie podpisów pod zdjęciem

Ostateczny arkusz

* {
	padding: 0;
	margin: 0;
}
body {
	font-size: small;
	font-family: Tahoma, Geneva, sans-serif;
	padding: 50px;
	text-align:center;
}
div, dd, dl, dt {
	float: left;
}
div {
	border: 1px solid #ccf;
	height: 160px;
}
dl {
padding: 7px 14px;
}
dd {
	clear: left;
	font-size: 75%;
	width: 120px;
}
dt {
	width: 120px;
	height: 112px;
	display: table;
}
a {
	text-decoration: none;
	font-size: 110%;
	font-weight: bold;
	color: #369;
}
dt a {
	border: none;
	display: table-cell;
	vertical-align: middle;
}
img {
	padding: 5px;
	background: #fff;
	border: 1px solid #ccf;
	-moz-box-shadow: 1px 1px 0 #ccf;
	-webkit-box-shadow: 1px 1px 0 #ccf;
     box-shadow: 1px 1px 0 #ccf;
}

Musimy wycentrować obrazek w pionie, o tym zabiegu można przeczytać np. tutaj
Aby po tym zabiegu uzyskać wyśrodkowanie tekstu względem obrazka, trzeba dodać właściwośćwidth
dla <dt> i <dd>.

No i pozostaje kwestia cienia pod obrazkiem. Znam dwa rozwiązania:

  • Dodać <div> za obrazkiem (nadrzędny do obrazka) i ustawić obrazek relatywnie względem niego
  • Użyć dobrodziejstw CSS 3

Pierwsze rozwiązanie burzy układ obrazków, ponieważ wyśrodkowanie obrazka w poziomie wtedy idzie w łeb, a z tej racji, że nie ma określonej długości dla pojemnika otaczającego, a co za tym idzie ustawienia marginesów dla niego nic nam nie dadzą. Użycie JavaScript-u, obliczanie szerokości obrazka i dodawanie go do stylu pojemnika pomogło by rozwiązać ten problem.

Ja użyłem właściwości box-shadow efekt jest bardzo fajny, obecny wszędzie poza IE
Brak tego efektu z IE i tak pewnie nie byłby zauważony przez większość użytkowników tak prześladowanej przeze mnie strony…

Mimo sprawy cienia pod obrazkiem, uważam, że takie rozwiązanie jest warte świeczki, ale pozostanie pewnie tylko na tej stronie, a kod tamtej strony będzie dalej cuchnął i zapychał łącza…

A i zapomniałbym;), efekt końcowy:

Kategorie:web Tagi: ,

W separacji z Java;)

Październik 28, 2009 Dodaj komentarz

Właściwie powinienem napisać to już dawno temu. W związku z tym, że jestem na etapie pisania pracy dyplomowej, a uznał, że najlepszy do mojego tematu będzie język PHP, tak jak to już widać zresztą od dłuższego czasu, zawieszam swoją działalność blogową.

Jeśli ktoś z odwiedzających miałby jakiś problem, na który nie może znaleźć odpowiedzi na forum, może pisać na mejla, jeśli tylko znajdę czas z chęcią pomogę, żeby trochę oderwać się od PHP no i nie tracić całkowicie kontaktu z Java, bo sam na pisanie czegoś ciekawego nie mam po prostu czasu.

Pozdrawiam

Kategorie:Bez kategorii

Powiadają, że najlepsze są powroty….

Wrzesień 10, 2009 Dodaj komentarz

…no i wróciłem z wygnania za chlebem, stając od razu przed kampanią wrześniową w szkole (czytaj ukończeniem piekielnego projektu), który stwierdzam teraz jest trochę bezsensowny, bo zaprzęganie Java do tworzenie zwykłej aplikacji desktop-owej mija się trochę z celem, chciałbym zacząć powoli bawić się w coś bardziej związanego z ideą Java, ale jak mus to mus, trzeba walczyć…

Pozdrawiam wszystkich dotychczas oceniających moje notki, za wszelkie uwagi jestem bardzo wdzięczny.

Kategorie:Różności

JTable nie takie „straszne”.

Czerwiec 28, 2009 Dodaj komentarz

Mamy okres wakacyjny, dla wielu w tym i mnie jest to czas wyjazdów do pracy, aby zarobić trochę pieniążków na chleb (czyt. kolejny rok studiów, itd.). Pomimo tego, że czasu nie mam za wiele, postanowiłem napisać cokolwiek, żeby blog nie był taki martwy. Temat dzisiejszej rozkminy prosty, ale na pewno przyda się początkującym adeptom Java którzy podobnie jak ja jeszcze nie dawno, staną w pewnym momencie do walki o ujarzmienie JTable.

Z moich obserwacji wynika, że kłopoty z prawidłową obsługą tabeli ma duża część początkujących. Zwykle wybierana przez nich droga rozwiązania nie jest najlepsza i kończy się dużą ilością kompletnie nie potrzebnego kodu, koszmarnego w utrzymaniu, zwłaszcza jeśli w programie mamy wiele różnych tabel. Wydaje mi się, że wynika to przede wszystkim z faktu nie znajomości wzorców projektów, szczególnie MVC, z którego to biblioteka Swing czerpie pełnymi garściami, gdzie nawet najmniejszy JButton jest przykładem tego wzorca.

Ale do rzeczy, model dla JTable jest implementacją interfejsu TableModel, programiści API wyręczyli nas z bezpośredniej implementacji tego interfejsu, tworząc jego szkieletową implementację AbstractTableModel, dzięki czemu odpada nam dużo roboty.

Przykład, model tabeli operujący na obiektach klasy Person:

public class PersonTableModel extends AbstractTableModel {

    private List<Person> persons = null;
    private final static Object[] columnNames = {"", "Imię", "Nazwisko", "Płeć",
        "Ulica","Nr. d/m", "Miasto", "Kod pocztowy", "Województwo",
        "Tel. kom", "Tel. dom", "Email"};
    
    private final static int HIDDEN_IDX = 0;
    private final static int NAME_IDX = 1;
    private final static int SURNAME_IDX = 2;
    private final static int GENDER_IDX = 3;
    private final static int STREET_IDX = 4;
    private final static int APARTMENT_IDX = 5;
    private final static int CITY_IDX = 6;
    private final static int ZIPCODE_IDX = 7;
    private final static int PROVINCE_IDX = 8;
    private final static int CELL_IDX = 9;
    private final static int PHONE_IDX = 10;
    private final static int EMAIL_IDX = 11;

    public PersonTableModel() {}
    
    @Override
    public int getRowCount() {
        if(persons==null) return 0;
        return persons.size();
    }

    @Override
    public int getColumnCount() {
        return columnNames.length;
    }
    
    @Override
    public Object getValueAt(int rowIndex, int columnIndex) {

        if(persons == null) return null;
        Person p = (Person) persons.get(rowIndex);
        switch (columnIndex) {
            case HIDDEN_IDX:
                return p.getId();
            case NAME_IDX:
                return p.getName();
            case SURNAME_IDX:
                return p.getSurname();
            case GENDER_IDX:
                return p.getGender().toString();
            case STREET_IDX:
                return p.getStreet();
            case APARTMENT_IDX:
                return p.getApartment();
            case CITY_IDX:
                return p.getCity();
            case ZIPCODE_IDX:
                return p.getZipCode();
            case PROVINCE_IDX:
                return p.getProvince().toString();
            case CELL_IDX:
                return p.getCellNumber();
            case PHONE_IDX:
                return p.getPhoneNumber();
            case EMAIL_IDX:
                return p.getEmail();
            default:
                return p;
        }
    }

    @Override
    public String getColumnName(int column) {
        return columnNames[column].toString();
    }

    @Override
    public boolean isCellEditable(int row, int column) {
        return false;
    }
    
    public void setModelData(List<Person> persons) {
       this.persons =  persons;
    }
    public Person getPerson(int position) {
        return persons.get(position);
    }
 
}

Aby stworzyć model o podstawowej funkcjonalności wystarczy zaimplementować 3 metody abstrakcyjne:

  • public int getRowCount() – zwracającą liczbę wierszy w tabeli
  • public int getColumnCount() – analogicznie liczbę kolumn
  • public Object getValueAt(int rowIndex, int columnIndex) – zwracają wartość o podanych współrzędnych

Bardzo często w modelu dodaję się obsługę nagłówka tabeli, zwłaszcza jeśli mamy pewność, że liczba kolumn w tabeli nie będzie się zmieniać, wtedy należy przesłonić metodę public String getColumnName(int column), a jeśli chcemy zablokować możliwość edycji komórek tabeli przeciążamy metodę public boolean isCellEditable(int row, int column)
Jak widać model tabeli przechowuje referencję do kolekcji obiektów Person, gdy stan tej listy zmieni się wystarczy wywołać metodę fireTableDataChanged(), aby odświeżyć stan tabeli.

I to już wszystko, jak widać nie takie to straszne, więcej przydatnych informacji przechowuje skarbonka wiedzy suna: JTable Tutorial.

Kategorie:Java SE

Mój sposób na „oczyszczanie” formularzy….

Czerwiec 17, 2009 3 Komentarze

Wszyscy znamy opcję reset spotykaną chociażby w zwykłych formularzach html. Na pierwszy rzut oka z kodzenie takiej funkcjonalności w aplikacji okienkowej wydaje się banalnie proste, bierzemy wszystkie komponenty typu JTextComponent, lub jeszcze inne, wkładamy je do kolekcji i w odpowiedniej metodzie, przywracamy pierwotny stan kontrolek (np. setText(null);).

I co wszystko jest tip top? Otóż mamy zonka połamaliśmy co najmniej kilka z podstawowych zasad programowania obiektowego z SRP i DRY na czele. Czyli ogólnie wszystko jest do dupy. Wystarczy pomyśleć co by było w przypadku 10 różnych formularzy w aplikacji, a pasuje każdemu dodać taką funkcjonalność. Ile zbędnych, koszmarnych w utrzymaniu linii kodu nabazgrzemy?

Postanowiłem pobawić się w małą, nie doskonałą jeszcze, ale jednak refaktoryzację…
W standardowej bibliotece Java mamy bardzo przyjemny interfejs ContainerListener i nim właśnie zamierzam się posłużyć, powołując do życia interfejs ContainerClearer.

package view;
import java.awt.event.ContainerListener;

public interface ContainerClearer extends ContainerListener {

   void clearComponents();

}

Mając już podstawę na potrzeby klas konkretnych, pasuje skodzić jakąś implementację.

package view;

import java.awt.Component;
import java.awt.event.ContainerEvent;
import java.util.*;
import javax.swing.*;

public class SimpleFormClearer implements ContainerClearer {

    private List<Component> components;

    public SimpleFormClearer() {
        components = new ArrayList<Component>();
    }

    public void clearComponents() {
        for(Component c : components) {
            if (c instanceof JTextComponent)
                ((JTextComponent)c).setText(null);
            if (c instanceof JComboBox)
                ((JComboBox)c).setSelectedItem(null);
        }
    }

    public void componentAdded(ContainerEvent e) {
        components.add(e.getChild());
    }

    public void componentRemoved(ContainerEvent e) {
        components.remove(e.getChild());
    }

}

W celu zmniejszenie ilości kodu, można posunąć się jeszcze dalej, tworząc klasę abstrakcyjną i zaimplementować w niej interfejs ContainerClearer, jak to jest często wykorzystywane w standardowych bibliotekach Java (Abstract + nazwa interfejsu)

public abstract class AbstractContainerClearer implements ContainerClearer {

    protected java.util.List<Component> components;

    public AbstractContainerClearer() {
        components = new java.util.ArrayList<Component>();
    }

    abstract public void clearComponents();

    public void componentAdded(ContainerEvent e) {
        components.add(e.getChild());
        addClearerToChildContainer(e.getChild());
    }

    public void componentRemoved(ContainerEvent e) {
        components.remove(e.getChild());
        addClearerToChildContainer(e.getChild());
    }

    protected void addClearerToChildContainer(Component c) {

        if (c instanceof Container) {

            Container cont = (Container) c;
            cont.addContainerListener(this);

            Component[] children = cont.getComponents();

            for (int i = 0; i < children.length; i++) {
                addClearerToChildContainer(children[i]);
            }
        }
    }
}

Dzięki temu, że jest to klasa abstrakcyjna nie musimy implementować w niej metod narzucanych przez interfejs, dorzucamy abstract i wszystko śmiga. Powyższy zabieg uświadomił mi jeszcze jedną ważną kwestią, otóż aby klasa SimpleFormClearer działała poprawnie chociażby w okienku dialogowym zawierającym wiele zagnieżdżonych paneli musielibyśmy dodawać go do każdego panela, co w przypadku bardziej rozbudowanego GUI staje się dość wkurzające. Metoda addClearerToChildContainer rozwiązuje ten problem, a implementacja tej klasy staję się znacznie bardziej przejrzysta:

public class BasicFormClearer extends AbstractContainerClearer {

    @Override
    public void clearComponents() {
        for(Component c : components) {
            if (c instanceof JTextComponent)
                ((JTextComponent)c).setText(null);
            if (c instanceof JComboBox)
                ((JComboBox)c).setSelectedItem(null);
        }
    }
}

Teraz wystarczy dodać go do kontenera najwyższego poziomu:

getContentPane().addContainerListener(new BasicFormClearer());

i po sprawie…

Zalety: dużo prościej, ładniej i przejrzyście, tworząc kilka klas konkretnych można jeszcze pokusić, a nawet należy dodać odpowiednią fabrykę.
Wady: na pierwszy rzut oka widać że kod metody clearComponents() nie jest doskonały (OCP, rzutowanie), mi jak na razie nie przychodzi lepsze rozwiązanie, może ktoś przedstawi jakąś sugestię?

W każdym razie i tak jest dużo lepiej, warto zaglądać do dokumentacji w poszukiwaniu przydatnych narzędzi i zawsze należy dążyć do uzyskania jak największej jakości kodu.

Kategorie:Java SE Tagi:

SwingWorker w akcji;)

Czerwiec 5, 2009 1 komentarz

Postanowiłem oderwać się od swojej piekielnej potyczki z wyrażeniami regularnymi i napisać coś w końcu zgodnego z tytułem blogu. Temat na początek łatwy, ale od czegoś trzeba zacząć, a dokładnie wykonywanie długotrwałych procesów programu za pomocą klasy SwingWorker
Postanowiłem o tym napisać bo sam dosyć często lekceważyłem tę kwestię i jakoś nie przeszkadzało mi kilku-sekundowe przywieszenie się UI podczas np. łączenia z bazą danych w prostych programikach na zaliczenia. W końcu stało się to bardzo wkurzające i jest to po prostu duży przypał jeśli widzi się takie działanie programu. Drugim powodem było to, że nie tak wiele osób umie prawidłowo korzystać ze „świątyni wszechwiedzy” Google, widziałem nawet jak nie którzy sami próbowali implementować „ciężkie” zadania zrzucając je na własne wątki, czyli prostować poziomicę.

Co do tych ciężkich procesów, to ogólna zasada mówi że każde zadanie ktorego czas trwania moze spowodowac zauwazalne ‚zamrozenie’ interfejsu uzytkownika, nie powinno być wykonywane w wątku operującym GUI. Do takich długotrwałych procesów zalicza się m.in:

  • Łączenie z bazą danych oraz operacje na bazie danych
  • Wczytywanie dużych plików np. (jpg-ów) (ogólnie operacji IO)
  • Długotrwałych obliczeń (np.operacji morphingu)
  • Odświeżanie dużych tabel

W Java wątek odpowiadający za obsługę GUI zwie się Event Dispatch Thread (jakby ktoś nie wiedział;)), czyli po prostu EDT. Jak sama nazwa mówi zajmuję się on obsługą kolejki zdarzeń i informowaniem o nich obiektów nasłuchujących (czyt. z reguły ActionListener-ów), dodatkowo zarządza rozłożeniem komponentów, ich wyświetleniem, zmianą właściwości komponentów (np. dezaktywacja przycisku) i obsługą zadań. Zadaniami tymi powinny być tylko i wyłącznie krótkotrwałe procesy. Niektórzy myślą że to…:

public static void main(String[] args) {

    SwingUtilities.invokeLater(new Runnable(){
        @Override
        public void run() {
            JFrame f = new JFrame();
            f.setSize(200,200);
            f.setVisible(true);
        }
    });
    }
}

lub

public static void main(String[] args) {

    EventQueue.invokeLater(new Runnable(){
        @Override
        public void run() {
            JFrame f = new JFrame();
            f.setSize(200,200);
            f.setVisible(true);
        }
    });
    }
}

…załatwia sprawę, ale to dopiero początek, każda nie banalna aplikacja powinna odpalać GUI w EDT za pomocą powyższych technik (pierwsza jest ciut mniej pracochłonna, ponieważ wystarczy zaimportować tylko pakiet javax.swing.*;), ale jak zwał tak zwał wszystko to jeden ciul ważne że mamy prawidłowo zainicjalizowane GUI i jak na razie wszystko jest wporządku .

Teraz potrzebujemy obsługi długotrwałych zadań, która nasza spłodzona w pocie czoła aplikacja ma wykonywać.
Pierwszy krok…
…a jakże ulubiona dokumentacja

Rozkminimy sobie co ten wspaniały produkt programistów Sun-a ma nam do zaoferowania.
Pierwsze co rzuca się w oczy to deklaracja klasy:

public abstract class SwingWorker<T,V> extends Object 
implements RunnableFuture

Jak widać SwingWorker to klasa abstrakcyjna, czyli możemy utworzyć obiekt tylko i wyłącznie klasy po niej dziedziczącej. Drugą rzeczą rzucającą się w oczy są parametry T i V, tak SwingWorker czerpie pełnymi z garściami z dobrodziejstw typów uogólnionych dodanych w Java 1.5. Implementowany interfejs jest połączeniem interfejsów Runnable i Future (z niego właśnie pochodzi metoda get() zwracająca rezulat obliczeń).
Parametr T – określa typ zwracany przez metody doInBackground() i get() o których później. Jest to po prostu rezultat naszego zadania np. obrazek.
Parametr V – określa typ danych pośrednich, które może produkować zadanie, przykładowo linie tekstu z wczytywanego pliku. Dane te można wyłuskać za pomocą metody process(List dane), a które przekazuje do niej metoda publish(V... dane), która powinna być używana w implementacji metody doInBackground() .
Uwaga!
Aby metody zwracały typ void (a dokładniej żadnego konkretnego typu), bo taką możliwość naturalnie też mamy, w deklaracji klasy należy podstawić parametr Void.

Użyteczne metody klasy SwingWorker:

  • abstract protected doInBackground()

    Zaimplementowanie tej metody najbardziej nas interesuje, ba, nawet musimy to zrobić ponieważ jest to metoda abstrakcyjna. To w niej powinniśmy dostarczyć zadanie do wykonania. Jeśli przesłonimy także metodę process(List dane), możemy przy pomocy publish(V... dane) przekazywać do process(List dane) cząstkowe wyniki działania zadania.

  • protected process(List dane)

    Dzięki tej metodzie możemy operować na pośrednich danych zwróconych przez publish(V... dane). W tej metodzie możemy bezpiecznie operować na komponentach graficznych, np. dodawać linijki tekstu to JTextArea, ponieważ działa ona asynchronicznie w EDT.

  • protected void publish(V... chunks)

    Metoda ta przesyła szczątkowe dane metodzie protected process(List dane), nie ma potrzeby jej przesłaniania, ale można to zrobić w przypadku gdy np. chcemy wykonać jakieś extra operację na dostarczanych danych.

  • protected void done()

    Metoda wywoływana po zakończeniu zadania, wykonywana w EDT, można w niej przeprowadzić sprzątania i zaprezentować w GUI główny rezultat wykonywanego zadania, czyli pobrać rezultat działania doInBackground() przy pomocy metody get().

To nie wszystkie ciekawe rzeczy związane ze SwingWorker-em, polecam zaglądnięcie do dokumentacji, jest to bardzo dobrze udokumentowana klasa.

A teraz kodzik prostego przykładu, pełny program można pobrać stąd.

	        
button.addActionListener(new ActionListener(){
           public void actionPerformed(ActionEvent e) {
               SwingWorker<Icon, Void> worker = new SwingWorker<Icon, Void>(){

                    @Override
                    protected Icon doInBackground() throws Exception {
                        progressBar.setIndeterminate(true);
                        return loadImage(textField.getText());
                    }

                    @Override
                    protected void done() {
                        try {
                            imageLabel.setIcon(get());
                            progressBar.setIndeterminate(false);
                        } catch (Exception ex) {
                            ex.printStackTrace();
                        }
                    }

               };
               worker.execute();
           }
        });

Dla ułatwienia jak widać posłużyłem się klasą anonimową, tutaj ktoś może się zastanowić dlaczego za każdym razem tworzę nowy obiekt, a to dlatego, że nie ma możliwości powtórnego wykorzystania obiektu tego typu.
I na tym kończę ten arcik, mam nadzieję że nie popełniłem żadnych byków i polecam wszystkim nie stosującym do tej pory, specjalnie przeznaczonego do wykonywania długich zadań SwingWorker-a.

Kategorie:Java SE Tagi: , ,