MXML: Default MXML for Different Views - actionscript-3

I have the following default mxml configuration.
<s:ViewNavigatorApplication
xmlns:fx="http://ns.adobe.com/mxml/2009"
xmlns:s="library://ns.adobe.com/flex/spark"
firstView="Home"
creationComplete="init()"
>
Is it possible to have a conditional value for firstView?
I was looking for a way to implement my application in 3 different views for mobile compatibility.So I would like to create different packages for each views. Is there any workaround for this?

You can define the views manually by using the ViewNavigator - remove the firstView from your MXML and do something like that in your init() method:
private function init():void
{
if(something)
{
navigator.pushView(Home);
}
else
{
navigator.pushView(OtherView);
}
}
// pass myData as data to the new view (will be accessible as .data property in the Home view):
navigator.pushView(Home, myData);
// remove the last view from the viewstack:
navigator.popView();
This article might help

Related

Flex: binding a function call

I've been out of Flex development for a while, and I'm trying to understand some code I'm coming across. I've included an example below, but the gist of it is, why are they using {} in the event handler? Isn't that binding? What's it binding for?
In the example below, what's the difference between using anEvent="{doSomething(event)}" and using anEvent="doSomething(event)"?
<mx:Script>
<![CDATA[
private static function doSomething(e:CustomEvent):void {
trace("something happens here");
}
]]>
</mx:Script>
<myComponents:CustomComponent
anEvent="{doSomething(event)}"
/>
// custom component definition
<?xml version="1.0" encoding="utf-8"?>
<mx:UIComponent
xmlns:mx="http://www.adobe.com/2006/mxml"
width="100%"
height="100%">
<mx:Metadata>
[Event(name="anEvent", type="com.mydomain.CustomEvent")]
</mx:Metadata>
<mx:Script>
<![CDATA[
private function test():void
{
var e:Customevent = new CustomEvent("custom_event_name");
dispatchEvent(e);
}
/** #inheritDoc **/
override protected function commitProperties():void
{
super.commitProperties();
test();
}
]]>
</mx:Script>
</mx:UIComponent>
I don't think there is any difference in the two approaches in your case here. Method binding is helpful when you want to bind a particular property of a component to always use a return value from a function.
For example if you had a label and you wanted to bind it's text to a function which returns a string based on a bindable value, it would be useful in such a case.
Have a look here : http://help.adobe.com/en_US/flex/using/WS2db454920e96a9e51e63e3d11c0bf64c3d-7ff3.html#WS2db454920e96a9e51e63e3d11c0bf64c3d-7ff2
In the case you have presented above, it's just an event handler and since it's not returning any value (void), I do not see how the binding can really create a benefit here, hence it should not be used.
Moreover your event handler is a static method and as far as I know binding does not work with static methods
Hope this helps.

Custom Flex 4 data components won't compile within fx:Declarations tags (and aren't allowed outside them)

I have built an extensive library to render complex data from several XML sources, and have abstracted some of these sources as classes which just extend Object. As an experiment, I began extending XMLListCollection in an attempt to make the data-handling implementation more integrated and Flex-like, but thus far mxmlc does not cooperate with a useful message.
The compilation which generates an error:
(fcsh) mxmlc -use-network=true -omit-trace-statements=false -debug=true xmllist_test.mxml
fcsh: Assigned 1 as the compile target id
Loading configuration file /dev/Flex SDK/frameworks/flex-config.xml
Recompile: xmllist_test.mxml
Reason: The source file wasn't fully compiled.
Files changed: 0 Files affected: 1
xmllist_test.mxml(-1): Error: Incorrect number of arguments. Expected 1.
<?xml version="1.0" encoding="utf-8" ?>
(fcsh)
The offending mxml, xmllist_test.mxml:
<?xml version="1.0" encoding="utf-8" ?>
<!-- xmllist_test -->
<s:Application xmlns:fx="http://ns.adobe.com/mxml/2009"
xmlns:mx="library://ns.adobe.com/flex/mx"
xmlns:s="library://ns.adobe.com/flex/spark"
xmlns:maf="mafComponents.*"
creationComplete="makeItSo()"
backgroundColor="#FFFFFF">
<s:layout>
<s:VerticalLayout/>
</s:layout>
<fx:Script>
<![CDATA[
import mafComponents.examples.*;
public var maf:XML = MafExamples.ghp010; // defined statically in the package
public function makeItSo():void
{
trace('name', maf.#name);
}
]]>
</fx:Script>
<fx:Declarations>
<!-- a comment -->
<mx:XMLListCollection id="mafSource1" source="{maf.block}"/> <!-- no problem -->
<maf:MafXMLListCollection id="mafSource2" metasource="maf}"/> <!-- won't compile -->
</fx:Declarations>
</s:Application>
I included in the above xmllist_test.mxml code an example which works using <mx:XMLListCollection>, but the whole compilation fails at the presence of my custom component <maf:MafXMLListCollection>.
I don't know how to interpret the compiler message, considering that xmllist_test.mxml(-1) seems to indicate an error before lines were counted, and the reporting of the xml declaration line.
I couldn't find any documentation on how to use fx:Declarations in this way, although it is stated as a place reserved for non-visual components, which mine is:
package mafComponents
{
import mx.collections.*;
/**
* An extension of XMLListCollection that specifically serializes the "block" elements of a MAF, while processing other information as well.
**/
public class MafXMLListCollection extends XMLListCollection
{
public var maf:XML;
public var mafXMLList:XMLList;
public var name:String;
private var _metasource:*;
/**
* Process the sequence of "blocks" in datasource to an XMLList to use as the parent class's "source" attribute
**/
public function set metasource(datasource:*):void
{
// multiple options for source, get it to the point of an XML with "maf" at the root
if (datasource is XML)
{
maf = datasource as XML;
}
mafXMLList = maf.block; // this expression returns an XMLList
name = 'MafXMLListCollection_' + maf.#name;
source = mafXMLList;
// do other stuff with the data structure here
// ...
}
/**
* #private
**/
public function get metasource():* { return _metasource; }
public function MafXMLListCollection(datasource:*)
{
super();
metasource = datasource;
// make the main list from the sequence of "block" sections in the XML
trace(name + '.length: ' + length);
}
}
}
Despite this problem, I can use the custom component through actionscript in the <fx:Script> tag without compilation errors. I just don't understand if the MXML compilation fails because I am making a syntactic error, or if my misunderstanding is at the philosophical level and I have attempted something that just isn't implemented.
Thank you in advance for any perspective you might offer on the subject.
1) Since MafXMLListCollection doesn't implement IVisualElement, it can't be included in the visual element declaration (hence the warning about the tag being required).
2) Since MafXMLListCollection has parameters to the constructor, you can't use the MXML format declaration. You must create the variable inside your <Script> block:
public var mafSource2:MafXMLListCollection = new MafXMLListCollection(maf);
Or, you could change MafXMLListCollection to have a default constructor, and use the metasource property (which you're already doing in the MXML).

How To Control Icons in TabbedViewNavigator tabs for Flex Mobile

As currently developing an mobile application with Flex Mobile, got to the point of finalising things and make some fine visual layout adjustments. As my Application is built in sections using TabbedViewNavigatorApplication i wanted to add and fine tune icons that appear in the TabbedViewNavigator #tabBar tabs. Well, that was one of those tasks that seems to take minutes, ending in searching Adobe api docs and googling around for hours, eventually to find the solution in days. Said so i want to post my solution, hoping that someone will need it and use it in their own specific situations.
The first thing to mention here is that Adobe per default, has an extensive help library for skinning spark components, but eventually and on the end - those examples often only scratch the surface of what is needed in specific situation and / or implementation.
At first i want to say that I avoid using mxml skins and always do my spark skinning job in AS3 classes using ClassReference. Seems to me that this approach is much more flexible, elegant and cleaner than over-spamming things in mxml. Although sometimes more harder to implement.
So your typical minimal TabbedViewNavigator Application looks like this:
<?xml version="1.0" encoding="utf-8"?>
<s:TabbedViewNavigatorApplication xmlns:fx="http://ns.adobe.com/mxml/2009"
xmlns:s="library://ns.adobe.com/flex/spark" applicationDPI="160" applicationComplete="onApplicationComplete(event)">
<fx:Style source="MyStyles.css" />
<fx:Script>
<![CDATA[
import spark.components.ViewNavigator;
private function onApplicationComplete(e:Event):void {
for each(var vn : ViewNavigator in this.navigators){
/// do something with your VN's ...
vn.icon = "./assets/myFaboulousIcon.png";
}
}
]]>
</fx:Script>
<s:ViewNavigator label="one" width="100%" height="100%" firstView="views.oneView"/>
<s:ViewNavigator label="two" width="100%" height="100%" firstView="views.twoView"/>
<s:ViewNavigator label="three" width="100%" height="100%" firstView="views.threeView"/>
</s:TabbedViewNavigatorApplication>
In this typical configuration you can access the ViewNavigator icon property and set it either in your mxml or from AS3 (eg. in the for loop above) eventually adding a switch statement to select different icons for each tab ... But that is where your story ends. ViewNavigator icon property is used when certain ViewNavigator is represented by a visual component. Which means that in your #tabBar the ViewNavigator is represented visually as a part of navigators stack, and is logical - in this situation it uses the icon specified.
Now let's suppose now you want to change the alpha or colorise your icons programatically, meaning you always use one set of icons but in different situations - you give them different visual properties, and one of those situations can be - their visual appeal in the application #tabBar.
The icon property is a path to the icon image file, and as a type is an generic Object. So you can't change alpha or do anything else with it. Neither icon property will give you any reference to the display object containing the icon itself or anything else you want for your juicy AS3 jonglery pokery ...
For such task we must do some nice spark skinning (in AS3) ;) So read the answer bellow
The first step is to add your css file from example above MyStyles.css (i always do skin class references from within css, found it easy to manage and change if needed) ...
/* MyStyle.css example */
.
.
s|TabbedViewNavigator #tabBar {
skinClass: ClassReference("com.myapp.skins.TabBarSkin");
}
.
.
You must now define your custom TabBarSkin class that can look something like this:
/* TabBarSkin Class */
package com.myapp.skins
{
import spark.skins.mobile.TabbedViewNavigatorTabBarSkin;
import spark.skins.mobile.supportClasses.ButtonBarButtonClassFactory;
import spark.components.ButtonBarButton;
public class TabBarSkin extends TabbedViewNavigatorTabBarSkin
{
public function TabBarSkin() {
super();
}
override protected function createChildren():void
{
if (!firstButton) {
firstButton = new ButtonBarButtonClassFactory(ButtonBarButton);
firstButton.skinClass = TabBarFirstTabSkin;
}
if (!lastButton) {
lastButton = new ButtonBarButtonClassFactory(ButtonBarButton);
lastButton.skinClass = TabBarLastTabSkin;
}
if (!middleButton) {
middleButton = new ButtonBarButtonClassFactory(ButtonBarButton);
middleButton.skinClass = TabBarLastTabSkin;
}
super.createChildren();
}
}
}
Not getting so much in details, you must know that this custom class TabBarSkin inherits from TabbedViewNavigatorTabBarSkin which has 3 skin classes for each of the significant tab positions / first / mid / last / in your #tabBar. In the most simple situation we must implement (extend) two of them / first / and / last -> as the / mid / position also uses the / last / skin, and in this case we don't need it separately implemented.
/* TabBarFirstTabSkin Class */
package com.myapp.skins
{
import spark.components.Group;
import spark.skins.mobile.TabbedViewNavigatorTabBarFirstTabSkin;
public class TabBarFirstTabSkin extends TabbedViewNavigatorTabBarFirstTabSkin
{
private var __iconGroup : Group = null;
public function TabBarFirstTabSkin() {
super();
}
override protected function layoutContents(unscaledWidth : Number, unscaledHeight : Number) : void {
super.layoutContents(unscaledWidth, unscaledHeight);
if(!__iconGroup) {
__iconGroup = getIconDisplay() as Group;
}
}
}
}
Same as for the / last / one:
/* TabBarLastTabSkin Class */
package com.myapp.skins
{
import spark.components.Group;
import spark.skins.mobile.TabbedViewNavigatorTabBarLastTabSkin;
public class TabBarLastTabSkin extends TabbedViewNavigatorTabBarLastTabSkin
{
private var __iconGroup : Group = null;
public function TabBarLastTabSkin() {
super();
}
override protected function layoutContents(unscaledWidth : Number, unscaledHeight : Number) : void {
super.layoutContents(unscaledWidth, unscaledHeight);
if(!__iconGroup) {
__iconGroup = getIconDisplay() as Group;
}
}
}
}
Finally, the __iconGroup member will now have reference to the Group visual component containing your icon! -> and which you defined in your ViewNavigator instance through mxml / or in AS3 code. Now we can go dirty ;) and do things like this for example:
.
.
.
var colorTransform : ColorTransform = new ColorTransform();
colorTransform.color = 0xFF3300;
colorTransform.alphaMultiplier = 0.85;
__iconGroup.transform.colorTransform = colorTransform;
.
.
.
Which will colorise your icon in red and give alpha 0.85. etc ... This is really the basics you can do with spark #tabBar skinning in Flex Mobile. Hope will help someone. Cheers.

Flex, access view.data object into a variable

Below is the code i'm looking at the moment.
<s:View xmlns:fx="http://ns.adobe.com/mxml/2009"
xmlns:s="library://ns.adobe.com/flex/spark" title="data"
xmlns:model="model.*"
creationComplete="data=sqlSearch(data)"
>
In this View, it has just been pushView 'ed with data object. I need to use this data as part of a sql search.
I've used creationComplete one other time in the initial view. My understanding is, on creationComplete, whatever the function, (I'll just name sqlSearch here as the example), is run, and its return value becomes the data to be used in a List.
The error for the creationComplete line is
Multiple markers at this line:
-1137: Incorrect number of arguments. Expected no more than 0.
How should I go about this?
The creationComplete defined by you expects an event handler function.
To make some logic directly to the event use { } like in the following
creationComplete = "{data=sqlSearch(data)}"
I strongly suggest to use a handler function so you can add more logic on creationComplete.
For this take the following sample
<s:View xmlns:fx="http://ns.adobe.com/mxml/2009"
xmlns:s="library://ns.adobe.com/flex/spark" title="data"
xmlns:model="model.*"
creationComplete="handleCreationComplete()"
>
<mx:Script>
<![CDATA[
private function handleCreationComplete():void
{
//Here is my code
data=sqlSearch(data);
}
]]></mx:Script>

How can I "unaccept" a drag in Flex?

Once I've called DragManager.acceptDrag is there any way to "unaccept" the drag? Say that I have a view which can accept drag and drop, but only in certain areas. Once the user drags over one of these areas I call DragManager.acceptDrag(this) (from a DragEvent.DRAG_OVER handler), but if the user then moves out of this area I'd like to change the status of the drag to not accepted and show the DragManager.NONE feedback. However, neither calling DragManager.acceptDrag(null) nor DragManager.showFeedback(DragManager.NONE) seems to have any effect. Once I've accepted the drag an set the feedback type I can't seem to change it.
Just to make it clear: the areas where the user should be able to drop are not components or even display objects, in fact they are just ranges in the text of a text field (like the selection). Had they been components of their own I could have solved it by making each of them accept drag events individually. I guess I could create proxy components that float over the text to emulate it, but I'd rather not if it isn't necessary.
I've managed to get it working in both AIR and the browser now, but only by putting proxy components on top of the ranges of text where you should be able to drop things. That way I get the right feedback and drops are automatically unaccepted on drag exit.
This is the oddest thing about D&D in AIR:
DragManager.doDrag(initiator, source, event, dragImage, offsetX, offsetY);
In browser-based Flex, offsetX and offsetY should be negative (so says the documentation, and it works fine). However, when running exactly the same code in AIR you have to make the offsets positive. The same numbers, but positive. That is very, very weird.
I've tested some more and what #maclema works, but not if you run in AIR. It seems like drag and drop in AIR is different. It's really, really weird because not only is the feedback not showing correctly, and it's not possible to unaccept, but the coordinates are also completely off. I just tried my application in a browser instead of AIR and dragging and dropping is completely broken.
Also, skipping the dragEnter handler works fine in AIR, but breaks everything when running in a browser.
Are you using only the dragEnter method? If you are trying to reject the drag while still dragging over the same component you need to use both the dragEnter and dragOver methods.
Check out this example:
<?xml version="1.0" encoding="utf-8"?>
<mx:Application xmlns:mx="http://www.adobe.com/2006/mxml" layout="absolute">
<mx:Script>
<![CDATA[
import mx.core.DragSource;
import mx.managers.DragManager;
import mx.events.DragEvent;
private function onDragEnter(e:DragEvent):void {
if ( e.target == lbl ) {
if ( e.localX < lbl.width/2 ) {
trace("accept");
DragManager.acceptDragDrop(this);
}
else {
DragManager.acceptDragDrop(null);
}
}
}
private function doStartDrag(e:MouseEvent):void {
if ( e.buttonDown ) {
var ds:DragSource = new DragSource();
ds.addData("test", "text");
DragManager.doDrag(btn, ds, e);
}
}
]]>
</mx:Script>
<mx:Label id="lbl" text="hello world!" left="10" top="10" dragEnter="onDragEnter(event)" dragOver="onDragEnter(event)" />
<mx:Button id="btn" x="47" y="255" label="Button" mouseMove="doStartDrag(event)"/>
</mx:Application>
If you don't need native drag and drop in AIR, you can get the Flex drag and drop behavior by subclassing WindowedApplication and setting the DragManager. See this post on the Adobe Jira for more info: https://bugs.adobe.com/jira/browse/SDK-13983
You are misunderstanding the concept. Your "unaccept" is achieved by implementing the dragOverHandler and signaling that the data is not wanted.
Here is the basic concept:
register the dragEnterHandler or override the already registered method.
function dragEnterHandler(event: DragEvent):void {
if (data suites at least one location in this component)
DragManager.acceptDragDrop(this);
}
This enables your container to receive further messages (dragOver/dragExit). But this is NOT the location to decide which kind of mouse cursor should be displayed.
Without DragManager.acceptDragDrop(this); the other handlers aren't called.
register the dragOverHandler or override the already registered method.
function dragOverHandler(event: DragEvent):void {
if (data suites at least no location in this component) {
DragManager.showFeedback(DragManager.NONE);
return;
}
... // handle other cases and show the cursor / icon you want
}
Calling DragManager.showFeedback(DragManager.NONE); does the trick to display the "unaccept".
register the dragExitHandler or override the already registered method.
function dragOverHandler(event: DragEvent):void {
// handle the recieved data as you like.
}
ok, I see the problem now. Rather than null, try setting it to the dragInitiator.
Check this out.
<?xml version="1.0" encoding="utf-8"?>
<mx:WindowedApplication xmlns:mx="http://www.adobe.com/2006/mxml" layout="absolute">
<mx:Script>
<![CDATA[
import mx.controls.Alert;
import mx.events.DragEvent;
import mx.managers.DragManager;
import mx.core.DragSource;
private function doStartDrag(e:MouseEvent):void {
if ( e.buttonDown && !DragManager.isDragging ) {
var ds:DragSource = new DragSource();
ds.addData("test", "test");
DragManager.doDrag(btn, ds, e);
}
}
private function handleDragOver(e:DragEvent):void {
if ( e.localX < cvs.width/2 ) {
//since null does nothing, lets just set to accept the drag
//operation, but accept it to the dragInitiator
DragManager.acceptDragDrop(e.dragInitiator);
}
else {
//accept drag
DragManager.acceptDragDrop(cvs);
DragManager.showFeedback( DragManager.COPY );
}
}
private function handleDragDrop(e:DragEvent):void {
if ( e.dragSource.hasFormat("test") ) {
Alert.show("Got a drag drop!");
}
}
]]>
</mx:Script>
<mx:Canvas x="265" y="66" width="321" height="245" backgroundColor="#FF0000" id="cvs" dragOver="handleDragOver(event)" dragDrop="handleDragDrop(event)">
</mx:Canvas>
<mx:Button id="btn" x="82" y="140" label="Drag Me" mouseDown="doStartDrag(event)"/>
</mx:WindowedApplication>
Yes, drag and drop is different in AIR. I HATE that! It takes a lot of playing around to figure out how to get things to work the same as custom dnd that was built in flex.
As for the coordinates, maybe play around with localToContent, and localToGlobal methods. They may help in translating the coordinates to something useful.
Good luck. I will let you know if I think of anything else.