[Flex mobile]About placing many instances of a custom component - apache-flex

Right now , my development of app is in the optimizing stage. I have a problem when adding one of my components: PlayerInfo, which is extended from Group and has some Labels and Images in it,I have to create 60 of this component and put'm all into a HGroup. But in the process of adding them into the hgroup, my app just stops responding for a few seconds,which is not tolerable. Can I achieve this with less memory usage?
I have read this page and thought if I can do it with any of my components,not only bitmaps.does anyone know how to do that?
here is how I did it:
class PlayerInfo extends Group{
private var name:Label;
private var age:Label;
private var photo:Image;
}
and in my list class:
public function addPlayers(arrPlayer:Array):void{
for(;;){
var player:PlayerInfo=new PlayerInfo();
HGroup.addElement(player);
}
}

Can I achieve this with less memory usage?
Yes! With your current approach; if you have 60 instances of your component, then the app creates 60 instances of the component, renders them all and puts them on the screen; even if they are not currently a view area.
You should make use of a class, such as a List or DataGroup, as #RiaStar suggests. Your custom component, PlayerInfo, should be used as the itemRenderer and the 'list' you are creating these components from should become the List's dataProvider.
Once you do this, then your app will make use of the List's renderer recycling. So, only the visual elements displayed on screen will be displayed to the user. So, if you have 10 items shown on screen from your list of 60; the app will generate 50 less an items. This will make better use of processing power and better use of device memory.
To change your class to a renderer, you will have to implement a dataChange() event handler so that the component updates whenever the list changes the data that the renderer should display.
I don't think we enough information to get a more detailed explanation. What is your dataProvider? What propeties need to be set on your custom component?

Related

Why JavaFX properties have not getListeners() methods

I am using JavaFX properties since JavaFX 8.0 , they seem to miss something appropriate i think . Let's say i add a change listener to a JavaFX Property :
DoubleProperty doubleProp = new SimpleDoubleProperty(1);
doubleProp.addListener((observable,oldValue,newValue)->{ ...code here }));
and i want to add 3 more including and InvalidationListener
Why to create an instances of ChangeListenerJavaDoc or InvalidationListenerJavaDoc?
Adding lines of code like :
ChangeListener<? super Number> listener = (observable , oldValue , newValue) -> {
if (newValue.intValue() == 1)
//..... code
};
all over the place isn't clear.
Why methods like these doesn't exist:
doubleProp.getChangeListeners().clearAll();
or
doubleProp.getChangeListeners().remove(doubleProp.getChangeListeners().get(0));
or
doubleProp.remove(doubleProp.getChangeListeners().get(0));
or
doubleProp.getInvalidationListeners().get(0));
Does JavaFX 9.0 has methods like these ? Is what i want a bad design ? I need to know the above :).
Allowing access to a collection of all listeners on a property would be a bad design as it would break encapsulation for controls (or other objects) that used those properties. (Specifically, a control would likely want to expose a property while registering listeners on that property and keeping those listeners private. If the property exposed the listeners, this would become impossible.)
Just as one example, if StringProperty exposed a ObservableList<ChangeListener<String>> getListeners() method, then the API would make the following possible:
Label label = new Label("Some text");
label.textProperty().getListeners().clear();
This would completely break the label. The label's skin registers a listener with the label's textProperty(), that ensures the skin updates if the text is changed. If you remove this listener, which is the only possible result of executing the code above, then the label's skin would not know it had to resize or even display new text when you called label.setText(...).
If you need to register a listener that you might need to remove, you just need to retain a reference to it. The overhead in code is minimal, i.e. there is not much difference between
label.textProperty().addListener((obs, oldText, newText) -> { /* code */ });
and
ChangeListener<String> listener = (obs, oldText, newText) -> {/* code */} ;
label.textProperty().addListener(listener);
#James_D, your explanation about what can go wrong when a listener is carelessly removed is good. However, this is really a developer problem, not design. Indeed, it is bad design for any 'add/remove' pattern not to also include access to whatever has been added.
For example, it may be useful to remove ("suspend") all the listeners temporarily in order to perform a process that changes the value (indeed several times) where the value at the end of the process is the same as the start; when the process is finished the listeners are added back ("restored"). While the 'reference' solution works for listeners you know about when designing a control, it doesn't help for listeners added by a developer using your control - these can't be referenced because you don't know what they will be. The 'suspend/restore' technique deals with this, it's up to the developer to use it properly and in the right situation.
JavaFX's property enhancements (object properties, bindings etc) are a huge improvement over Swing, like moving from a covered wagon to a Bugatti. However, the omission of a 'getListeners()' feature is a drawback.

call flex initComplete at a specific time

Below is the overriden on complete function for a preloader in Flex.
private function initComplete(e:Event):void
{
//dispatchEvent(new Event(Event.COMPLETE));
cp.status.text="Configuring... Please Wait";
}
What I want to do is when the app has finsihed loading I want to change the preloaders text to "configuring".
Then I want to go and do a bunch of setup stuff in my code.
Once I've done all the setup I wanted how can I get the Preloader to dispatch its Event.complete from else where in my code?
I tried Application.application.preloader but it comes up null.
So I guess my question really is how to access a preloader from anywhere in my application.
Would a better approach be to have all setup classes as members of my preloader class?
One thing that might help is a Model-View-Controller pattern. Are you using a framework for your application like Mate, Swiz, or Cairngorm?
If you were using Mate, for example, you could do something like this:
Create an AppStateManager class with a property (e.g. applicationState)
Create an EventMap with an EventHandler for the FlexEvent.INITIALIZE event. In this handler, set the AppStateManager.applicationState to something like "CONFIGURING"
Your EventMap has an injector that injects the applicationState property into a view. The injector listens for changes to this property and updates the view. In this case it might just be injected into your main view.
In the main view, you have a public bindable property also called applicationState that gets injected by Mate.
In the setter for this property, you can have an if/then or a switch that does different tasks depending on the state. For example, if applicationState == "COMPLETE", then this.preloader.dispatchEvent(Event.COMPLETE) or something like that.
The details are pseudo-sketched out but the idea is to use Flex's bindings to notify view components when changes have been made, and to have shared objects that maintain state. Not sure if that's what you're looking for...
The component LifeCycle does specific stuff in a specific order, and the near final element is to make the component visible.
It sounds to me like you want to defer this setting of visible to true to do other stuff. But, I imaging if you were making use of the component LifeCycle this would be a non-issue.
What sort of app init stuff do you need to do?

static/private child component in mxml?

Are there any way to declare a child component in mxml which is private/protected or even static?
Sure we can do this inside a script tag, but are there any other way?
Ashier suggests using the "Exclude" metadata tag, but Maskit offers its limitations and suggests alternative solutions:
http://blog.ashier.com/2008/03/25/hiding-properties-in-flex-components/
http://smaskit.blogspot.com/2008/07/making-mxml-subcomponent-private.html
In the strict meaning of these terms, no you can't do that using mxml. The second link posted by Luis contains some workarounds for private/protected behavior.
I found a solution to the static question. I wanted a quick memo pad that could be accessed anywhere in the mobile app, without one instance overwriting edits left open in a different screen.
I created a memo pad mxml control, and then placed it inside a declarations section in the top level application mxml. In each view that I wanted the memo to appear in, I added:
import mx.core.FlexGlobals;
import components.QuickMemo;
private var memo:QuickMemo;
In the view creation complete:
memo = FlexGlobals.topLevelApplication.memo;
In the viewActivation code, I added:
memo.visible = false;
addElement(memo);
In the viewDeactivation code, I included:
removeElement(memo);
The net effect is that only one instance of the memo exists at any time, and that one instance opens in whatever state it existed in the last view it appeared in.

Flex and fake Mxml initialisation without actually showing the component, (more insise)

I have a TitleWindow mxml class wich has several components, and listeners.
On its creationComplete and init state i add some listeners which listen for events on its gui.
This TitleWindow is only shown when the user click on a "button", i made TitleWindow a singleton with the following code:
public static function getInstance():MyWindow
{
if ( MyWindow.singleton )
{
return MyWindow.singleton;
}
else{
MyWindow.singleton = new MyWindow();
return MyWindow.singleton;
}
}
I needed a singleton because the user will call this window several times as much as he wants and i only need one.
The problem is the following on some special external events i need to "modify" some listeners (remove listeners and add new ones) on a button from MyWindow, before it was even shown once.
I still have MyWindow.getInstance() in memory when my application starts up.
However adding /removing listeners does not seem to have any effect if he actual rendering of the components did not happen, event when using the following code on app startup.
myWindow= MyWindow.getInstance();
myWindow.initialize();
Not suprisingly if i "show" ('render') the myWindow at least once then the events modifications on the myWindow instance works perfectly.
How can i fake the complete initialisation of this component without showing it on startup ?
Thanks !
Which sort of a container holds your button? If you are using a Multiple View Container you can try setting the creationPolicy to all. Single View Containers create all their children in one go and you shouldn't face this problem.
From Flex 3.0 docs I could retrieve this:
The default creation policy for all containers, except the Application container, is the policy of the parent container. The default policy for the Application container is auto.
This looks like the cause for all your troubles.
Update: I did not mention this earlier, since I thought this was to be expected :) Setting the creationPolicy to all makes your application load more slowly. So, read up on Ordered Creation -- this technique helps you to choose if the controls are displayed all in one go (which is the default behavior, after all of the controls have been created) or step-by-step, as and when they are created.

Flex component access other component

I have 2 components for example (editor.mxml using mx:windows), when I click an edit button, I want to get the current value from the other component's datafield? (datagrid.mxml using mx:window)
I do know how to access the main MXML's datagrid by parentDocument or Application.application method, but stumped block if I want to access other way as mentioned above. Keep the code as simple as possible.
You could either do dependency injection, that is, give component A a reference to component B so that they can communicate directly (example of tighter coupling,) or have both components communicate through a common mediator using events (example of more loose coupling.)
Both of those options would be implemented wherever it is that you're creating those components (A and B in this example) and adding them to the display list.
This might be more complicated than it deserves, and it smacks of Pattern-Fever, but you could use a mediator class that listens for the CLICK event from the button and knows enough about the other component to query its property. It could even transmit that data using a custom event, which the button listens for.
While this involves three classes instead of two, it often turns out to be easier to have two components that focus on looking good and one that worries about coordination.
Cheers
Try this:
FlexGlobals.topLevelApplication
This points Your root. From the root You can grab every element You want.
You can also add an id to the custom component like this,
<custom:Editor id="myCustomComponent">
</Editor:AddressForm>
and
access your datagrid's value like this,
var data:ArrayCollection = myCustomComponent.DatagridID.dataProvider;

Resources