środa, 18 marca 2009

Swing: Własny komponent, EasyWay

Swing to stosunkowo prosty w użyciu zbiór klas do tworzenia GUI. Ma wiele zalet i tyle samo przeciwników co zwolenników. Rozwiązanie to ma już swoje lata, jednak rozwija się dość sprawnie dalej i ciągle pozwala na bardzo sprawne kodowanie. Sun tworząc Swing'a pozostawił stosunkowo dużo możliwości rozwinięcia go. Jednym ze sposobów rozszerzenia Swing jest stworzenie własnego komponentu graficznego. Opiszę tutaj jedną z prostszych metod na stworzenie go.

Zbiór komponentów jest dość duży. Pozornie zawsze znajdzie się czego użyć do stworzenia swojej aplikacji. Czasami by uzyskać właściwy efekt w jakimś specjalistycznym zastosowaniu konieczne jest skorzystanie z kombinacji nawet kilkunastu elementów. Istnieją też takie przypadki, gdy użycie własnego rozwiązania jest po prostu konieczne.

Stosunkowo prosta i chyba najlepsza metoda na stworzenie własnego komponentu to rozszerzenie już istniejącego komponentu o zbliżonych właściwościach zgodnie z zasadami programowania obiektowego. Mówiąc prościej wystarczy stworzyć klasę dziedziczącą z klasy komponentu z pakietu javax.swing.*. Osobiście proponuję użycie klasy JPanel. Jest to bardzo prosty komponent, a co najważniejsze jest kontenerem, więc służy do "przechowania" innych komponentów. Z zasad obiektowości wiemy, że klasa dziedzicząca z JPanel będzie posiadała wszystkie jego właściwości.

Co można zrobić z JPanel? W zasadzie wszystko co potrzeba. Najpierw trzeba wiedzieć jak dokładnie taką klasę przygotować:

public class JCustomComponent extends JPanel
{
  public JCustomComponent()
  {
    super();
  }
}

No i cała filozofia. Co dokładnie robi powyższy kod? Przykład tworzy nową klasę JCustomComponent, która jak na razie zachowuje się dokładnie jak JPanel i w ten sposób można ją wykorzystać. Teraz czas najwyższy na rozszerzenie. Dajmy na to niech ten komponent w tle ma czerwony owal tak na cały swój wymiar.

Nazwy komponentów w Swing są praktycznie takie same jak w AWT. Różnice są niewielkie w ilości dostępnych elementów. Przyjęto więc konwencję, iż nazwy klas Swing zaczynać się będą od dużej litery J. Wskazane jest, aby konwencję tą zachować tworząc nowe klasy, aby jednoznacznie wskazać, pochodzenie komponentu.

Aby wpłynąć na sposób rysowania komponentu wystarczy przeciążyć jedną metodę.

protected void paintComponent(Graphics g)

Za każdym razem gdy komponent jest odrysowywany wywoływana jest właśnie ta metoda. W przypadku większości komponentów tak na prawdę nie zawiera ona nic konkretnego. Rysowaniem jak i obsługą zajmuje się DelegatUI. Nie wpływamy jednak na delegata, tylko na klasę podstawową komponentu. Nie musimy nawet przy tej metodzie wiedzieć, że Delegat istnieje.

Przykład z czerwonym owalem:
public class JCustomComponent extends JPanel
{
  public JCustoComponent()
  {
    super()
  }

  protected void paintComponent(Graphics g)
  {
    g.setColor(Color.red);
    g.drawfilloval(0,0,this.getWidth(), thid.getHeight);
    super.paintComponent(g);
  }
}

Ostatnia linijka w metodzie paintComponent() jest niemal obowiązkowa. Jej zadanie to wywołanie metody rysowania rodzica, czyli klasy JPanel. Jest to szczególnie ważne gdy użyjemy komponentu jako kontenera. Zapewnia to poprawne odrysowanie "dzieci" komponentu dzięki już stworzonym mechanizmom klasy JPanel. Reszta kodu różniącego się od pierwszego przykładu, to tylko rysowanie figury geometrycznej po ustawieniu odpowiedniego koloru.

Proponuję, aby zawsze w konstruktorze komponentu, czyli jeszcze przed jego rysowaniem zadbać o odpowiedni jego wygląd. Mam na myśli ustawienie jego tła na przezroczyste korzystając z metody setBackground(), którą to odziedziczył oczywiście po JPanel. Aby tło było przezroczyste wystarczy stworzyć nowy kolor (klasa Color) z parametrem Alpha ustawionym na 100% (wartość: 255 lub 0xFF)

Co można więcej? W zasadzie co tylko dusza zapragnie. Po pierwsze komponent może dziedziczyć po dowolnym istniejącym komponencie rozszerzając jego możliwości. Przykładowo możemy stworzyć klasę JPasswordField z dowolnym EchoChar'em dziedzicząc z JTextField. Najważniejsze, to aby nie powielać już istniejących rozwiązań. Jeśli potrzebujemy po prostu panelu z tłem w dowolnym kolorze, to lepiej użyć zwykłego JPanel niż tworzyć nową klasę dziedziczącą. W końcu przecież JPanel posiada metodę setBackground(Color).

Jest to raczej krótki wstęp do tworzenia komponentów Swing. Zawiera tylko opis jednej z metod i co ważne prostej, lecz skutecznej. Jej użycie wymaga niewielkiej znajomości Javy. Następnym razem postaram się przybliżyć tworzenie LayoutManager'a dla Swing na przykładzie MapLayoutManager.

Jak można zauważyć każdy post zaopatrzony jest w ilustrację. Te wyposażone w emoticony z tabliczkami można uznać w pewnym sensie za wyjątkowe. Emotsy będą nam nieco przeszkadzać w tekście. Na razie nie ma ich wiele, dlatego te same powtarzają się w obrazkach, ale z czasem przybędzie ich i mam nadzieję czasem kogoś rozbawią. Od razu ostrzegam, że nie jest to komiks internetowy w paskach. Emotsy z założenia mają nie wychodzić poza ramy jednego obrazka, chyba że właśnie gdzieś sobie pójdą ;).

1 komentarz: