Krótka przerwa od Guavy i przesiadka na Vaadin. Dziś o własnych szablonach układów.
Problem wyglądu
Często gęsto jest tak, że chcąc uzyskać jakiś konkretny układ graficzny dla bardziej skomplikowanych komponentów graficznych tworzymy własne komponenty. Własne komponenty składamy w bardziej skomplikowane by finalnie uzyskać pożądany efekt.
Doskonałym przykładem tego typu sytuacji są wszystkie aplikacje zarówno webowe jak i desktopowe. Sam też niejednokrotnie na forum 4programmers pisałem, że „jak nie potrafisz ułożyć skomplikowanego layout podziel go na mniejsze i spróbuj znowu”. Jest to oczywiście kolejne zastosowanie metody „dziel i zwyciężaj” lecz w przypadku Vaadin niesie ona duże niebezpieczeństwo.
Problem spotykany jest też w Swingu – spadek wydajności przy renderowaniu okien. Rzecz oczywiście rozbija się o to jak wiele dodatkowej pracy związanej z wyliczaniem pozycji komponentów ma aplikacja. Co więcej, jest to robione „pod spodem” i tym samym ma magiczną zdolność do bycia ignorowanym.
W przypadku Vaadin niepisana zasada mówi, żeby nie zagnieżdżać więcej niż trzy LayoutManagery. Pierwszym będzie zatem główny układ aplikacji. Drugim układ okna, trzecim układ komponentu… i zaczyna być ciasno w przypadku skomplikowanych komponentów.
Jak to ogarnąć?
Paradoksalnie najprościej ten problem rozwiązać stosując stary dobry CSS w połączeniu z klasami rozszerzającymi CustomLayout. Layout ten to nic innego jak swoisty „trik” pozwalający na wykorzystanie szablonów napisanych w czystym html-u jako layoutów w Vaadin.
Część HTML + CSS
W trakcie generowania aplikacji za pomocą mavena możemy podać nazwę wykorzystywanej skórki (theme). Podajemy coś tam, coś tam i teraz w src/main/webapp/VAADIN/themes/ mamy katalog o nazwie jaką podaliśmy. W nim są obecnie tylko pliki css i scss, ze stylami naszej aplikacji. Tworzymy zatem tu katalog layouts, a w nim plik MyCustomLayout.html.
Listing 1. Plik MyCustomLayout.html
<!DOCTYPE html> <html> <head> <title></title> </head> <body> <nav location="button" id="button-box"></nav> <div location="label" id="label-box"></div> </body> </html>
Jak widać jest to zwykły, „goły” plik html. Jedyna różnica w stosunku do innych plików tego typu polega na dodatkowych atrybucie location w elementach, które mają być miejscami „wstrzyknięć” komponentów Vaadin. Następnie w pliku styles.css konfigurujemy nasz układ:
Listing 2. konfiguracja układu w styles.css
#button-box{ border: 1px solid #afafff; height: 4em; width: 12em; } #label-box{ border: 1px solid #afafff; height: 4em; width: 12em; font-family: garamond; }
Tyle. Oczywiście można to dowolnie rozbudowywać, przekształcać i dostosowywać do potrzeb. Co więcej jeżeli zastosujemy taki układ jako główny dla aplikacji możemy dodać statyczne grafiki tak jak w HTMLu.
Część javowa
Tu sprawa troszkę się komplikuje jeżeli chcemy sobie ułatwić życie w przyszłości. Po pierwsze musimy przygotować klasę reprezentującą nasz nowy układ:
Listing 3. Klasa MyCustomLayout
public class MyCustomLayout extends CustomLayout { private final static String NAME = "MyCustomLayout"; public MyCustomLayout() { super(NAME); } public void addComponent(Component c, Placeholders p) { addComponent(c, p.placeholder); } //... }
Pole NAME po przekazaniu do konstruktora klasy nadrzędnej będzie traktowane jako nazwa pliku przy czym nie należy dodawać sufiksu .html, bo się zjebie.
Kolejnym elementem jest enum Placeholders:
Listing 4. Enum Placeholders
public class MyCustomLayout extends CustomLayout { //... public static enum Placeholders { BUTTON("button"), LABEL("label"); private final String placeholder; Placeholders(String label) { this.placeholder = label; } } }
Który będzie sobie siedział w naszym layoucie. Dlaczego tak? Ponieważ możemy popełnić literówkę w nazwie punku „wstrzyknięcia”. Łatwiej poprawić ją w jednym miejscu niż w wielu.
La grande finale
Czas to wszystko złożyć do kupy w postaci naszej aplikacji testowej. Przerobiłem po prostu wygenerowany kod na taki:
Listing 5. Aplikacja testowa
@Theme("mytheme") @SuppressWarnings("serial") public class MyVaadinUI extends UI { @Override protected void init(VaadinRequest request) { final MyCustomLayout layout = new MyCustomLayout(); setContent(layout); Button button = new Button("Click Me"); button.addClickListener(new Button.ClickListener() { public void buttonClick(ClickEvent event) { layout.addComponent(new Label("Thank you for clicking"), LABEL); } }); layout.addComponent(button, BUTTON); } @WebServlet(value = "/*", asyncSupported = true) @VaadinServletConfiguration(productionMode = false, ui = MyVaadinUI.class) public static class Servlet extends VaadinServlet { } }
Śmiga całkiem miło.
Swego czasu popełniłem kupę kodu w Vadinie dla pewnego banku w P.
Z perspektywy czasu widzę, że prościej i fajniej byłoby to zrobić w AngularJs. Polecam. Gwt i pochodne to ślepy zaułek. Szkoda czasu i nerwow. Tak samo jak JSP i JSF.
chcę poznać argumenty.