I'm new to Adobe Flex, and am working on a desktop Air application. The jist of my problem is figuring out how a child component can be aware of one of its parents properties. The parent might be more than one level up.
Here's a simplified example to illustrate.
I have a custom component that is made up of a few child components, in the example there is only a single button. I have several instances of this custom component next to each other. When one of the child buttons is pressed, I want to generate an event that lets another part of my application know which instance of the custom component the button belonged to. I understand how to create custom events, so I left that part out of the sample code.
My current approach involves creating a property, (e.g. a "position" integer) that I assign to each of the parent components in mxml. The children components also have the position property, which is set to match the parent when the "preinitialize" event occurs. According to the component lifecyle, the preinitialize event occurs in parent components first, then goes down the line of children. Is this safe? Or is there a better way to do this?
Using data binding seems a waste of resources, because the position property never changes after the application loads.
Here are some relevant code snippets.
main.mxml
<s:VGroup>
<components:CustomComponent position="0"/>
<components:CustomComponent position="1"/>
<components:CustomComponent position="2"/>
</s:VGroup>
CustomComponent.mxml
<fx:Script>
<![CDATA[
[Bindable]
public var position:int;
]]>
</fx:Script>
<components:CustomButton
preinitialize="(event.target as CustomButton).setPosition(position)"/>
CustomButton.mxml
<s:Button>
<fx:Script>
<![CDATA[
public var position:int;
public function setPosition(position:int):void
{
this.position = position;
}
]]>
</fx:Script>
</s:Button>
"how a child component can be aware of one of its parents properties."
-> this.parent.propertyName;
The parent might be more than one level up.
-> Then it is not a parent, but an ancestor. You might need to go up the parent chain. this.parent.parent.
Also look into this.document and this.owner. For list based controls these mean different things.
Finally, Look into event bubbling. Sounds like you want your events to bubble up to your top level container.
Generally, this is where a framework/microarchitecture comes into play. You have to keep in mind, that the Flex/Adobe Air library is first and foremost a component library. That means you don't get anything out of the box to help with things such as communication between different part of the app. Sometimes folks will try to use the Singleton Pattern to manage dependencies, but I can tell you from experience that is a horrible mistake that will cost you in the long run as the code becomes very brittle and fragile with high cyclomatic complexity.
Nowadays, there are many frameworks designed to help you with the problem and others that you have and will face. A list of these frameworks can be found through Google, but Swiz, Mate, Robotlegs and Parsley is a great starting point.
There are other frameworks that you may run across such as Cairngorm and PureMVC. Those are generally termed "first generation" frameworks. Personally, I shy away from those because they feel a little too heavy an intrusive.
Related
When creating custom layouts in Flex, you generally override LayoutBase, in which case the documentation and tutorials on the Internet say you "have to" override the functions updateDisplayList() and measure(). . .Huh?
I don't think there is a construct in AS3 to syntactically require a function to be overridden, and there sure isn't one here. There's not even as special logic to raise unhandled Errors or anything. This works perfectly fine:
<?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="*" backgroundColor="#000000" showStatusBar="false" click="createImage()">
<fx:Script>
<![CDATA[
import spark.components.Image;
[Embed(source="AL.png")]
private static const IMAGE:Class;
private var iX:int = 0;
private var iY:int = 0;
private function createImage():void
{
var img:Image = new Image();
img.source = IMAGE;
img.width = 100;
img.height = 100;
img.x = iX
img.y = iY;
iX += 100;
iY += 100;
grp.addElement(img);
}
]]>
</fx:Script>
<s:Group id="grp" width="400" height="400">
<s:layout>
<GridLayout/>
</s:layout>
</s:Group>
</s:WindowedApplication>
And in my GridLayout.as class:
package
{
import spark.layouts.supportClasses.LayoutBase;
public class GridLayout extends LayoutBase
{
}
}
Each time I click on the application, a new image is spawned at (x, y) * n, where n is the number of images previously spawned. It works perfectly fine. I even added code so that right-clicking would take an already existing image and move it around on the screen, and that worked fine as well.
So I guess my question is. . .Why in the world are they saying this is absolutely required? What do they mean? I can understand the documentation and stuff saying that those two functions are where you want to concentrate your customizations at or something like that, but in programming, saying that a function override is required is some pretty strong language; if stuff holds up just fine without you overriding those functions, why would both documentation and independent tutorials be saying this? Thanks!
Why in the world are they saying this is absolutely required?
Since I don't know what documentation you're reading; it is tough to say. The documentation could be wrong; or you could be taking something out of context. It is not required that you write any sort of code in an updateDisplayList() or measure() method, but...
What do they mean?
In the context of creating Flex components; it makes sense to make use of the Flex Component LifeCycle methods, for either MX components or Spark Components. The lifecycle methods (createChildren(), commitProperties(), updateDisplayList(), and measure() are executed by the Flex Framework and make use of an invalidation cycle built right into the framework.
The framework methods help you put "similar" code in the same place so you don't have it all over the place.
The Spark Framework changed things a bit by providing the option of separating layout and sizing code (AKA updateDisplayList() and measure() out of the main class and into a specific layout class.
saying that a function overload is required is some pretty strong
language
I'd be surprised if you've read any docs that say that overloading is required. Overloading is the act of creating multiple functions with the same name, but different arguments. This is not supported in ActionScript. Overriding is the act of extending a function in a child component in order to change or extend functionality of that function. They are different concepts; and I'm unclear if that was a typo on your part or if you are confused about some tenants of object oriented programming.
Does this help? [Unfortunately, I don't have the time right now to try to re-work your sample to make proper use of Flex Framework conventions.
Why in the world are they saying this is absolutely required?
I understand your concern, these overriding functions are required for the components to works optimally. These components don't require to follow these component lifecycle principles if your applications is small, the component does not change frequently and no body else touches your class.
But, If your app needs to change these components frequently then you might have enforce these lifecycle principles.
For example: you want to Prepare Coke, steps(I'd say life cycle) would be take water > mix sugar > add colors > mix soda > bottle it
Now you get orders to change the color of the Coke that you just prepared, the life cycle is already completed, if you need to change anything at this point, you need to put a lot of effort even to change the color of it. If you had followed the life cycled, it makes life easier for the app at run time. No performance issues.
I have a menu with five buttons. Menu is visible all the time. there is click event for each menu item. which slides corresponding movie clip from left to right. each movie clip has different nature events and respective animation and activity. for example tab 1 brings the video page. and within that movie clip I have video events like play pause volume and on complete etc. events and code. tab 2 has button group for Time and another button group Features. depending on user selection code will calculate and show value on a animated counter. tab 3 has button group for Time and button group Source. as per the user selection it will calculate and show the values as animated graph. and so on.
Right now I have all the individual tab movie clip has its own time line code for its own events. and some crossover variables and references with other tabs. Everything is working as expected. No problem. I know time line code is not the best way to do any complex project.
So, I would like to get the entire coding as one class or more classes if that is the correct way.
I am beginner as far as class logic. I have already created Main as document class and could control the general navigation of tabs and their initial look. But stuck at tab specific button events and other such unique events for the specific tab.
Any help is greatly appreciated. Thanks in advance.
any similar example or suggestions.
First of all, thanks a lot for a prompt response. It seems like I am not even a beginner. I need to read a lot and probalbly grasp all fundamental concepts thoroughly. I have gone through both the links suggested in your comments. I am trying to digest the stuff slowly. I do not have any formal informal education regarding OOP or any sort of programming. To be honest, I have hard time understanding the code you have suggeted. Not because of your code but because of my level of caliber. I will have to spend some time to make myself clearer regarding events and sequence etc. different tab contents are as movieclips to main timeline and already placed on stage. It comes and goes to its corresponding tab button click event. I am not marking your answer as yes because I still need to my own homework based on your suggestion. Thanks a lot once again. I am sure I will ask few more questions later.
This is how I would design it:
I'd have a Menu Class, which only contains the buttons and "converts" clicks on them into more specific events. That might look something like this:
public Class Menu extends Sprite {
protected var buttons:Vector. = new Vector.();
public function Menu() {
super();
var loops:int = numChildren;
for (var i:int=0; i<loops; i++) {
var button:SimpleButton = getChildAt(i) as SimpleButton;
if (button) {
buttons[buttons.length] = button;
button.addEventListener(MouseEvent.CLICK, broadcastMenuEvent);
}
}
}
public function broadcastMenuEvent(e:Event):void {
var button:DisplayObject = e.currentTarget as DisplayObject;
dispatchEvent(new Event(button.name, true));//bubbling, can catch at any level
}
}
The way this is built, you can change the events that are being dispatched simply by changing the name you give the instance of the button on stage. Note that you need to apply Menu as the Base Class and not the Class for this to work if you have "declare instances automatically" unchecked, because doing it that way allows the compiler to generate those instance names for you in a way your base Class doesn't have to know about.
At this point, you can then deal with those events in another place--whether it's your main document Class or whether you have a separate Controller.
I would define each of the Views you described as a separate Class as well. If you have objects coming and going on the stage, you can use one of the techniques described here to handle that. Otherwise, it's fairly straightforward to address your timeline instances from the base Class instead of timeline code. Again, you can listen for those events in the main document Class or a dedicated Controller--the main point is to make sure your Views are not making any important decisions and usually they should not be editing data.
You can choose to have your Main Document orchestrate how the tabs get added and removed (I'm a big fan of using the timeline with goToAndStop, but not everyone shares this preference), or, again, you can separate this logic out to a dedicated Controller. I would suggest that if it's possible to generalize how your Views work to have them implement a single Interface. That way, you can give them a single instance name and manage them all with the same getter/setter pair (assuming you go the timeline route).
Note the Flash compiler isn't terribly sophisticated in this regard, so if you do this and your Views extend different parent Classes, you'll get compiler warnings. Just ignore these--they don't mean anything.
The thing you shoud try to root out of your code completely is the part where Views are referencing each other. The only time it's acceptable for one View to know about another is when it's a parent knowing about its child. Even then, try to have as little specific knowledge as possible. Notice in the Menu View I wrote as an example, the parent only knows there may be some SimpleButtons, but it has no specific knowledge of where they are on stage, what, specifically, is in them, or even what there instance names are.
Instead of having your Views know about one another, have a third party (which, again, you can choose to use the main Document Class for or not) that transfers requests for state changes (in the form of events) from one to another.
I have an application that switched between different graphs in Flex. Each graph is it's own state. In the MXML I have it set so that the source of the image change when the state changes:
<s:Image id="chartImage"
source.all="{ callImages.all }"
source.classA="{ callImages.classB }"
source.classB="{ callImages.classA }"
/>
I have buttons that successfully change the images. However, I have a small bug which occurs because after the line
this.currentState = chartName;
My code expected the graph image source to be changed, however the actual change to the s:Image element doesn't appear to happen until after the function ends and the screen updates, I assume. So when it grabs chartImage.height, it uses the old one from the state I just left.
Is there a way to have it get the new image (and thus it's dimensions) so I can do calculations with those dimensions on the next line? So far, chartImage.height returns the height from before the state change, but it is different after the function executes.
I could run a timmer after a fraction of a second then execute the lines there, and it would work. However, I'd rather tell it to render the screen and then continue my code. Is this possible? It just seems more elegant.
Is there a way to have it get the new image (and thus it's dimensions)
so I can do calculations with those dimensions on the next line?
Each Flex component must go through it's own validation cycle, which is built on top of the Flash Player's rendering mechanism. There are some great posts out on this, if you perform a Google search for the Flash/Flex Elastic Racetrack.
However, you can force a component to run through it's validation methods in a linear manner by calling the validateNow() method.
It is possible--especially if you have a state transition--that the properties on your Image have not changed yet after you set the currentState variable.
You generally can't change the source of an image in Flex and then immediately check ("on the next line") it's height — the exception might be when the source is a raw bitmap, but even then Flex's layout framework will vary depending on different factors so I wouldn't rely on this.
This is the classic problem with Flex: the beauty (and misery) of the framework is that it progressively renders it's changes to maximize the responsiveness of the app. Google "flex component life cycle" for a ton of resources about the details of this process.
There are a couple of ways to deal with this. Usually you'll want to use binding expressions, since they are designed for exactly this reason: asynchronous layout changes. Don't overuse them but they are a solid tool to keep the codebase simple and flexible.
You can bind to the height of a SparkImage via mxml:
<s:Image id="chartImage" />
<s:Label id="debugLabel" text="{ 'Height of chartImage: ' + chartImage.height }" />
but if you need to run logic I'd recommend using the BindingUtils (in the script block):
import mx.binding.utils.BindingUtils;
protected function someOtherFunctionBlock():void
{
BindingUtils.bindSetter( handleHeigtChange, image, "height" );
}
protected function handleHeigtChange( value:Number ):void
{
// Deal with value change
}
Having read many tutorials, articles and questions, I am still have confusions about updating the GUI. Plus there are numerous related questions here on this website and still no luck - even though I think my problem is very simple.
Basically, I have a JFrame that has a JLayeredPane as its root container. And I have some layers of JPanels inside it.
The main issue is with updating a particular JPanel in this JLayeredPane. And for this particular Panel, I have implemented an update method that changes the contents inside it.
updatePanel(int para)
//doesn't remove this panel
//removes some existing labels and replaces it with new ones
Once I create the whole Frame, obviously just calling this method won't show any change displayed the frame.
private void static main (String[] args){
WindowFrame frame = new WindowFrame()//WindowFrame extends JFrame
frame.updatePanel(2);
.....
.....
}
And that's where I am stuck. I want to update the contents as the frame is displayed.
I saw these methods mentioned by people but due to nature of problems, I couldn't fully grasped the concepts. Plus the documentation on these methods isn't really helping - at least to me.
revalidate()
validate()
repaint()
How/when should these methods should be called? Or is this not the right way of what I should be doing, given these methods and the problem I am trying to solve?
Thank you for your time.
Basically you need two methods:
revalidate()
This method does the same as invalidate() but in AWT event dispatching thread (i will just call it Swing thread later on)). It updates container and all of its ancestors (parent containers in which this one is placed) layouting.
Basically if you either move something inside this container or place/remove components inside of it you should call this method (or invalidate in case you are performing it in Swing thread, for example inside any Mouse/Action listener body or just inside).
repaint()
This method forces component, all its sub-components (if it has them) and parent container (basically if this component is NOT opaque) to update what they are "painting".
Usually you don't need this method since all standard Swing components know when to repaint themselves and they do it on their own (that ofcourse depends on components UIs and some other things). This method might be useful in case you have your own specific components with some unique painting-way (for e.g. some custom selection over the components) and in some rare problematic cases with standard components.
Also the way this method acts depends on the components placement (due to some Swing painting optimizations) - if you have some massive repaints rolling you'd better optimize them to repaint only those parts (rects) that you actually need to repaint. For example if you change the component bounds inside any container the best choice is either to repaint its old bounds rect and new bounds rect OR repaint rect that contains both of those bounds, but not the whole container to avoid repainting uninvolved in the action components.
So, basically in your case after some changes with panels you should call revalidate on their container (or invalidate) followed by repaint (in case revalidate leaves some visual artefacts) again for the container.
Guess i didn't miss anything and i hope that now you know the basic meaning of those methods.
revalidate at the end of your update method like so .
updatePanel(int para){
.....
.....
this.revalidate(); //of course this refer to the panel
parent.revalidate(); // parent refer to the window
}
I've got a very simple project in FlashBuilder 4.5. It's a mobile application of type ViewNavigatorApplication with a single view, MapView. In the MapView.mxml file, I've got a Flex component of type Map declared in xml.
<?xml version="1.0" encoding="utf-8"?>
<s:View xmlns:fx="http://ns.adobe.com/mxml/2009" name="CatBusMapView">
<fx:Script>
<![CDATA[
import com.esri.ags.Map;
....
]]>
</fx:Script>
<fx:Declarations>
....
</fx:Declarations>
<esri:Map id="cbm">
...
</esri:Map>
</s:View>
In this same project, I've an actionscript class called UserGeolocation. In this class, I'm attempting to get a reference to this map component.
So far, I can get a reference to the top-level application, its ViewNavigator and the NavigationStack of said ViewNavigator. However, I cannot find a way to access the MapView, much less the map I've declared within it.
I'm aware of methods like firstView() and activeView(), but I want an absolute solution, one that retrieves the view regardless of whether or not it's first or active. I've tried navigator.getChildByName("MapView") with no luck.
You could also do it using events. Fire a custom event from the MapView onActivate/creationComplete, and pass the esri map component cbm as the data part of the event.
Have a listener in the main ViewNavigatorApplication class that listens for this event. In the handler of the event listener, you can access this through event.data, and then assign it to a variable declared in the main class or in the UserGeolocation class.
Brian
Just to underscore the original intent of the question: I want an absolute reference to a View in a ViewNavigatorApplication. I'm surprised there's no clear way of doing this in the API, since it seems common practice to put Flex components in Views, in the form of declarative MXML. Existing methods that manipulate the stack aren't helpful in this regard, because they are either a) conditional upon the active or first view, or b) affect the visual presentation of the application.
There are three general solutions that have been suggested which, oddly enough, focus on problems engendered by multiple stacks in TabbedViewNavigatorApplication and don't mention the ViewNavigatorApplication.
Put the object/component you want access to at the top of the container hierarchy (ie FlexGlobals.topLevelApplication)
Use a lightweight framework
Use events (either custom or existing)
I opted for the first.