I'm fairly new to ActionScript/Flex so I'm not entirely sure if this is even possible.
Basically I have the following block repeating several times in my code:
<s:TextInput .. \>
<s:BitmapImage .. \>
What I'm trying to do is create an ActionScript custom component so I can replace the above block everywhere in my code with:
<MyBlock\>
My best guess is I have to do this by extending spark.application?
What I have so far:
package MyPackage
{
import spark.components.Application;
public class MyBlock extends Application
{
..
}
..
}
I am completely at a loss as to how to combine two existing components into a new custom one, if it is even possible.
How exactly should I proceed next? Any help would be appreciated.
Thanks,
It is so much easier than that: for this use case you should simply extend Group. And to make things easier, write your composed component in MXML.
Create a new file MyBlock.mxml (for instance in com/mydomain/components) and add the following code:
<s:Group xmlns:fx="http://ns.adobe.com/mxml/2009"
xmlns:s="library://ns.adobe.com/flex/spark">
<s:TextInput .. />
<s:BitmapImage .. />
</s:Group>
Now simply use this component:
<c:MyBlock />
where the c namespace is defined as xmlns:c="com.mydomain.components.*" at the root node of your document using this class. For example:
<s:Application xmlns:fx="http://ns.adobe.com/mxml/2009"
xmlns:s="library://ns.adobe.com/flex/spark"
xmlns:c="com.mydomain.components.*">
<c:MyBlock />
</s:Application>
Now suppose you want to have a different text in each block, you'll have to expose a property. To do this, lets add a label property to MyBlock:
<fx:Declarations>
<fx:String id="label" />
</fx:Declarations>
To make the TextInput show what's in that variable whenever it changes, we use binding like so:
<s:TextInput text="{label}" />
The final component would look something like this:
<s:Group xmlns:fx="http://ns.adobe.com/mxml/2009"
xmlns:s="library://ns.adobe.com/flex/spark">
<fx:Declarations>
<fx:String id="label" />
</fx:Declarations>
<s:TextInput text="{label}" .. />
<s:BitmapImage .. />
</s:Group>
Now you can create multiple MyBlock instances with different texts:
<c:MyBlock label="hello" />
<c:MyBlock label="world" />
Note that if your regular use of MyBlock is more in a list-like fashion, you may want to consider using a List component with a custom ItemRenderer, rather then using MyBlock over and over again.
Related
I have got a SkinnableComponent:
public class ContentView extends SkinnableComponent
{
[Bindable]
public var titleBar:IVisualElement;
public function ContentView(pContentXML:XML)
{
this.setStyle("skinClass", ContentViewSkin );
}
}
and now i want to display the titleBar in the mxml skin file.
<?xml version="1.0" encoding="utf-8"?>
<s:Skin xmlns:fx="http://ns.adobe.com/mxml/2009"
xmlns:s="library://ns.adobe.com/flex/spark">
<fx:Metadata>
[HostComponent("EMM.App2Go.Viewer.Component.ContentView")]
</fx:Metadata>
<s:DataGroup width="100%" >
<s:dataProvider>
<s:ArrayList source="{hostComponent.titleBar}" />
</s:dataProvider>
</s:DataGroup>
as you can see I managed it with a DataGroup but this is kind of ugly, and I was thinking about an easier way to do this like
<fx:Object source="hostComponent.titleBar" />
or something like that.
I hope you can help me.
You seem to have a deep misunderstanding of how the Spark SKinning Model should work. You should not reference the hostComponent from the skin in order to display items. You should create the element in the skin; and use the same name. So if the hostComponent has a skin part defined like this:
[Bindable]
[SkinPart]
public var titleBar:IVisualElement;
the skin should have something like this:
<s:DataGroup width="100%" id="titleBar" >
</s:DataGroup>
I suggest reading through this information on Spark Skinning, and also learn the Component LifeCycle.
how to I pass in a uicomponent to a mxml custom component?
ex: I want to pass in any number of buttons, and I want my custom component to lay them out in a certain order.
MainApp.mxml:
<?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/mx"
minWidth="955" minHeight="600" xmlns:local="*"
>
<local:myComp >
<s:Button label='Button1' />
<s:Button label='Button2' />
<!--I want to add anything else here -->
</local:myComp>
myComp.mxml:
<?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"
creationComplete="init()"
width="400" height="300"
>
<fx:Script>
<![CDATA[
private function init():void {
// how do I access the added components and place them where I want?
// do I use removeChildren and AddChildren?
// addItemsHere.addchild(nextButton);
]]>
</fx:Script>
<s:HGroup id='addItemsHere' />
As your component extends Group, you shall use addElement instead of addChild (and for all other methods with 'child' in their name it shall be replaced with 'element'. So, access to all elements will be like that:
for(var i:int =0; i < numElements; i++){
var button:Button = Button(getElementAt(i));
doWhatIWantWithMyButton(button);
}
It is also better to override createChildren method of your component if you know what to add at the creation moment.
If you don't need very specific button placement, you can set layout property of your component to any desired layout (like VerticalLayout, for example), and those layouts are tunable.
You seem to be trying to recreate functionality that already exists in the SDK. This is wat SkinnableContainer is for.
Depending on your use case, there are two ways to use it:
You only need to add some custom graphic elements to you custom component, but no additional behaviour
In this scenario, you would simple reuse SkinnableContainer and assign it a custom skin, like so:
<s:SkinnableContainer skinClass="MySkin">
<s:Button label='Button1' />
<s:Button label='Button2' />
</s:SkinnableContainer>
With MySkin.mxml perhaps something like:
<s:Skin xmlns:fx="http://ns.adobe.com/mxml/2009"
xmlns:s="library://ns.adobe.com/flex/spark">
<fx:Metadata>
[HostComponent("spark.components.SkinnableContainer")]
</fx:Metadata>
<s:states>
<s:State name="normal" />
<s:State name="disabled" />
</s:states>
<s:layout>
<s:VerticalLayout/>
</s:layout>
<s:Label text="I'm a SkinnableContainer"/>
<s:Group id="contentGroup" width="100%" height="100%">
</s:SkinnableContainer>
Your Buttons will now automatically be added to the contentGroup; the SkinnableContainer class handles this for you. Note that this Group must have exactly that id; it's a required SkinPart.
You want to add some behaviour to you component
The procedure is the same, but you would now subclass SkinnableContainer (this is usually done in pure ActionScript), write some behaviour in there, and assign the skin to an instance of this class.
public class MyComp extends SkinnableContainer {
//additional behaviour goes here
}
Usage:
<local:MyComp skinClass="MySkin">
<s:Button label='Button1' />
<s:Button label='Button2' />
</local:MyComp>
See the simple application created below. I have a RichTextEditor and a RichText component. The idea is to display whatever typed in the RichTextEditor in the RichText component. Everything else (I think) works except for Bullets! The conversion works as if bullets don't exist!
<?xml version="1.0" encoding="utf-8"?>
<s:Application width="100%"
height="100%"
xmlns:fx="http://ns.adobe.com/mxml/2009"
xmlns:s="library://ns.adobe.com/flex/spark"
xmlns:mx="library://ns.adobe.com/flex/mx">
<s:layout>
<s:HorizontalLayout />
</s:layout>
<fx:Script>
<![CDATA[
import flashx.textLayout.conversion.TextConverter;
import mx.events.FlexEvent;
protected function convert_clickHandler(event:MouseEvent):void
{
richText.textFlow = TextConverter.importToFlow(editor.htmlText, TextConverter.TEXT_FIELD_HTML_FORMAT);
}
]]>
</fx:Script>
<mx:RichTextEditor id="editor" />
<s:Button id="convert" click="convert_clickHandler(event)" />
<s:RichText id="richText" />
</s:Application>
Any idea on how to make Bullets work with RichText ? Am I using the correct conversion method ? TextConverter.importToFlow ?
See the image below.
List items are not a supported by TEXT_FIELD_HTML_FORMAT. You would need to create your own editor that supports the added TextFlow functionality. There is an example of one such editor in Tour de' Flex, but it was written prior to list support in TLF 2. I'm not sure if it's been updated, but if not you will need to add in that functionality yourself.
Good luck!
Objective: I would like to pass Skins to an itemRenderer (which is a Button) of a List, and be able to skin every button in that List.
This is what I have:
List:
<s:List itemRenderer="renderers.ItemRenderer" dataProvider="{collectionWorkspace}" />
ArrayCollection:
<s:ArrayCollection id="collectionWorkspace">
<comp:Layout1 />
<comp:Layout2 />
<comp:Layout3 />
<comp:Layout4 />
<comp:Layout5 />
</s:ArrayCollection>
The Layouts are Skin classes with HostComponent Button.
ItemRenderer:
<?xml version="1.0" encoding="utf-8"?>
<s:ItemRenderer xmlns:fx="http://ns.adobe.com/mxml/2009"
xmlns:mx="library://ns.adobe.com/flex/halo"
xmlns:s="library://ns.adobe.com/flex/spark">
<s:states>
<s:State name="normal" />
</s:states>
<s:Button skinClass="{data}"/>
</s:ItemRenderer>
I get an error (fixed for clarification):
Error: Skin for Application....Button1 cannot be found.
You are handing the skinClass property an instance of the skin class, not the actual class (which the button needs to create its own instance of the skin class).
If you can, the best thing to do would be to make collectionWorkspace be an array of Class objects, not of instances.
<s:ArrayCollection id="collectionWorkspace">
<fx:Class>yourPkg.Layout1</fx:Class>
<fx:Class>yourPkg.Layout2</fx:Class>
<fx:Class>yourPkg.Layout3</fx:Class>
<fx:Class>yourPkg.Layout4</fx:Class>
<fx:Class>yourPkg.Layout5</fx:Class>
</s:ArrayCollection>
If you can't do that, you should be able to pull out the class of the instance and pass it to the skinClass.
<s:Button skinClass="{Object(data).constructor}"/>
EDIT:
The binding by default won't work because data starts off as null before it gets initialized with the class. If you give it null, you will get the exception. To fix it, you will need to return the default for the time between null and value:
<s:Button skinClass="{data != null ? data as Class : spark.skins.spark.ButtonSkin}"/>
I tried doing this with an ArrayCollection using some button skins. It worked.
I have a code like the one below
<mx:Button id="TestingID" width="100%" height="20">
<mx:Script>
<![CDATA[
import flexlib.containers.WindowShade;
]]>
</mx:Script-->
</mx:Button>
I am getting the error "id attribute is not allowed on the root tag of a component"
I have to give a Id to the button to refer to it. What should i do.. how do i solve this problem??
Regards
Zeeshan
if you are calling the component from within itself then you use the 'this' keyword.
<mx:Button height="20">
<mx:Script>
<![CDATA[
import flexlib.containers.WindowShade;
this.percentWidth = 100;
]]>
</mx:Script-->
</mx:Button>
And if you want to refer to the custom component from your application then you do this.
<application xmlns:local = "[Directory containing custom component]">
<local:MyCustomButton id="myButtonInstantiation" />
</application>
Make sense?
An MXML file is essentially a class. So if you want to reference the instance of that class from within it then you just use "this".
This can also happen if you are nesting a component inside another
As soon as you use the <fx:Component> tag you are in the root of an mxml-inline-document
<mx:itemEditor>
<fx:Component>
<mx:TextInput id="precioVenta"/>
</fx:Component>
</mx:itemEditor>
All you need to do is move the id attribute into the tag like this
<mx:itemEditor>
<fx:Component id="precioVenta">
<mx:TextInput />
</fx:Component>
</mx:itemEditor>
This applies to any kind of tag or nesting that causes the Flex compiler to create a new context for the inlinde declaration of a component
If you're defining this in a file as a subclass of Button then you can't set the id here. Put the id in the place you use this new component. For example, if this new component will be an AwesomeButton, you could use it like so:
<mycompnamespace:AwesomeButton id="testingId" />
Let see below code -
<s:ComboBox id="myCombo" dataProvider="{al}">
<s:itemRenderer>
<fx:Component>
<s:CheckBox **id="NOWAY**" click="clickHandler(event)"/>
</fx:Component>
</s:itemRenderer>
</s:ComboBox>
id is not allowed in these sort of scenarios, you use of 'this' keyword, because in this context CheckBox is root tag within Component.