Moving children of a container (defined in MXML) inside an "inner container" - apache-flex

I'm currently working on a custom component which extends Canvas (let's call it SuperCanvas) ; it's basically a container that let you zoom & pan its contents.
It would be too long to explain why, but I can't use scrollRect, so I was forced to declare a Canvas object (called innerCanvas)... inside my SuperCanvas (I know, not very nice =/)
I would like to know if there's a proper way to "redirect" the creation of my component's children in this canvas.
Let me explain:
<comp:SuperCanvas id="superCanvas">
<mx:Image id="img" source="image.jpg"/>
<mx:Label id="lbl" text="Sample"/>
</comp:SuperCanvas>
With this, img and lbl are added to my SuperCanvas. I want them to be added to superCanvas.innerCanvas instead.
I can't override the add/removeChild methods to do the "redirection", since I won't be able to add this innerCanvas...
So I tried this :
<comp:SuperCanvas>
<comp:innerCanvas>
<mx:Image id="img" source="image.jpg"/>
<mx:Label id="lbl" text="Sample"/>
</comp:innerCanvas>
</comp:SuperCanvas>
But Flex complains that "In initializer for 'contents': type mx.controls.Image is not assignable to target type mx.containers.Canvas". I read I could use an array of UIComponents with a [ArrayElementType] metatag, and manually instanciate objects, but I I'm looking for a simplier (and probably proper) solution.
I also saw the childDescriptor property (which contains descriptions for every child defined in the MXML file), but it's read-only, so I can't pass it to my innerCanvas.
If I'm not clear enough, do not hesitate to ask me precisions, english isn't my native tongue, so it's pretty hard to explain things well =/
Any help would be greatly appreciated, I'm totally stuck.
EDIT:
My SuperCanvas class (minus the imports and the zoom & pan logic that doesn't matter here) :
public class SuperCanvas extends Canvas
{
public innerCanvas:Canvas = new Canvas();
public function SuperCanvas()
{
super();
addChild( innerCanvas );
}
}

This blog entry details an approach where you add components to the SuperCanvas, but then move them all to the inner canvas after creation. So that's one workaround.
Alternatively, you could set the DefaultProperty to be a dataProvider-type object, and then add things to the inner canvas from there, rather than making them children of the SuperCanvas first.
Addition:
I ran across this blog entry which, among other things, talks about the Panel component and how it handles this problem. You might look at it and at the Panel source code.

Related

How do I access a public function outside of the view it is in using Flex?

Hi, I have been working on a Flex Mobile application using Flash Builder 4.6.
I have 2 mxml 'views' in my project. In one mxml file, i have a function that grabs xml data.
In my other mxml file, I have a refresh button that when depressed is suppsosed to call the function in the first mxml file in order to once again grab the xml data.
I dont know how to call that function from outside the mxml file it is housed in.
I appreciate any help given. Thank you!
[UPDATE #2]*
I thought I should share some more details about my issue.
It is a reddit client mobile app. It fetches the feeds, etc.
In my main view called RedditReaderHomeView.mxml, I am using a splitViewNavigator spark component to house two other views like so:
RedditReaderHomeView.mxml
<s:SplitViewNavigator width="100%" height="100%" id="splitViewNavigator" autoHideFirstViewNavigator="true">
<s:ViewNavigator id="redditList" firstView="views.subredditList" width="300" height="100%"/>
<s:ViewNavigator id="redditFeed" firstView="views.redditFeed" width="100%" height="100%">
<s:actionContent.landscape>
<s:Button id="refreshButtonlLandscape" icon="#Embed('assets/refresh160.png')" click="refreshRSS()" />
</s:actionContent.landscape>
<s:actionContent.portrait>
<s:Button id="refreshButton" icon="#Embed('assets/refresh160.png')" />
<s:Button id="navigatorButton" label="Search" click="splitViewNavigator.showFirstViewNavigatorInPopUp(navigatorButton)" />
</s:actionContent.portrait>
</s:ViewNavigator>
</s:SplitViewNavigator>
As you can see in the code above, in my main view I have a button with the id "refreshButton." When I click this button, I want the reddit data to refresh. In other words I want to call a function to refresh the data, that is housed in the view, 'redditFeed'.
This is the function which is in a separate view named 'redditFeed.mxml', that I want to call using the refresh button in the main view shown above.
redditFeed.mxml
protected function myList_creationCompleteHandler(url:String):void
{
getRedditFeedResult.token = redditFeedGrabber.getRedditFeed(url);
getRedditFeedResult.addEventListener(ResultEvent.RESULT,busyOff);
}
I hope this helped clear out confusion as to what I was trying to do. Im assuming that the solution is quite simple, but alas, I am a novice programmer and new to Flex, so Im learning the ropes. Any help is appreciated. Thank you!
IF you have an instance of the view, then just do:
myViewInstance.myPublicFunction();
In MXML, the id element of the MXML tag is used to reference the view in ActionScript. Since you didnt' describe your architecture; it is unclear how one view can call the other.
If the view that needs to trigger the call is a parent of the view that has the function to make the call, then you could use the approach described above.
If the view that need to trigger the call is a child of the view that has the function to make the call, then you should dispatch an event from the "child" which the parent can listen to. In the event handler you would trigger the call.
If the view that needs to trigger and the view that has the function to make the call are both children of the same parent; then you should dispatch an event from the "Trigger" view, listen for it in the parent, and then use that event listener to make the call (Using similar code to what I explained above).
If you have a more complicated architecture of these two views; then you should look into some method to encapsulate the "remote call" functionality, such as into a service class. Many frameworks offer approaches to share that service class and/or results across multiple classes. ( MXML Files are classes).
There are two ways you can do this without getting into bad architecture by having the child view explicitly know about its parent:
Your child view can generate an event, which the parent is listening for. The parent will then call the function
The child view can have a public property of type Function. The parent view passes a reference to that function by setting the variable. The child view then calls the function (after checking to make sure it is not null).

How can i refer to an external function in an actionscript file - Flashbuilder

I hava a problem with flashbuilder:
I have a list with an itemrenderer that renders an image that (should be) draggable.
the rendered image refers to a function that is declared in an actionscript file: dragDrop.as in the folder AS.
the list:
<s:List id="imageList" width="139" height="438"
dataProvider="{xmlListColl}"
itemRenderer="itemRenderer.ImageRenderer"
dragEnabled="true">
</s:List>
the itemrenderer renders this image and refers to the function doDrag:
<mx:Image width="100" height="70" maintainAspectRatio="true"
MouseDownEffect="AS.dragDrop.doDrag(event)"
source="{data.#thumbnailImage}"/>
the function in dragDrop.as:
public function doDrag(event:MouseEvent):void
{
var img:mx.controls.Image = event.currentTarget as mx.controls.Image;
var dragImg:mx.controls.Image = new mx.controls.Image();
dragImg.source = img.source;
var dsource:DragSource = new DragSource();
dsource.addData(img, 'img');
DragManager.doDrag(img, dsource, event, dragImg);
}
but it seems the function is never called...
also parentdocument and outerdocument don't seem to work (if i put the function in the document where the itemrenderer is called)
Please Help!
There's a few issues here, but ultimately, you're not seeing a reference to that method, which means your dragDrop.as file is not including.
Here are a few suggestions:
Replace MouseDownEffect with mouseDown. Instead of causing an effect to occur, you're now listening for the "MouseEvent.MOUSE_DOWN" event to fire. Differences between effects and responding to events are described here and, to quote, "Behaviors let you add animation and motion to your application when some user or programmatic action occurs, where a behavior is a combination of a trigger paired with an effect. A trigger is an action, such as a mouse click on a component ... An effect is a visible or audible change to the target component that occurs over a period of time, measured in milliseconds."
Make sure you're including your dragDrop.as file. Flex 3 vs. Flex 4 handle script tags differently. If you're not including or importing your code, then of course it won't fire.
include vs import is a good question. You can "include" code that's a definition of methods, instances, constants, etc. But you would "import" defined classes for use. Since your method is a public function, does it live within a class? Or is it meant to just live in the script file, in which case you should remove the accessor "public"
If you're looking to implement Drag-and-Drop, I highly recommend NOT re-inventing the wheel and checking out what Adobe has already implemented for components, including dragEnabled='true' and dragMoveEnabled='true'. Check them out here: http://livedocs.adobe.com/flex/3/html/help.html?content=dragdrop_4.html
http://livedocs.adobe.com/flex/3/html/help.html?content=dragdrop_1.html
Here is an example of a Flex 3 script tag:
<mx:Script source="AS/dragDrop.as"/>
Here is an example of a Flex 4 script tag:
<fx:Script source="AS/dragDrop.as"/>
This is an link to the documentation on how to include directly into a <fx:Script> tag the code you'd like: http://help.adobe.com/en_US/flex/using/WS2db454920e96a9e51e63e3d11c0bf61c8a-7ff4.html

address a Flex checkbox in a component

I have a checkbox in a component:
<s:CheckBox id="myCB_1" />
In my main.mxml I need to test for the state of the checkbox. I originally had all my code in main.mxml, but it was getting really long, and I thought that it was better practice to break my code into components. Also, I have other projects where the same concept will apply, so I really want to figure this out.
I have the following function :
private function checkAlarms(currentTime:Date):void
{
if (!breakfastAlarmSounded)
{
if ((currentTime.hours > breakfastTime.hours) || ((currentTime.hours == breakfastTime.hours) && (currentTime.minutes >= breakfastTime.minutes)))
{
if (myCB_1.selected)
{
playBreakfastAudioAlarm();
}
if (myCB_2.selected)
{
playBreakfastVisualAlarm();
}
breakfastAlarmSounded = true;
}
}
...
simply addressing the component, as in:
myComponent.myCB_1.selected
doesn't work. Someone mentioned that I need to create a variable in my component that refers to the id (myCB_1) of checkbox, but I don't really understand or know how to do that, and they didn't elaborate.
How do I test for the status of the CheckBox "myCB_1" in the component from within my main.mxml?
many thanks,
Mark
(newbie)
With very little information, I'm going to suspect you originally had the CheckBox included in main.mxml and moved it to a custom component. If so, you need to address the CheckBox's ID via the custom component's ID. Something like this (from main.mxml):
if(yourComponentsID.myCB_1.selected)
{
...
}
If this isn't the case, please edit your post and give us more detail.
EDIT
You said you created a new custom component and moved the CheckBox into it. Great, that's a helpful start :) When you included your new component in your main.mxml file, it should look something like this:
<component:YourNewComponent />
Of course, however you named it (and whichever namespace is used to reference it) will be different from my example, but the principle should still apply. In main.mxml, you need to give your custom component a unique ID string so you can reference it within main:
<component:YourNewComponent id="myComponent" />
From here on, you should be able to reference the component, and any public elements within it: myComponent.myCB_1.
It would be useful to provide more details about the context in which you're using this script. Nonetheless I'm going to throw out some information that may help.
In order for the script to access the component, it has to be within the scope of the component. Usually that means one of the following:
You have a <script> tag in the MXML, with code in it that references components within the same MXML file.
You have a <script source='external.as'/> tag in the MXML, where external.as is referencing components in the MXML file.
You are creating the component in your script and you have a definition for the component within ActionScript (ex. var myCB_1:CheckBox; is within the class definition).
If the script and the component aren't within the same scope then they can't see one another.
You need to refer to the checkbox through the component. Lets say that you use your component in your main like this:
<local:MyComponent id="myComponent" />
In your script, you want to refer to it:
if(myComponent.myCB_1.selected) { // do something }
Strangely enough, it works. I was getting a getting an 1119 error (Description 1119: Access of possibly undefined property myCB_1 through a reference with static type Class.) when I refer to the component with dot notation (myComponent.myCB_1.selected) and an 1120 error (Description 1120: Access of undefined property myCB_1) when not addressing it via myComponent.
With these errors I never thought to try running the thing. Long story short - it runs with or without addressing the component (???) go figure!
thanks for all the input and would love to hear any other comments.
MCE

does actionscript addChild require a display object first

Solution:
if you have the same problem, addElement() instead of addChild() is what did it
I'm trying to move away from mxml to actionsctipt. I have a <s:Rect> that I've created and set its properties, but having trouble adding it.
var aRect:Rect = new Rect();
//set properties like aRect.x, aRect.y, aRect.width, aRect.height
//tried adding it various ways
addChild(aRect);
Application.addChild(aRect);
Application.application.addChild(aRect);
stage.addChild(aRect);
But I keep getting the error
1067: Implicit coercion of a value of type spark.primitives:Rect to an unrelated type flash.display:DisplayObject
Originally in the mxml, it was right inside <s:Application> not nested inside anything
<s:Application>
<s:Rect id="aRect" x="10" y="10" width="15%" height="15%">
//then fill code here, removed for readability
</s:Rect>
</s:Application>
What's the deal, I thought actionscript would be nicer than mxml.
tried changing addChild(aRect); to addElement(aRect); and that worked beautifully.
It's because Flex 4 significantly changed the way the display hierarchy works in MXML-based applications. This is a bit confusing since addChild() no longer works as simply as you'd want it to - you have to add elements to a dataprovider, and then the logic of displaying those elements (which ones to add where, how to skin them, etc) is handled elsewhere. It's kind of a useful change, though, because it forces you separate your concerns in a very concrete way. Once you have your elements all added to your dataProvider you can swap out Layout objects at will (even at runtime) to change the way your application looks.
EDIT: Technically it's not the displayList itself that they've changed. It's the fact that the basic unit used by Flex is now the "Group" - even s:Application extends group. You add your content to a a Group (or to the top level Application) and then you assign the group a layout to tell it how to display the items you've added.
Yes, you need a DisplayObject. I'm not familiar with spark.primitives.Rect, but perhaps you could just create a new Sprite and call methods on its Graphics object to draw the rectangle?
According to the live docs, the addChild method of the Application class does require it to be a displayObject.
Annoyingly we will often struggle to add flash assets ( swf swc ) (display objects) using addElement.
I'm working on a way to do this right now :( more hoops and jumping
Also my swc is not viewable in the package explorer (why not ?)

Flex Truncating Button Labels

First and foremost, I apologize for any vagueness in this question. At this point, I'm simply trying to get some new ideas of things to try in order to diagnose this bug.
Anyway, the problem I'm having is with an application that's using a custom moduleloader. That moduleloader has been compiled into an swc and the moduleloader is being instantiated via its namespace. This all works perfectly fine. The problem I'm encountering is specific to mx:button controls used within modules. For whatever reason, their labels are being truncated so, for example, Sign In is showing up with an ellipsis, as Sign ...
After quite a bit of fooling around I have been able to establish the following:
This problem only seems to occur within modules. If a button control is used in the main mxml, the label does not get truncated.
The button control whose label is being truncated does not have a width specified (setting its width to 100% or a specific pixel width doesn't fix the issue)
The button control is using the default padding (messing with the padding by setting left and right to 5 or any other value doesn't help matters either).
We are not using any embedded fonts so I've ruled that out as a possibility as well.
mx:CheckBox and mx:LinkButton are equally impacted by this problem although mx:CheckBox also seems to not want to show its checkbox, it just shows the truncated label.
A potential side affect of this is that attaching a dataprovider to mx:ComboBox causes the combobox control to throw a drawing error but I'm not entirely certain that it's related to the above problem.
One interesting thing I did find while perusing the net for an answer was a mention of fontContext and its relationship to IFlexModuleFactory. There's no specification for fontContext within our implementation of moduleloader so I'm not entirely certain if this could be the issue. In any case, if anyone has any ideas, it would be hugely appreciated. On the other hand, if you know exactly what ails me and can provide me with an answer, I might just wet myself with excitement. It's late. I'm tired. I NEED my Flex app to play nice.
Thanks in advance,
--Anne
Edit: To clarify what I'm looking for with this question, I really just need to know the following:
Could this issue be caused by a namespace conflict?
What else can potentially override the default behavior of labels if no CSS has been implemented?
Has anyone encountered a problem with inheritance being lost while using a custom implementation of moduleloader?
Has anyone encountered this problem or a similar problem with or without using moduleloader?
I'm not sharing any code with this question simply because I'd have to share the entire application and, unfortunately, I can't do that. Again, I'm not looking for the end all, be all solution, just some suggestions of things to look out for if anyone has any ideas.
I've been dealing with this issue myself, off and on and in various forms, for a year, and while I haven't figured out just what's causing it yet, there's clearly a mismeasurement happening somewhere along the line.
What I have been able to to, though, is work around it, essentially by subclassing button-type controls (in my case, Button, LinkButton, PopUpButton, et. al.) and assigning their textField members instances of a UITextField extension whose truncateToFit element simply returns false in all cases:
public class NonTruncatingUITextField extends UITextField
{
public function NonTruncatingUITextField ()
{
super();
}
override public function truncateToFit(s:String = null):Boolean
{
return false;
}
}
The custom component just extends Button (or whatever other button-type control is the culprit -- I've created a half-dozen or so of these myself, one for each type of control), but uses a NonTruncatingTextField as its label, where specified by the component user:
public class NonTruncatingButton extends Button
{
private var _truncateLabel:Boolean;
public function NonTruncatingButton()
{
super();
this._truncateLabel = true;
}
override protected function createChildren():void
{
if (!textField)
{
if (!_truncateLabel)
textField = new NonTruncatingUITextField();
else
textField = new UITextField();
textField.styleName = this;
addChild(DisplayObject(textField));
}
super.createChildren();
}
[Inspectable]
public function get truncateLabel():Boolean
{
return this._truncateLabel;
}
public function set truncateLabel(value:Boolean):void
{
this._truncateLabel = value;
}
}
... so then finally, in your MXML code, you'd reference the custom component thusly (in this case, I'm telling the control never to truncate its labels):
<components:NonTruncatingButton id="btn" label="Click This" truncateLabel="false" />
I agree it feels like a workaround, that the component architecture ought to handle all this more gracefully, and that it's probably something we're both overlooking, but it works; hopefully it'll solve your problem as you search for a more definitive solution. (Although personally, I'm using it as-is, and I've moved on to other things -- time's better spent elsewhere!)
Good luck -- let me know how it works out.
I've used the custom button and link button class solutions and still ran into problems - but found a workaround that's worked every time for me.
Create a css style that includes the font you'd like to use for you label. Be sure to check 'embed this font' right under the text selection dropdown. Go back and apply the style to your button (or your custom button, depending on how long you've been bashing your hear against this particular wall), and voila!
Or should be voila...
I just came across this issue and solve it this way:
<mx:LinkButton label="Some label"
updateComplete="event.target.mx_internal::getTextField().text = event.target.label"
/>;
I've had some success preventing Flex's erroneous button-label truncation by setting labelPlacement to "bottom", as in:
theButton.labelPlacement = ButtonLabelPlacement.BOTTOM;
Setting the label placement doesn't seem to help prevent truncation in some wider button sizes, but for many cases it works for me.
In cases where you can't use a bottom-aligned button label (such as when your button has a horizontally aligned icon), janusz's approach also seems to work. here's a version of janusz's .text reassignment technique in ActionScript rather than MXML:
theButton.addEventListener(FlexEvent.UPDATE_COMPLETE, function (e:FlexEvent):void {
e.target.mx_internal::getTextField().text = e.target.label;
});
The preceding code requires you to import mx_internal and FlexEvent first, as follows:
import mx.events.FlexEvent;
import mx.core.mx_internal;
And here are the results…
Before (note truncation despite ample horizontal space):
After:
The only downside to this approach is you lose the ellipsis, but in my case I considered that a welcome feature.

Resources