<h2>Inleiding</h2> <p> De veronderstelling is dat je met Flex sneller een gelikte UI in elkaar kunt zetten, maar is dat ook echt zo? Misschien voor een simpel “hello world” appje, maar hoe zit het met complexere UI’s met “custom” vormgeving? </p> <p> In dit eerste deel van de vergelijking tussen Java en Flex zullen we ons richten op de mogelijkheden die Java i.c.m. de Synth LAF en Flex bieden om componenten te skinnen. </p> <h2>Skinnen in Flex</h2> <p> In Flex kan skinnen van componenten op twee manieren: direct in de MXML declaratie van een component of in aparte stylesheets. Skinnen met behulp van stylesheets heeft als voordeel herbruikbaarheid, en wordt daarom ook gezien als best practice.<br/> Stylesheets in Flex lijken verdacht veel op de <a href=”http://www.w3.org/Style/CSS/”>W3C CSS</a>; niet ontoevallig dat Adobe ze ook bij die naam noemt.<br/> Er zijn echter wat aspecten die afwijken van de W3C standaard, en specifiek met betrekking tot selectors: <ul> <li>Geen ondersteuning voor ID selectors. Hiermee kan je in CSS een selector voor een specifiek element declareren, b.v. <code>#pageTitle</code></li> <li>Geen ondersteuning voor nesting selectors. Hiermee kan je alle elementen binnen een ander element selecteren. Voorbeeld: <code>ul a</code>, alle links binnen een unordered list</li> <li>Geen ondersteuning voor pseudo-classes. Meest bekende pseudo-selector is <code>a:hover</code>, waarmee je een de stilering van een link waar de muispointer op ligt kan definiëren</li> </ul> </p> <p> Laten we om te beginnen eens kijken hoe het skinnen werkt als je dat direct op in de component declaratie doet. Als voorbeeld maken we een knop: <pre> <mx:Button label=”Click Me” fontFamily=”Georgia” fontSize=”20″ fontStyle=”italic” textDecoration=”underline” color=”#0000FF”/> </pre> Zoals eerder vermeld, om alle stileringsinformatie op de componenten zelf te plaatsen zou nogal wat werk kosten. Welnu, een klein voorbeeld van hoe dezelfde skin er in Flex CSS uitziet: <pre> Button { color: #0000FF; fontFamily: Georgia; fontSize: 20; fontStyle: italic; textDecoration: underline; } </pre> </p> <p> <div><object classid=”clsid:D27CDB6E-AE6D-11cf-96B8-444553540000″ codebase=”https://download.macromedia.com/pub/shockwave/cabs/flash/swflash.cab#version=6,0,0,0″ data=”/roller/luminis/resource/flexvsjava/ButtonExample.swf” height=”60″ type=”application/x-shockwave-flash2-preview” width=”200″ ><param name=”data” value=”/roller/luminis/resource/flexvsjava/ButtonExample.swf”/><param name=”loop” value=”false”/><param name=”menu” value=”false”/><param name=”movie” value=”/roller/luminis/resource/flexvsjava/ButtonExample.swf”/><param name=”quality” value=”high”/><param name=”scale” value=”exactfit”/><param name=”src” value=”/roller/luminis/resource/flexvsjava/ButtonExample.swf”/><param name=”type” value=”application/x-shockwave-flash2-preview”/><embed height=”60″ pluginspage=”https://www.macromedia.com/shockwave/download/index.cgi?P1_Prod_Version=ShockwaveFlash” quality=”high” src=”/roller/luminis/resource/flexvsjava/ButtonExample.swf” type=”application/x-shockwave-flash2-preview” width=”200″ /></object></div> </p> <p> Voor diegenen die vaker met HTML en CSS werken zal het redelijk bekend voorkomen. Nu een wat geavanceerder voorbeeld: <pre> Button { color: #0080c0; font-size: 12; corner-radius: 10; border-color: #006699; border-thickness: 3; fill-colors: #FCFFF4, #006699; fill-alphas: 0.5, 0.5; text-roll-over-color: #FFFFFF; } </pre> </p> <p> <div><object classid=”clsid:D27CDB6E-AE6D-11cf-96B8-444553540000″ codebase=”https://download.macromedia.com/pub/shockwave/cabs/flash/swflash.cab#version=6,0,0,0″ data=”/roller/luminis/resource/flexvsjava/AdvancedButtonExample.swf” height=”60″ type=”application/x-shockwave-flash2-preview” width=”150″ ><param name=”data” value=”/roller/luminis/resource/flexvsjava/AdvancedButtonExample.swf”/><param name=”loop” value=”false”/><param name=”menu” value=”false”/><param name=”movie” value=”/roller/luminis/resource/flexvsjava/AdvancedButtonExample.swf”/><param name=”quality” value=”high”/><param name=”scale” value=”exactfit”/><param name=”src” value=”/roller/luminis/resource/flexvsjava/AdvancedButtonExample.swf”/><param name=”type” value=”application/x-shockwave-flash2-preview”/><embed height=”60″ pluginspage=”https://www.macromedia.com/shockwave/download/index.cgi?P1_Prod_Version=ShockwaveFlash” quality=”high” src=”/roller/luminis/resource/flexvsjava/AdvancedButtonExample.swf” type=”application/x-shockwave-flash2-preview” width=”150″ /></object></div> </p> <p> Hier wordt al meteen duidelijk dat Flex een rijk scala aan stijl- en effectattributen biedt, o.a. voor hoekafrondingen, meerdere kleuren van borders, gradients, transparantie. Tevens is hier te zien dat het ontbreken van pseudo-selectors wordt opgevangen door attributen als <code>overFillColors</code>, <code>selectedFillColorRatios</code>, enz. </p> <p> Toewijzen van stijlen aan componenten in Flex kan op de volgende manieren: <ul> <li>Door middel van type selectors: de naam van de componentklasse wordt als selector gedefinieerd.</li> <li>Door middel van class selectors: een component een stijlnaam te geven, die vervolgens als selector in de style sheet te declareren.</li> </ul> Overigens zijn combinaties van bovenstaande methodes ook mogelijk, hierover later meer. </p> <h3>Type selectors</h3> <p> Indien we voor een componentklasse een stijl willen definieren, hoeven we slechts zijn naam als CSS selector met de bijbehorende stijlattributen te declareren: <pre> Button { color: red; cornerRadius: 12; textSize: 12; } Label { color: black; } </pre> </p> <p> Een leuk aspect is de manier waarop componentovererving is verweven met stijldefinities. Nemen we als voorbeeld de <code>Text</code> klasse: deze erft van <code>Label</code>, en erft ook alle gedefinieerde stijlelementen van <code>Label</code>. <pre> Label { color: red; cornerRadius: 12; textSize: 12; } Text { color: black; } </pre> </p> <h3>Class selectors</h3> <p> De andere manier om stijlkoppelingen te bewerkstelligen is een stijl direct toewijzen een component, d.m.v. een zogenaamde class selector.<br/> Neem het volgende code fragment: <pre> .myButton { color: red; textSize: 12; } </pre> <pre> <mx:Button styleName=”myButton” label=”OK”/> </pre> </p> <p> Zoals eerder vermeld, zijn type selectors en class selectors ook te combineren. Type selectors worden toegepast op alle instanties van een component klasse, maar daarnaast kan een instantie van een component klasse ook een eigen stijl selecteren. De gedeclareerde attributen in deze stijl zullen alle attributen overschrijven die in de type selector staan. Als voorbeeld: <pre> Button { color: yellow; textSize: 12; } .myButton { color: red; } </pre> <pre> <mx:Button label=”Cancel”/> <!– Gele tekst –> <mx:Button styleName=”myButton” label=”OK”/> <!– Rode tekst –> </pre> </p> <h2>Java Synth LAF</h2> <p> De Java Synth LAF is, zoals de naam al zegt, een Pluggable Look & Feel implementatie voor Swing. Dit wil ook zeggen dat Synth niet per definitie als skin voor een applicatie wordt gebruikt, maar dat deze, zoals alle Swing Look and Feels, eerst geïnstalleerd moet worden. <pre> URL file = …; // De URL naar de Synth style file SynthLookAndFeel synth = new SynthLookAndFeel(); try { synth.load(file); UIManager.setLookAndFeel(synth); } catch (Exception e) { e.printStackTrace(); } </pre> </p> <p> Een van de dingen die al meteen opvallen is dat Synth LAF een referentie naar een bestand nodig heeft. Dit is een bestand waar alle stijlen zijn gedefiniëerd; Synth heeft namelijk geen ‘default’ skin, dus de aanweizigheid van dit bestand is vereist. </p> <p> Wat staat er nou in zo’n Synth stijl bestand? Hier een klein voorbeeld: <pre> <synth> <color id=”fg_color” value=”#00364e”/> <color id=”bg_color” value=”#0f79a9″/> <font id=”std_font” name=”Arial” size=”12″ style=”PLAIN”/> <style id=”default”> <font idref=”std_font/> <state> <color idref=”fg_color” type=”FOREGROUND”/> <color idref=”bg_color” type=”BACKGROUND”/> </state> </style> <bind style=”default” type=”region” key=”.*”/> <!– … overige stijlen… –> </synth> </pre> Dit is een voorbeeld van een Synth Style bestand in zijn meest kale vorm: er wordt alleen een standaard stijl gegeven die geldt voor alle componenten. Aan deze stijl wordt gekoppeld een voor- en achtergrondkleur en een font.<br/> Een interessant detail aan dit voorbeeld is dat het illustreert hoe stijlelementen, zoals kleuren en lettertypen, herbruikt kunnen worden. <br/> De functie van het bind element behandelen we verderop. </p> <p> Nu naar een wat ingewikkelder component, een button: <pre> <style id=”defaultbutton”> <state id=”def”> <imagePainter method=”buttonBackground” path=”button_up.png” sourceInsets=”8 8 8 8″ paintCenter=”true” /> <insets top=”8″ left=”8″ bottom=”8″ right=”8″ /> <font name=”Arial” size=”12″ style=”BOLD” /> <color value=”#000000″ type=”TEXT_FOREGROUND” /> </state> <state value=”PRESSED”> <imagePainter method=”buttonBackground” path=”button_pressed.png” sourceInsets=”8 8 8 8″ paintCenter=”true” /> <insets top=”8″ left=”8″ bottom=”8″ right=”8″ /> </state> <state value=”MOUSE_OVER”> <imagePainter method=”buttonBackground” path=”button_over.png” sourceInsets=”8 8 8 8″ paintCenter=”true” /> <insets top=”8″ left=”8″ bottom=”8″ right=”8″ /> </state> <state value=”DISABLED” clone=”def”> <color value=”#999999″ type=”TEXT_FOREGROUND” /> </state> </style> <bind style=”defaultbutton” type=”region” key=”BUTTON”/> </pre> </p> <p> <applet code=”ButtonStyleExample.class” codebase=”.” archive=”/roller/luminis/resource/flexvsjava/flexvsjavastyles.jar” width=”300″ height=”100″></applet> </p> <p> In dit voorbeeld zijn eigenlijk al bijna alle kernconcepten van de Synth Style vervat. Een Synth Style bestand bevat een of meerdere style declaraties, die een unieke identificatie hebben (het id attribuut). Een style slaat in de praktijk op de skin van een specifieke componentklasse, in dit voorbeeld buttons. </p> <p> Binnen een style worden een of meerdere states gedefiniëerd. Een state is ook letterlijk de toestand waarin het component zich bevindt. In dit voorbeeld zijn een viertal states gedefinieerd: de standaard wanneer er niets bijzonders met het component gebeurt, wanneer hij ingedrukt wordt (<code>PRESSED</code>), wanneer hij disabled is (<code>DISABLED</code>) en wanneer de mousepointer er over zweeft (<code>MOUSE_OVER</code>). Overige beschikbare states zijn: <code>ENABLED</code>, <code>FOCUSSED</code>, <code>SELECTED</code>, <code>DEFAULT</code>. </p> <p> Een state bevat de stijlattributen van een component in een specifieke toestand. DIt kunnen zijn kleuren voor de voor- of achtergrond (het <code>color</code> element), lettertypes voor tekst (het <code>font</code> element), afbeeldingen (het <code>imagePainter</code> element), en de grensruimte van een component (het <code>insets</code> element). </p> <p> Overerving van stijlen is ook mogelijk, en wel door middel van het clone attribuut binnen het style element. <pre> <style id=”textfield”> <state> <color value=”yellow” type=”BACKGROUND”/> <color value=”blue” type=”TEXT_FOREGROUND”/> <font name=”Arial” size=”14″ style=”BOLD” /> <insets top=”4″ left=”4″ bottom=”4″ right=”4″ /> </state> </style> <style id=”passwordfield” clone=”textfield”> <state> <color value=”orange” type=”BACKGROUND”/> <color value=”black” type=”TEXT_FOREGROUND”/> </state> </style> <bind style=”textfield” type=”region” key=”TextField”/> <bind style=”passwordfield” type=”region” key=”PasswordField”/> </pre> </p> <p> <applet code=”TextFieldStyleExample.class” codebase=”.” archive=”/download/attachments/14812620/flexvsjavastyles.jar” width=”300″ height=”100″></applet> </p> <p> In Java Synth wordt een stijl gekoppeld aan een component door middel van het bind element. Dit element biedt de mogelijkheid om een stijl te binden aan een component. Het bind element heeft 3 attributen die van belang zijn: <ul> <li><code>style</code>: de referentie naar een elders gedeclareerde stijl</li> <li><code>type</code>: de soort koppeling, d.w.z. generieke of specifieke koppeling</li> <li><code>key</code>: de sleutel van de koppeling, d.w.z. de naam van een component of de naam van de klasse.</li> </ul> </p> <p> Beginnend met de declaratie van een stijl: <pre> <style id=”buttonStyle”>…</style> </pre> Een bind element moet in ieder geval een referentie hebben naar de gedeclareerde stijl, in dit geval <code>buttonStyle</code>: <pre> <bind style=”buttonStyle” … /> </pre> Als voorbeeld willen we alle knoppen in onze applicatie binden aan de stijl <code>buttonStyle</code>. Dit doen we door als type attribuut ‘region’ op te geven, en als key attribuut een van de constanten uit de <code>javax.swing.plaf.synth.Region</code> klasse te gebruiken, in dit geval de waarde van <code>javax.swing.plaf.synth.Region.BUTTON</code>: <pre> <bind style=”buttonStyle” type=”region” key=”Button” /> </pre> </p> <p> Om een enkel component een afwijkende stijl te geven is wat meer werk nodig. Nu vertellen we de binding dat het moet kijken naar componenten die een specifieke naam hebben. Als voorbeeld maken we een binding die de stijl <code>buttonStyle</code> koppelt aan componenten met naam <code>XXX</code>: <pre> <bind style=”buttonStyle” type=”name” key=”XXX” /> </pre> Met deze binding hebben we in feite nog niets gekoppeld. Dit gebeurt pas als er een component wordt aangemaakt die <code>XXX</code> als naam heeft: <pre> JButton b = new JButton(”Klik mij”); b.setName(”XXX”); </pre> </p> <p> Een leuke feature van binden is dat er ook reguliere expressies gebruikt kunnen worden. Bijvoorbeeld: <pre> <bind style=”knopStijl” type=”name” key=”knop.*” /> </pre> Dit bindt alle componenten wiens naam begint met <code>knop</code> aan de stijl <code>knopStijl</code>. </p> <p> Een ander aspect van stijl koppelingen in Synth is dat er meerdere stijlen gebonden kunnen worden aan componentent. Neem de volgende stijldeclaraties en bindings: <pre> <style id=”Globaal”> <state> <color value=”blue” type=”FOREGROUND”/> </state> </style> <style id=”Knop”> <state> <color value=”orange” type=”FOREGROUND”/> </state> </style> <style id=”SpecialeKnop”> <state> <color value=”red” type=”BACKGROUND”/> </state> </style> <bind style=”Globaal” type=”region” name=”*”/> <bind style=”Knop” type=”region” name=”Button”/> <bind style=”SpecialeKnop” type=”name” name=”speciale_knop.*”/> </pre> Bovenstaande code geeft alle componenten een gele tekstkleur, behalve knoppen die een blauwe tekstkleur krijgen. Indien de naam van een knop (die reeds de <code>Knop</code> stijl heeft) begint met <code>speciale_knop</code>, zal deze ook de stijl <code>SpecialeKnop</code> toegewezen krijgen, wat inhoudt dat de achtergrond rood wordt. </p> <h2>Een vergelijking in de praktijk</h2> <p> Welnu, een praktijk voorbeeldje. We stellen ons als opdracht: het maken van een button met afgeronde hoeken en een achtergrond met een verticale 2-kleuren gradient (wit en groen). Tevens heeft de button een mouse-over effect waarbij de tweede gradient kleur wijzigt naar donkergroen. </p> <h3>Implementatie in Flex</h3> <p> In Flex is dit een fluitje van een cent. Om te beginnen met de ronde hoeken, welke te verwezenlijken zijn met het ‘corner-radius’ stijl attribuut: <pre> Button { cornerRadius: 12; /* De mate van afronding */ } </pre> Vervolgens de gradient, waar een tweetal stijattributen van belang zijn: fillColors en fillColorRatios. Het eerst attribuut definieert de kleuren, het tweede de relatieve posities (op een schaal van 0 tot 255) waar maximale verzadiging is. <pre> Button { cornerRadius: 12; /* De mate van afronding */ fillColors: #FFFFFF, #00FF00; /* wit, groen */ fillColorRatios: 0, 255; } </pre> Ten slotte het mouse-over effect, waarbij de overFillColors en overFillColorRatios worden gebruikt. <pre> Button { cornerRadius: 12; /* De waarde geeft de mate van hoekafronding */ fillColors: #FFFFFF, #33FF00; /* wit, groen */ fillColorRatios: 0, 255; overFillColors: #FFFFFF, #00CC00; /* wit, donkergroen */ overFillColorRatios: 0, 255; } </pre> </p> <p> <div><object classid=”clsid:D27CDB6E-AE6D-11cf-96B8-444553540000″ codebase=”https://download.macromedia.com/pub/shockwave/cabs/flash/swflash.cab#version=6,0,0,0″ data=”/roller/luminis/resource/flexvsjava/ButtonGradientExample.swf” height=”60″ type=”application/x-shockwave-flash2-preview” width=”150″ ><param name=”data” value=”/roller/luminis/resource/flexvsjava/ButtonGradientExample.swf”/><param name=”loop” value=”false”/><param name=”menu” value=”false”/><param name=”movie” value=”/roller/luminis/resource/flexvsjava/ButtonGradientExample.swf”/><param name=”quality” value=”high”/><param name=”scale” value=”exactfit”/><param name=”src” value=”/roller/luminis/resource/flexvsjava/ButtonGradientExample.swf”/><param name=”type” value=”application/x-shockwave-flash2-preview”/><embed height=”60″ pluginspage=”https://www.macromedia.com/shockwave/download/index.cgi?P1_Prod_Version=ShockwaveFlash” quality=”high” src=”/roller/luminis/resource/flexvsjava/ButtonGradientExample.swf” type=”application/x-shockwave-flash2-preview” width=”150″ /></object></div> </p> <h3>Implementatie in Java Synth LAF</h3> Hier lopen we al meteen tegen 2 issues aan: Synth LAF heeft geen ondersteuning voor afgeronde hoeken in componenten (de zogenaamde corner radius) en gradients. In principe is een gradient achtergrond wel te maken, maar dit kost wat werk. Eerst moeten we een painter coderen die gradients kan maken: <pre> public class GradientPainter2 extends SynthPainter { public void paintTextFieldBackground(SynthContext context, Graphics g, int x, int y, int w, int h) { GradientPaint gp = new GradientPaint((float)(x + (w/2)), (float)y, Color.WHITE, (float)(x + (w/2)), (float)(y + h), new Color(0, 200, 0)); Graphics2D g2 = (Graphics2D)g; g2.setPaint(gp); g2.fillRect(x, y, w, h); g2.setPaint(null); } } </pre> Vervolgens declareren we deze in de Synth configuratie als een object, en maken een painter op basis van dit object: <pre> <object id=”gradient”/> <style id=”button”> <painter method=”buttonBackground” idref=”gradient”/> </style> </pre> Hiermee hebben we helaas nog geen afgeronde hoeken. Een andere taktiek is dus vereist. De simpelste methode is het maken van achtergrondafbeeldingen die de gradient en afgeronde hoeken al hebben. Deze afbeeldingen worden door Synth geschaald op de grootte van de button <pre> <style id=”button”> <state> <imagePainter method=”buttonBackground” path=”buttonUp.png” sourceInsets=”8 8 8 8″ paintCenter=”true”/> </state> <state value=”MOUSE_OVER”> <imagePainter method=”buttonBackground” path=”buttonDown.png” sourceInsets=”8 8 8 8″ paintCenter=”true”/> </state> </style> </pre> Interessant om te vermelden is dat de bovenstaande methode met achtergrondafbeeldingen ook in Flex toegepast kan worden: <pre> Button { up-skin: Embed(source=”buttonUp.png”); over-skin: Embed(source=”buttonDown.png”); } </pre> <h2>Conclusie</h2> <p> Als we de twee implementaties nu vergelijken, valt meteen op dat Synth erg afhankelijk is van externe graphics wil er een beetje leuk resultaat uit komen. Dit heeft tot gevolg dat er een grote afhankelijkheid op een designer is, en dat de grootte van de applicatie aanzienelijk toeneemt door alle extra graphics die erbij komen. </p> <p> Flex daarentegen biedt een developer wat meer ruimte en mogelijkheden om in de loop van een ontwikkeltraject zelf nog aanpassingen te kunnen doen. Hierbij moet opgemerkt worden dat bij ieder nadeel weer en voordeel zit: indien er voor wordt gekozen om de skin volledig met externe graphics op te bouwen, vereist het minder vaardigheden van de designer, die zich niet bezig hoeft te houden met MXML en/of Actionscript. </p> <p> Wel is het zo dat skins in Synth op een veel consistentere manier zijn op te bouwen. Vrijwel alle stijlelementen zijn herbruikbaar, dus er zullen minder snel foutjes insluipen. Tevens heeft dit tot gevolg dat skin refactorings (denk aan verandering van fonts of kleuren) een relatief eenvoudige operatie zullen zijn. Dit zou in Flex toch wat meer aandacht en tijd kosten, aangezien CSS geen hergebruik (anders gezegd: centrale declaratie) van stijlelementen mogelijk maakt. </p> <p> Wat verder opvalt is dat de de hoeveelheid CSS code voor een Flex applicatie t.o.v. Synth Style bestand vele malen compacter is. </p>
Flex vs Java Deel 1: Stijlen
- Nog geen reacties.
- Nog geen trackbacks.
