Inheritance with Flex mxml files - screenshot attached - actionscript-3

In a Flex mobile app I have 5 Views, handling OAuth logins through 5 different social networks - including Google+ and Facebook. The Views are being selected through the menu shown below:
The filenames are FB.mxml, GG.mxml, MR.mxml, OK.mxml, VK.mxml and their source code looks very similar:
<?xml version="1.0" encoding="utf-8"?>
<s:View xmlns:fx="http://ns.adobe.com/mxml/2009"
xmlns:s="library://ns.adobe.com/flex/spark"
viewActivate="loadInfo(event)"
viewDeactivate="closeSWV(event)"
title="Facebook.com">
....
private function closeSWV(event:Event=null):void {
_busy.visible = _busy.includeInLayout = false;
_reload.visible = _reload.includeInLayout = true;
stage.removeEventListener(Event.RESIZE, resizeSWV);
if (! _swv)
return;
_swv.removeEventListener(Event.COMPLETE, extractAccessToken);
_swv.removeEventListener(LocationChangeEvent.LOCATION_CHANGE, extractAccessToken);
_swv.removeEventListener(IOErrorEvent.IO_ERROR, closeSWV);
_swv.removeEventListener(ErrorEvent.ERROR, closeSWV);
_swv.dispose();
_swv = null;
}
private function resizeSWV(event:Event=null):void {
if (! _swv)
return;
// align to the right-bottom corner
_swv.viewPort = new Rectangle(stage.stageWidth - Preferans.SCALE * width,
stage.stageHeight - Preferans.SCALE * height,
Preferans.SCALE * width,
Preferans.SCALE * height);
}
....
<s:VGroup>
<s:Label id="_first" fontWeight="bold" text="Your name:" />
<s:Label id="_gender" fontWeight="bold" text="Gender:" />
<s:Label id="_city" fontWeight="bold" text="City:" />
</s:VGroup>
<s:Button id="_play"
label="Play"
click="startGame(event)"
includeInLayout="false"
visible="false" />
</s:View>
My problem is: The 5 mxml files listed above have many similar methods and variables and UI elements and only few different methods and variables.
I've tried to introduce a "base class" for them all several times already and have always given up, because it is not straightforward for mxml files (versus pure AS3 classes).
Does anybody please have an idea, how to approach this?

You can define an abstract class that extends the UI component that you want to use in each row of the list. In your case, it will extend View.
package your.package{
public class SocialAccountView extends View{
// in this class you can add the generic methods
// that you want the view to have
}
}
After that, in the mxml file you can use the class you created as the main type instead of View
<your.package:SocialAccountView
xmlns:fx="http://ns.adobe.com/mxml/2009"
xmlns:s="library://ns.adobe.com/flex/spark"
viewActivate="loadInfo(event)"
viewDeactivate="closeSWV(event)"
title="Facebook.com">
</your.package:SocialAccountView>
With this you know that all the items in the list are of that type, and have the methods of the parent class. You can even override methods in each view.
Regarding the UI components, the maximum abstraction level (that I am aware of) you can have with them is to build an extra component and use it in all views. Something like...
Labels.mxml
<s:VGroup>
<s:Label id="_first" fontWeight="bold" text="Your name:" />
<s:Label id="_gender" fontWeight="bold" text="Gender:" />
<s:Label id="_city" fontWeight="bold" text="City:" />
</s:VGroup>
and then use it in each component like
<your.package:Labels
id="labelsComponent"/>
Hope this helps.

Related

Actionscript 3 setStyle is not a function

I am trying to style a Flex 4 GridItem using actionscript, I have tried the following:
<mx:VBox
height="878" width="1920"
xmlns:mx="http://www.adobe.com/2006/mxml"
xmlns:s="library://ns.adobe.com/flex/spark" xmlns:local="*" creationComplete="addStyles()">
<mx:Script>
<![CDATA[
public var selectedLot:String = "";
private function addStyles():void
{
testBorder.setStyle("borderThickness", "3");
}
but I get the following error:
setStyle is not a function.
Am I missing something?
The GridItem is inside a repeater.
Here is my GridItem:
<mx:GridItem id="testBorder" width="101" height="52.25" horizontalAlign="center" verticalAlign="middle" borderStyle="solid" borderColor="gray">
<mx:Image source="assets/ruler-icon.png" />
<s:Label text="{r.currentItem.sqft}" fontSize="10" color="#808080" fontFamily="Helvetica" />
</mx:GridItem>
When using a repeater the GridItem's id will not be the same. To access any item inside a repeater you have to specify an index which is correspondent to the repeated item.
Example: Array consists of ["Audi", "BMW"], we set this array to your repeater's dataProvider, then to access "Audi"'s grid item and set its style, we have to use:
testBorder[0].setStyle("borderThickness", "3");
Additionally, an important point to consider, the VBox "creationComplete" can be executed before the repeater is fully built, therefore, the best place to call your function "addStyles" is in the repeater's "repeatEnd" event i.e (repeatEnd="setTransactionPropertyType()").
Hope this helps,
Goodluck.

Flex: how to control Spark datagrid header-text alignment?

If I have 10 columns in a Spark datagrid, and some headers need to be left-justified, some headers right-justified, and some centered, what's the simplest way to accomplish this?
Assuming a custom headerRenderer is needed, are there any simple examples that can walk me through it?
Thanks in advance for any comments.
The simplest way I could find to solve this is to override the settings in the spark DefaultGridHeaderRenderer, as discussed in this link:
http://flexponential.com/2011/10/30/changing-fontweight-of-spark-datagrid-headers/
More specifically, I used the following custom headerRenderer, saved as file: myRightHeaderRenderer.mxml:
<?xml version="1.0" encoding="utf-8"?>
<s:DefaultGridHeaderRenderer xmlns:fx="http://ns.adobe.com/mxml/2009"
xmlns:s="library://ns.adobe.com/flex/spark"
xmlns:mx="library://ns.adobe.com/flex/mx" >
<fx:Declarations>
<s:Label id="labelDisplay"
verticalCenter="1" left="0" right="0" top="0" bottom="0"
verticalAlign="middle"
maxDisplayedLines="1"
textAlign="right"
fontWeight="bold"
showTruncationTip="true" />
</fx:Declarations>
</s:DefaultGridHeaderRenderer>
This custom header renderer right-justifies header text. To use it, simply add it to one or more columns of the Spark DataGrid as follows:
...
<fx:Array>
<s:GridColumn ... />
<s:GridColumn headerRenderer="myRightHeaderRenderer" ...>
<s:GridColumn ... />
...
</fx:Array>
...
I'm not sure how to do it, but I'm sure it can be made more flexible by parameterizing the textAlign attribute to be center, left, or right.
If you take a look at this blog post, there's a decent amount of source code available showing you how to do this.
However, I think that the blog's example is much more complex than you'll need. You will need a custom headerRenderer, as you feared, but your code should be pretty straightforward. I've only tested this lightly, so if you have any issues, let me know.
Custom Header Renderer
package
{
import spark.skins.spark.DefaultGridHeaderRenderer;
public class CustomGridHeader extends DefaultGridHeaderRenderer
{
public function CustomGridHeader()
{
super();
}
public function set headerTextAlign(value:String):void
{
labelDisplay.setStyle("textAlign",value);
labelDisplay.styleChanged("textAlign");
}
}
}
Variables Available to Your Columns...
[Bindable] private var leftFactory:ClassFactory = new ClassFactory(CustomGridHeader);
[Bindable] private var rightFactory:ClassFactory = new ClassFactory(CustomGridHeader);
[Bindable] private var centerFactory:ClassFactory = new ClassFactory(CustomGridHeader);
On initialize or preinitialize...
leftFactory.properties = {headerTextAlign: "left"};
rightFactory.properties = {headerTextAlign: "right"};
centerFactory.properties = {headerTextAlign: "center"};
For Each Column...
<s:GridColumn headerText="..." headerRenderer="{centerFactory}"/>

Flex Mobile : How to skin view?

I would like to know if it's possible to skin view for my Flex mobile application :
My ActivityView.as
public class ActivityView extends View
My ActivityViewSkin.mxml (It skin associated)
<?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("com.corp.views.activity.ActivityView")]
...
It's a good way for mobile development ?
And how can I use this in this skin :
<s:navigationContent>
Thank you very much !
Anthony
no.
Spark skin is not optimized for mobile. you should use MobileSkin . (Action script only).
I have been looking for similar information, however everything I can deduce from documentation and blogs implies that MobileSkin is something you do for component-level skinning (e.g. buttons, lists, itemRenderers, etc.), things that would be used many times throughout the app.
THe other reason to think you might be able to get away with skinning your View via MXML is that all the Views I have seen code for are done so declaratively (MXML) and by skinning the View subclass using just the Skin class, you are only adding one more layer of hierarchy via the contentGroup in most skinnableContainer skins.
If you are using spark.components.View, then you are using a skin associated with as it is a SkinnableContainer. It is NOT a simple group as you might think.
I dunno, I am kinda at a loss as to where to focus my efforts. I am sure performance implications (if any) will rear their heads way later in the development stage.
From experience so far, you don't skin the View. You skin the ViewNavigatorApplication. First, create the custom skin:
public class DViewNavigatorApplicationSkin extends ViewNavigatorApplicationSkin
{
[Embed(source="assets/wine_240.jpg")]
protected var cornerImage:Class;
public function DViewNavigatorApplicationSkin()
{
super();
}
override protected function drawBackground(unscaledWidth:Number, unscaledHeight:Number):void
{
graphics.beginFill(0x000000);
graphics.drawRect(0,0, unscaledWidth, unscaledHeight);
graphics.endFill();
var ba:BitmapAsset = new cornerImage() as BitmapAsset;
var translateMatrix:Matrix = new Matrix();
translateMatrix.tx = unscaledWidth - ba.width;
translateMatrix.ty = unscaledHeight - ba.height;
graphics.beginBitmapFill(ba.bitmapData, translateMatrix);
graphics.drawRect(unscaledWidth - ba.width + 1, unscaledHeight - ba.height + 1, ba.width, ba.height);
graphics.endFill();
}
The contents of drawBackground docks the image to the lower right-hand corner of the display. You can draw anything in this function.
Then in the theme.css:
s|ViewNavigatorApplication
{
color: #ffffff;
focusColor: #ff9999;
skinClass: ClassReference("com.domain.skins.mobile.ThemeName.DViewNavigatorApplicationSkin");
}
s|View
{
backgroundAlpha: 0;
}
You draw the background image on the application itself. You then make the View totally transparent so that you can see the background image through it.
It may be possible to skin each individual view, but so far, it seems more practical to skin the application instead.
It is kinda late to answer this question. Actually, we can use Spark Skin to skin the View component without any problem. View is just a subclass of SkinnableContainer (which is subclass of SkinnableComponent) so by default, whatever content you add directly to the MXML of View component will be added to contenGroup of SkinnableContainer.
I have added an example to skin the View using Spark Skin:
Main Application:
<s:Application xmlns:fx="http://ns.adobe.com/mxml/2009"
xmlns:s="library://ns.adobe.com/flex/spark" applicationDPI="160">
<fx:Script>
<![CDATA[
import com.accessdigital.core.SimpleView;
]]>
</fx:Script>
<fx:Style>
#namespace s "library://ns.adobe.com/flex/spark";
#namespace core "com.accessdigital.core.*";
core|SimpleView{
skinClass : ClassReference("skin.view.Skin_SimpleView");
}
</fx:Style>
<s:ViewNavigator width="100%" height="100%"
firstView="{SimpleView}">
</s:ViewNavigator>
</s:Application>
View Class
public class SimpleView extends View
{
public function SimpleView()
{
super();
}
[SkinPart(required="true")]
public var myButton:Button;
override protected function createChildren():void{
super.createChildren();
var anotherButton:Button = new Button();
anotherButton.label = "Another button";
anotherButton.addEventListener(MouseEvent.CLICK, onAnotherButtonClick);
if(!actionContent){
actionContent = [];
}
actionContent.push(anotherButton);
}
protected function onAnotherButtonClick(event:MouseEvent):void
{
trace("This is another button");
}
override protected function partAdded(partName:String, instance:Object):void{
super.partAdded(partName, instance);
if(instance == myButton){
myButton.addEventListener(MouseEvent.CLICK, onButtonClick);
}
}
protected function onButtonClick(event:MouseEvent):void
{
trace("This is a simple button");
}
}
Skin File:
<s:Skin xmlns:fx="http://ns.adobe.com/mxml/2009"
xmlns:s="library://ns.adobe.com/flex/spark">
<!-- host component -->
<fx:Metadata>
[HostComponent("com.accessdigital.core.SimpleView")]
</fx:Metadata>
<!-- states -->
<s:states>
<s:State name="disabled" />
<s:State name="normal" />
</s:states>
<!-- SkinParts
name=myButton, type=spark.components.Button, required=true
name=contentGroup, type=spark.components.Group, required=false
-->
<s:Rect width="100%" height="100%">
<s:fill>
<s:LinearGradient rotation="90">
<s:GradientEntry color="#666666"/>
<s:GradientEntry color="#222222"/>
</s:LinearGradient>
</s:fill>
</s:Rect>
<s:Group id="contentGroup" width="100%" height="100%">
<s:Button id="myButton" label="My Button" horizontalCenter="0" verticalCenter="0"/>
</s:Group>
</s:Skin>
Hope it helps

Flex custom component, best way to use it

I have not completly understood how custom components work...
Let's assume I have my Main.mxml application
<?xml version="1.0" encoding="utf-8"?>
<s:WindowedApplication xmlns:fx="http://ns.adobe.com/mxml/2009"
xmlns:s="library://ns.adobe.com/flex/spark"
xmlns:mx="library://ns.adobe.com/flex/mx"
xmlns:local="*">
<fx:Script>
<![CDATA[
private var privateStr:String = "Stringa Private";
public var publicStr:String = "Stringa Public";
]]>
</fx:Script>
<local:AddUser height="100" width="500"/>
<s:Label id="lblText" x="120" y="120" width="418" height="115" text="!!!"/>
</s:WindowedApplication>
And the component AddUser.mxml
<?xml version="1.0" encoding="utf-8"?>
<mx:VBox
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="initialize_component()">
<fx:Script>
<![CDATA[
public var btnName:String = "Login";
private function initialize_component():void
{
login.label = btnName;
}
private function doLogin():void
{
//some stuff here
}
]]>
</fx:Script>
<s:TextInput id="txtuser" x="96" y="36"/>
<s:TextInput id="txtpass" x="96" y="66"/>
<s:Button id="login" x="96" y="96" width="128" click="doLogin()" />
</mx:VBox>
I would like that on the Button (login) click I get the publicStr/privateStr that are in the main.mxml...
Am I getting everything wrong? how can I use more components like they are all part of the same application and use the same variables/methods?
It seems like you're having issues with the idea of encapsulation. Child components shouldn't know about parent components, and View components shouldn't do real work, only request work from Controller components. In very simple projects, your top level component can contain the controller logic, but many people prefer to keep it separate even in small projects. How to do this is beyond the scope of this answer.
So, how should the parent and child properly communicate? Child components should expose properties that the parent (or Framework, if you're feeling ready to use a dependency injection framework) can populate with only the data the child components need.
Child components request work from the controller by generating events.
So, doLogin() would containe something like
dispatchEvent(new Event('doLogin'));
and the parent component would be listening for this Event. In its handler, you would perform the login. More than likely, your login would be asynchronous, so you'll need another handler to listen for the login data to come back. When the login data comes back, you will then set the properties on the login View based on the return.

Why won't visual elements display inside custom component extended from another custom component

I created a custom MXML component, TurboContent, that extends the NavigatorContent class:
<s:NavigatorContent xmlns:fx="http://ns.adobe.com/mxml/2009"
xmlns:s="library://ns.adobe.com/flex/spark"
xmlns:mx="library://ns.adobe.com/flex/mx">
<fx:Metadata>
[DefaultProperty("mxmlContentFactory")]
</Fx:Metadata>
<s:Group id="midWrapper">
<s:Button id="btnAdd" />
</s:Group>
<s:Group id="rightWrapper" >
<s:DataGrid id="dgdSelect" >
<s:columns>
<s:ArrayList>
<s:GridColumn headerText="Yo"></s:GridColumn>
</s:ArrayList>
</s:columns>
</s:DataGrid>
<s:Button id="btnRemove" />
<s:Button id="btnClear" />
</s:Group>
</s:NavigatorContent>
I am trying to extend that custom component but when I add display elements to the second extended componet they elements are never seen. For instance: (The first custom component is in the comp package)
<comp:TurboContent xmlns:fx="http://ns.adobe.com/mxml/2009"
xmlns:s="library://ns.adobe.com/flex/spark"
xmlns:mx="library://ns.adobe.com/flex/mx"
xmlns:comp="comp.*">
<s:Button id="myButton"/>
</comp:TurboContent>
In this case, the 'myButton' component never shows up, but all the elements of the base component do (3 buttons and a datagrid).
I'm curious to understand what you're trying to achieve?
From the pieces of code it seems you're trying to compose a new view which visually inherits the TurboContent-component and adds another button to it.
What happens really under the hood is that the NavigatorContent extends SkinnableContainer. SkinnableContainer has as a default property mxmlContentFactory, which once initialized can not be changed or substituted, or add stuff on top of it, unless you do that with ActionScript:
mxmlContentFactory = null; // or some IDeferredInstance
But then your approach of visual inheritance won't be visual inheritance, but content substitution.
The base class already has initialized it, so the subclasses can't do modifications to it.
From what I can tell, the pattern of defining a component directly in MXML (as you referenced was done in the FIAW series) disallows the ability to then visually insert children in the container's display list. One of the problems is that the mxmlContent (which normally a skin would control) is statically defined in the component and it doesn't seem like one can use contentGroup inside the MXML component directly.
For better control, and what I consider a more strict implementation of the MVC pattern (which Flex 4, as a framework, implements), try separating your visual layout out into an MXML skin, and defining the component in AS3.
From what little I see of your component, I can't really make a judgment as to what properties of the component you want to expose to the container that will instantiate it. I'll at least give an example here of how one can pass info from the component to the skin.
I apologize for the MX components, but I only have Flex 4.1, and I wanted to make sure the program compiled fine. It shouldn't be too hard for you to swap in the spark versions.
Example Component (TurboContentAS.as)
package components {
import mx.controls.DataGrid;
import spark.components.NavigatorContent;
public class TurboContentAS extends NavigatorContent {
public function TurboContentAS() {
super();
}
// Skin Parts that constitute the necessary parts of the component
[SkinPart(required="true")]
public var dgdSelect:DataGrid; //just an example
// property you want to expose to the instantiating object
[Bindable]
public var firstColumnHeader:String = "default header";
}
}
Example Skin (TurboContentSkin.mxml)
<?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"
xmlns:mx="library://ns.adobe.com/flex/mx"
alpha.disabled="0.5" >
<fx:Metadata>[HostComponent("components.TurboContentAS")]</fx:Metadata>
<s:states>
<s:State name="normal" />
<s:State name="disabled" />
</s:states>
<!-- this is where the children that you add later go-->
<s:Group id="contentGroup" left="0" right="0" top="100" bottom="0" minWidth="0" minHeight="0">
<s:layout>
<s:BasicLayout/>
</s:layout>
</s:Group>
<s:Group id="midWrapper">
<s:Button id="btnAdd" label="Add" />
</s:Group>
<s:Group id="rightWrapper" left="200">
<s:layout>
<s:VerticalLayout/>
</s:layout>
<mx:DataGrid id="dgdSelect">
<mx:columns>
<fx:Array>
<!-- This will bind to the publicly exposed property in the component definition -->
<mx:DataGridColumn headerText="{hostComponent.firstColumnHeader}"/>
</fx:Array>
</mx:columns>
</mx:DataGrid>
<s:Button id="btnRemove" label="Remove"/>
<s:Button id="btnClear" label="Clear"/>
</s:Group>
</s:Skin>
Example Instantiation
<components:TurboContentAS skinClass="skins.TurboContentSkin" firstColumnHeader="myHeader">
<s:Button label="myButton"/>
</components:TurboContentAS>