A template for button skins using FXG graphics? - apache-flex

The UI in my app uses a lot of buttons that are mostly the same minus the FXG graphics used in their skins. Their skins look something like this
<?xml version="1.0" encoding="utf-8"?>
<s:Skin xmlns:s="library://ns.adobe.com/flex/spark" xmlns:fx="http://ns.adobe.com/mxml/2009" xmlns:ai="http://ns.adobe.com/ai/2009" xmlns:d="http://ns.adobe.com/fxg/2008/dt" xmlns:flm="http://ns.adobe.com/flame/2008" xmlns:graphics="assets.graphics.*" xmlns:ATE="http://ns.adobe.com/ate/2009">
<fx:Metadata>[HostComponent("spark.components.Button")]</fx:Metadata>
<s:states>
<s:State name="up"/>
<s:State name="over"/>
<s:State name="down"/>
<s:State name="disabled"/>
</s:states>
<graphics:RectangleGraphic bottom="0"/>
<graphics:ButtonTextGraphic alpha.disabled="0.45" x="22" y="6"/>
<graphics:ButtonSymbolGraphic alpha.disabled="0.45" x="4" y="4">
<graphics:filters>
<s:GlowFilter includeIn="over, down" blurX="3" blurY="3" inner="false" color="#3F5F93" strength="2" alpha="1.0" quality="2" knockout="false"/>
<s:GlowFilter includeIn="down" blurX="3" blurY="3" inner="false" color="0x5380d0" strength="2" alpha="1.0" quality="2" knockout="false"/>
</graphics:filters>
</graphics:ButtonSymbolGraphic>
<graphics:SmallButtonLineSeparatorGraphic bottom="-0.5"/>
</s:Skin>
where RectangleGraphic and SmallButtonLineSeparatorGraphic are FXGs that are used across all buttons, and ButtonTextGraphic and ButtonSymbolGraphic are FXGs that are specific to each button.
I would love to have a single template skin that is fed the two FXGs specific to each button, but I'm not sure how to best do that.

You'll have to do some ActionScript magic to make it work. Probably Leave RectangleGraphic and SmallButtonLineSeparatorGraphic as they are in MXML. Create variables for ButtonTextGraphic and ButtonSymbolGraphic. I'll just demonstrate using ButtonTextGraphic a sample. Something like this:
public var buttonTextGraphicClass : Class;
Also have variables for the class instance:
public var buttonTextGraphic : Class;
In the constructor, you can set the class values. Or if you're stuck w/ MXML, then use a preinitialize event handler:
buttonTextGraphicClass = ButtonTextGraphic;
in createChildren create the instances and add them:
protected function override createChildren():void{
super.createChildren();
buttonTextGraphic = new buttonTextGraphicClass()
// set other properties
addElement(buttonTextGraphicClass);
}
Finally, in updateDisplayList() size and position them elements, something like this:
buttonTextGraphic.x = 22
buttonTextGraphic.y = 6
buttonTextGraphic.width = unscaledWidth // or some other value
buttonTextGraphic.height = unscaledHeight // or some value
This way you can use the same exactly skin; just replacing the class instances for your changing FXG assets at runtime.
I suggest reading up on the Flex Component LifeCycle. If you have Z-Order issues; you may have to switch to creating all the skin's children in ActionScript; or possibly use the "addElementAt" method.
To accommodate for different states in the ActionScript code, you'll have to override the set currentState method to manually make our state changes, such as changing your glow or changing the alpha.
Pretty much everything I describe here is the approach to creating ActionScript skins. You may benefit from looking at some of the existing Flex 4.5 Mobile Skins. I'd start with the Mobile ButtonSkin. I think the border is an FXG Asset; which is implemented using a similar approach to what I describe here.

Related

Creating a new skin class for a ToggleButton

I'm using the following Flex component: http://flashcommander.org/blog/flex-4-mp3-player
The skin that comes with this component only allows for very basic skinning, such as background colour and borders. However the component makes use of a ToggleButton, defined in Mp3Player.as:
import spark.components.ToggleButton;
[SkinPart]
public var playPauseButton:ToggleButton;
What I would like to do is define an alternative skin for this control in the project, I'm pretty new to Flex and Spark so I'm un-sure as to how I go about define a new skin.
In the Mp3PlayerSkin.mxml file the skin is defined as follows:
<!--- #copy spark.components.VideoPlayer#playPauseButton -->
<s:ToggleButton id="playPauseButton" left="0" bottom="0"
skinClass="spark.skins.spark.mediaClasses.normal.PlayPauseButtonSkin"
focusIn="event.target.depth=1" focusOut="event.target.depth=0" />
I assume I can create a new class, although I don't know how to do that, or remove the reference to the class and extend the ToggleButton skin in some way. I'm thinking I need to override the 'default' skin in some way?
Any pointers appreciated ...
Looks like I can do the following:
Create a new Flex Skin by right-clicking the original Mp3PlayerSkin.mxml file
Name the file toggleButtonSkin.mxml
Copy the styles from the spark.components.VideoPlayer#playPauseButton class
Update the Mp3PlayerSkin.mxml as follows:
<s:ToggleButton id="playPauseButton" left="0" bottom="0"
skinClass="org.flashcommander.components.toggleButtonSkin"
focusIn="event.target.depth=1" focusOut="event.target.depth=0" />
Now I can edit the new toggleButtonSkin.mxml file by editing the new styles.

Smooth scrolling a Spark List with variable height items using the mouse wheel

We have a list of variable height items that we display in a Spark List control. When the user clicks and drags the vertical scrollbar the list scrolls smoothly. When the up/down arrows are used it moves in small and discrete steps. When the mouse wheel is used the list scrolls in very large discrete steps that is problematic for the user.
We would like to enable smooth scrolling for the mouse wheel. The height of our items vary significantly and it is easy to get lost when you scroll with the moouse due to the discrete scrolling.
Our implementation is fairly simple:
<s:List id="chartList"
dataProvider="{pm.charts}"
itemRenderer="charts.ChartItemRenderer"
horizontalScrollPolicy="off"
verticalScrollPolicy="on"
useVirtualLayout="false"
cachePolicy="auto">
</s:List>
<?xml version="1.0" encoding="utf-8"?>
<s:ItemRenderer xmlns:fx="http://ns.adobe.com/mxml/2009"
xmlns:s="library://ns.adobe.com/flex/spark"
xmlns:mx="library://ns.adobe.com/flex/mx"
autoDrawBackground="false"
xmlns:charts="charts.*"
>
<fx:Script>
<![CDATA[
private var _canvas:BitmapData;
public function set canvas(value:BitmapData):void
{
_canvas = value;
}
[Bindable]
public function get canvas():BitmapData
{
return _canvas;
}
public function render(x:int,y:int, data:int):void
{
_canvas.draw(this);
}
]]>
</fx:Script>
<charts:DefaultChartContainer
chart="{data}"
cachePolicy="on"/>
</s:ItemRenderer>
There does not appear to be an out of the box method for implementing smooth scrolling in a Spark List. How would one go about implementing smooth scrolling in a spark list for variable height items?
Edit:
Here is another way to do this:
http://forums.adobe.com/message/3844410#3844410
Ok, so this wont be easy, but it is doable.
1) Create a custom skin for your list
2) In the custom skin, replace the scroller with a custom scroller (MyScroller)
3) Create a new class that extends Scroller, called MyScroller
4) Adobe in their infinite wisdom made skin_mouseWheelHandler private - which they seem to do all over the place, so you would have to override something higher up, maybe attachSkin and detachSkin. Or you could try adding your own skin_mouseWheelHandler with a higher priority in attachSkin and prevent default on it so the default does not get called.
5) Replicate the code in skin_mouseWheelHandler, and modify it to suit your requirements.
Like #Sunil_D. suggested, listening the mousewheel event and manually adjusting the HorizontalScrollPosition is an easy way to handle this. Add the EventListener for your component
chartList.addEventListener(MouseEvent.MOUSE_WHEEL, chartList_mouseWheelHandler);
and increase/decrease the horizontalScrollPosition with, for example, multiples of event.delta
protected function chartList_mouseWheelHandler(event:MouseEvent):void
{
chartList.verticalScrollPosition -= (event.delta * SOME_CORRECT_VALUE);
}
Finding the proper SOME_CORRECT_VALUE might need some experimenting, but it shouldn't be to hard to find.

Where to put layout code

I need some code to run in a component after all the sizes of its children are known. The component has an absolute layout. I thought my component's measure() function would get called after all it's children's measure() functions had been called, but it seems like since it has an absolute layout, it never even calls measure.
Anyways, it's a Canvas and my override protected function measure():void never runs. What am I doing wrong?
updateDisplayList() is called in UIComponent's commitProperties. I believe this is called after the child sizes are known, and it is where you are supposed to put your layout code.
measure() is not called when you explicitly set a width and height on your components. If you have an absolute layout on your Canvas, but do not set a width and height, measure() will be called.
HTH;
Amy
You don't need to override anything, simply add a listener to the creationComplete of your component. This event is "Dispatched when the component has finished its construction, property processing, measuring, layout, and drawing."
Example where the component inherits from s:Group
<?xml version="1.0" encoding="utf-8"?>
<s:Group xmlns:fx="http://ns.adobe.com/mxml/2009"
xmlns:s="library://ns.adobe.com/flex/spark"
xmlns:mx="library://ns.adobe.com/flex/mx" width="400" height="300"
creationComplete="group1_creationCompleteHandler(event)">
<fx:Script>
<![CDATA[
import mx.events.FlexEvent;
protected function group1_creationCompleteHandler(event:FlexEvent):void
{
//TODO add code here
}
]]>
</fx:Script>
</s:Group>

How to have a cornerradius on Flex 4 TextInput component

How do I get corner radius on my TextInput component in Flex 4.
<s:TextInput prompt="username" width="150" maxChars="100" id="txt_username"
color="#000000"/>
Create a custom skin (possibly by copying spark TextInputSkin) and add a border graphic with rounded corners, like this:
<!-- border -->
<s:Rect id="border" left="0" right="0" top="0" bottom="0"
radiusX="10" radiusY="10">
<s:stroke>
<s:SolidColorStroke id="borderStroke" weight="1" />
</s:stroke>
</s:Rect>
If you want more special rounded corners you can also use these attributes:
topLeftRadiusX="4" topLeftRadiusY="8"
bottomLeftRadiusX="2" bottomRightRadiusY="10"
The default TextInputSkin does not allow for rounded corners, so there is no style that you could set on your TextInput to do it. So, no, there's no other way than creating a custom skin class.
You could take it a step further and make it dynamic. Create a custom TextInputSkin based on spark TextInputSkin and in the updateDisplayList method add this code above the super.updateDisplayList() call.
In YourTextInputSkin.mxml,
// in updateDisplayList()
if (getStyle("cornerRadius")!==undefined) {
border.radiusX = border.radiusY = getStyle("cornerRadius");
background.radiusX = background.radiusY = getStyle("cornerRadius");
}
That's it. You're done!
Now to use it add a CSS class selector to add a cornerRadius style like so:
/* set the Textinput.styleName to this style */
s|TextInput.roundedInput
{
contentBackgroundAlpha: .4;
contentBackgroundColor: #000000;
cornerRadius: 10;
skinClass: ClassReference("view.skins.TextInputRoundedSkin");
}
And set your instance to the class,
<s:TextInput styleName="roundedInput"/>
Unfortunately, you can't set the cornerRadius style on TextInput component instance in MXML. Should Flex support a styles tag for this type of thing like HTML tags do? Should styles declared in the Skin be proxied to the component using them? Currently the Flex compiler would throw an error if you declared a style in the Skin and tried to use it on the component instance even though it's valid to declare that style and any other style in the CSS. What about if UIComponents had a style proxy object that let you set styles? Anyway, I digress.
If you want to make that style available on the TextInput instance in addition to the previous methods you can do that by extending TextInput and adding the cornerRadius style metadata to it. You can also set the skinClass (inline or in a defaults.css file in the library) while you're at it.
Something like this:
<?xml version="1.0" encoding="utf-8"?>
<s:TextInput xmlns:fx="http://ns.adobe.com/mxml/2009"
xmlns:s="library://ns.adobe.com/flex/spark"
xmlns:mx="library://ns.adobe.com/flex/mx"
skinClass="TextInputRoundedSkin" >
<fx:Metadata>
[Style(name="cornerRadius", inherit="no", type="uint")]
</fx:Metadata>
</s:TextInput>
To use,
<local:MyExtendedTextInput cornderRadius="8" />
In the future you won't have to declare the CSS.

How can I skin or change the default cursor in Flex?

How can I skin, or otherwise change, the default cursor (white arrow) displayed in a Flex application?
Yes, this is possible. You'll need to leverage mx.managers.CursorManager.
There's no way to replace the cursor graphic, but you achieve this by adding a new cursor to the manager with a high priority:
CursorManager.setCursor(myCursor, CursorManagerPriority.HIGH);
In the above example, myCursor can be a JPEG, GIF, PNG, or SVG image, a Sprite object, or a SWF file. Additionally, setCursor accepts two additional parameters, xOffset:Number = 0, yOffset:Number = 0, which you can use to, well, offset the image from the actual pointer position, if you need to.
Re: Your comment:
I believe you're correct. There's no way I know of to override a components hover cursor other than some event foo. Keep in mind that it is the most recently added cursor with the highest priority (to the `CursorMangager, of course) that gets displayed.
If you wat to change the cursur you need to check the mouse when it is currently over the TextField sub-object of the Flex TextInput control:
<?xml version="1.0" encoding="utf-8"?>
<s:Application xmlns:fx="http://ns.adobe.com/mxml/2009" xmlns:s="library://ns.adobe.com/flex/spark" xmlns:mx="library://ns.adobe.com/flex/halo" minWidth="1024" minHeight="768" mouseMove="application1_mouseMoveHandler(event)">
<fx:Script>
<![CDATA[
protected function application1_mouseMoveHandler(event:MouseEvent):void
{
if(event.target is TextField)
{
if(TextField(event.target).type == TextFieldType.INPUT)
{
Mouse.hide();
}
}
else
{
Mouse.show();
}
}
]]>
</fx:Script>
<mx:TextInput width="300" />
</s:Application>
This is simply making it go away, but you could use the opportunity to make the cursor anything you want by replacing Mouse.hide() with the CursorManager methods described in the other comments. I don't really consider this event "trickery" and overriding the PlayerGlobals.swc class is always going to be more difficult than the open Flex SDK stuff.
Have a look at the following example:
http://blog.flexexamples.com/2007/09/10/changing-the-cursor-in-a-flex-application-using-the-cursormanager-class/

Resources