What would be the preferred (recommended) way to rearrange the components of a QML UI on an event such as a button click?
I do have designed a UI consisting of a bunch of custom components, each of them is bound to a (C++) model. I use anchors to position the components in the ApplicationWindow.
On a button click, I want to add an additional component which, due to its size, makes it necessary to rearrange the existing components. Actually, the button could be understood as switching into a different mode such as the Debug view in an IDE (think of Eclipse).
My goal is to keep the components in a consistent state between views and make the switch as fluent as possible.
I could think of these options:
Design two different views, and use a Loader or State to switch between them. As initializing the models takes some time, they should remain not be deleted during switching. I think, setting them as ContextProperty of the QMLApplicationEngine should take care of that.
Do a lot of rearranging in the onClicked()-Handler of the button. Probably the worst solution but does not require to reinitialize the components and models.
I think the most elegant solution would be to initialize the components in a some kind of container (or model) and then assign different layouts to this container on button click. But I have no idea, if this is possible and how to achieve that.
Edit: I followed approach 1. by using the StackLayout. It works basically, but it seems as if the invisible UI is still running in the background and consuming resources (e.g. requesting Images from my QQuickImageProvider).
Using a Loader eliminates this problem as it destroys the previous UI on every change of the source property. What I do like about the StackLayout however is that it preloads all UIs on app startup. So the transitions are smoother compared to the Loader. Is there a way to disable the invisible UIs?
Your option (1) is the one giving your the most flexibility, since you have two entirely separate UIs depending on state.
As you already discovered yourself this requires keeping all relevant state data in a way that is accessible by both UIs, e.g. in C++ or in globally accessible QML/Script objects.
I would go for that if you envision any more changes or tweaks than just rearranging elements.
If you are sure that rearranging elements will be sufficient, have a look at QML states
It is a bit like a combination of your options (2) and (3).
The state setup allows you very locally to define anchors and other properties for each element depending on a named state.
The button's signal handler then only needs to change which of the states is active, by assigning one of the names to the respective state property.
Related
Can anyone clarify this statement from the WatchKit Development Tips page?
Simplify controller scenes.
Reduce the number of hidden objects as much as possible to significantly improve load time. For example, five versions of a controller’s layout in a single controller scene will result in all objects being created before the controller is displayed
I've read it a few dozen times now and can't figure out what it's trying to say.
What is a "scene"? Is it referring to the Storyboard scene?
Are "hidden objects" referring to literally hidden UI elements like a hidden button?
How is it possible to have five versions of a controller's layout? This just does not compute.
When a storyboard is loaded, and there are, say, 10 different WKInterfaceControllers in the file, will that be very slow even if the initial controller is blank? Aren't these only loaded as-needed? Or, would it be better to have a single table with 10 prototype rows - each of which is actually a stand-alone interface - which are only instantiated one at a time?
Because we can't programmatically add interface elements to controllers in the current version of WatchKit, any interface elements that we might need to display must be included in a Storyboard scene. By including these initially-hidden elements, we can programmatically hide or unhide these elements as needed.
For example, it's common to include a full-screen label that is initially hidden. Then, if a full-screen message needs to be shown for some reason, the text is populated, the label is unhidden, and the rest of the elements on the screen are hidden. To make hiding a set of elements easier, they're typically included in a WKInterfaceGroup, so that only the top-level group element needs to be hidden.
So, to answer your questions:
Indeed, a "scene" is a standard Storyboard scene.
Yes, "hidden objects" is referring to literally hidden objects as I've described above.
Using the method I've described, you could create five top-level WKInterfaceGroup elements, each with its own set of controls and layout. Then, you'd likely unhide the one that makes sense to display and hide all the others.
I use these techniques in my own app, though I typically don't have more than three top-level groups.
So yes, because it takes time to initialize and layout all of these elements (even if they're hidden), the recommendation is to keep it to a minimum.
Regarding the loading of interface controllers in a storyboard, you're correct that only the interface controllers that are needed are loaded. However, if you have a set of five page-based controllers, they'll all be loaded and initialized before the first page is activated. Other controllers would then be loaded as appropriate.
Creating unique rows is another possibility, but whether you do that or simply hide/unhide top-level groups depends on your app's specific needs. As always, it's worth testing on actual hardware.
I have a spark List in which I am adding custom components manually (without an item renderer). Everything is working fine except when I add hundreds of items in the list (based on several calculations), the render time increases to several seconds.
My questions are:
Does flex automatically delay the rendition of components that are added to a list?
If not then, how can I tell the spark list to only render the components once everything is added.
I hope what I am asking makes sense. I am pretty sure the render time is increasing because I have quite a few components inside the custom component. I just need a simple answer to my questions.
One more thing to know is that,
ActionScript 3 runs on flash player based on even-driven. It means that if you run a chunk of code in blocking, anything else, including rendering, will not be executed until the code ends.
This design sometimes causes issues: If a developer adds too much job in one blocking function, the player looks frozen and unresponsive momentarily. (If the code is an infinite loop, it stops until the player detects and throws ScriptTimeoutError, or possibly forever.)
However, for your question, yes, anything will be delayed until the adding job is done. Flex components doesn't basically split the job automatically.
Flex components are rendered in the list according to the Flex lifecycle: http://help.adobe.com/en_US/flex/using/WS460ee381960520ad-2811830c121e9107ecb-7fff.html
There are two reasons your List may be running slow, your components may not be following the Flex lifecycle correctly and the second because virtual layouts are not enabled on your List.
First the hard one:
Your first step should be to ensure you are following the correct phases in your custom components for commitProperties(), measure() and updateDisplayList(unscaledWidth:Number, unscaledHeight:Number).
So long as you are not calling any method named validateNow() on either your UIComponent subclasses or the LayoutManager then your components should follow this just fine.
The second may be that your list layout is not using virtual layout. This is enabled by default in a Spark List component but if you're overriding the layout you should ensure that useVirtualLayout is set to true on whatever layout you're using.
This is the property to enable it:
http://help.adobe.com/en_US/FlashPlatform/reference/actionscript/3/spark/components/List.html#useVirtualLayout
This is what it does to speed up rendering many items in a Spark DataGroup component (which a List is):
http://help.adobe.com/en_US/flex/using/WSc2368ca491e3ff92-1483ec4a12136246e76-8000.html#WS685c4ccbb85d340c6617851e121d4b8abae-8000
I've read in the flex developer guide that you sometimes need to override one
of the lifecycle methods like: commitProperties and updateDisplayList
but I've written a few flex apps without ever needing to implement them.
when do I need to override them?
First, I 100% recommend studying this presentation by EffectiveUI:
Diving Deep with the Flex Component Lifecycle
and this by Michael Labriola from Digital Primates:
Diving in the Data Binding Waters
They go into things you'll never find in the docs but that are super practical for understanding the Flex Component Lifecycle.
From my experience, the only time you need to worry about overriding core lifecycle methods is if you are creating components. I make a distinction between Application Views and the Components.
Components are things that need to be nearly perfect, highly optimized, and extremely versatile/abstract.
Views are things that you may only need in one Application, but could reuse if you so desired (LoginScreen, ContactForm, etc.).
Views, for the most part, are just adding things to the display list of a more generic component (Canvas, Group, Container, VBox, List, etc.). You, as a View/Application developer, don't really care about how the "dataProvider" creates it's itemRenderers, it just works.
Components are a different story though. When you create a component, you want it to fit perfectly into that system Flex has set up: the Component Lifecycle. It's pretty tough when you first try to build a component like they do, but after you wrap your head around it it's super easy. Here's how I think of the methods when I develop components:
createChildren()
Called once when component is constructed
Called top down. So if Panel calls createChildren, it's createChildren method will call addChild on all of it's children, which calls initialize, which calls createChildren.
If you created a custom component, say a StarRatingComponent, you might want to add 5 stars to the stage when the component is constructed. So you'd override createChildren() to do add stars to the component you're in. By default, though, all Container components in the Flex SDK add their children here (lists do it a bit differently), so you never have to do this if you're building MXML views or something not-to-be-extremeley-reusable.
The next 3 methods are called 1 frame after properties are set.
measure()
If the parent doesn't have any sizing (percent or explicit), it will need to be sized based on it's children's sizes. This can only happen from the bottom up (took me quite a while to really wrap my head around that).
If the parent has explicit or percent sizes, it skips this step.
You override measure if you want to:
Have measuredWidth or measuredHeight return a useful value. So if you build a custom CoverFlowContainer component, and measuredWidth/measuredHeight aren't set (because measure was not overriden), then if you don't specify any sizing on CoverFlowContainer, it would be 0 width 0 height. So instead, override measure and have it set measuredWidth to radius * 2 or something like that, and now you don't need to give it a size!
If the component does not have an explicit or percent size, measure will be used to size the component. Otherwise it's skipped.
commitProperties
Called after measure.
Applies all property changes (from setting properties on the component) to the component (they were stored in private variables for that first frame).
Called a frame after initial property settings.
This is the most important method to override in my opinion. So for your CoverFlowContainer, say you set the hypothetical distance, gap, selectedItem, and tilt properties. When you set them, store them in private variables. Flex will wait a frame, and call commitProperties. In your overridden commitProperties, you can then say layout.updateEverything(selectedItem, distance, gap, tilt); so to speak. So this is the method you override to make all property changes be applied at once.
updateDisplayList
Called after commitProperties
Called top down.
You only override this to set visible properties on the component, such as setActualSize, graphics, etc. But by now (because of `commitProperties), you have all your required variables to update the display set to the right values.
Overall
So from my experience, I worked a lot with these lifecycle methods when creating a component library for things I would use in a million projects:
TitleWindow (my own version)
View3D (for Away3D/Papervision)
Tree and Stack for Flex 4
TextArea (with prompt, expandable, etc.)
ToolTip (easier to skin tooltip)
I needed to make sure everything was updated and rendered perfectly according to the lifecycle. Reading and understanding the Flex 4 Spark Source Code really helps clarify when to override these methods. As does the Openflux Source Code (very simple, clear alternative to the Flex Framework. Not as feature rich so it shows how to bare-bone override those methods to accomplish some pretty advanced things).
When I develop applications and make things like AdvertismentView, MenuView and LoginView, I don't ever think about it because all the components I'm using have already solved those problems (ViewStack, Group, List, etc.). I'm basically just setting properties they've defined and updated in their own commitProperties override.
The only time I would start overriding lifecycle methods in a View would be when I need to access custom variables from outside the view. So say I had a custom RichTextEditor and I created some properties called showFontControls and showStylePanel. When I set those variables, I would probably do what they described in the Data Binding Presentation: accessor sets private variable and calls the invalidation methods, lifecycle methods execute a frame later and I have overridden commitProperties and updateDisplayList to show those panels and fonts. But in practice, that's probably overkill because it wouldn't offer that much of a performance gain for the amount of work it would take. I'd just set up some binding to a visible property in that case. Nevertheless....
The best thing to do to really get into this is to just download the Flex SDK Source and see what they're doing.
Hope that helps.
Lance
Here's another presentation by Deepa (from the Flex framework team) that goes over a lot of the same framework methods, including a nice explanation of why the whole invalidation model exists to begin with:
http://tv.adobe.com/watch/max-2008-develop/creating-new-components-in-flex-3-by-deepa-subramaniam/
When I try to access the hidden TABs of my tab navigator control in action script, it returns a null error. But it works OK if I just activate the control in the user interface once. Obviously the control is not created until I use it. How do I make all the tabs automatically created by default ?
<mx:TabNavigator creationPolicy="all"/>
That should do it. Deferred instanciation is a feature, but sometimes it is a hassle.
The Flex framework is optimizing creation be default (creationPolicy="auto") so if you have a configuration dialog with a lot of tabs, for example, and the most useful tab is the first one, your application does not spend time and memory initializing the tabs that the user never sees.
This makes a lot of difference when dialogs like this never release, and is a good default to go with.
One thing to look at is using a private variable in your dialog/form instead of pushing the data to the control on the hidden page. This style treats the whole form as if it were a component, which it sort of is. To repeat: the MXML form/dialog/canvas is a class, and it can have data and methods in addition to containing other components.
Cheers
On a side note, I've run into the deferred-loading policy in a multi-state application, and circumvented it by forcing all elements to be included and invisible in the initial state. Something to consider, but only as a hack.
What is the best way to render to a UIComponent which hasn't been added to the stage? (I'm using UIComponents as renderers for objects, and want to render new copies for image export, filtering, etc.)
Two strategies I've seen/used so far include realizing the component to ensure it calls all the lifecycle methods:
Add the component to Application.application, render with BitmapData.draw(), remove component. This is similar to what I've seen done for printing unrealized components as well.
Add the component to a pop up window, render with BitmapData.draw(), dismiss popup after rendering complete.
I believe both of these just rely on the UI not refreshing while the current thread/event is executing, though (1) could also rely on the component being realized out of view.
Is there a better way?
What I've used in the past with much success is the following:
Create a new instance of your component
Add an event listener for FlexEvent.CREATION_COMPLETE
Set visible=false on the component
Add the component as a child of the main Application
When the component is created, the event listener function will be invoked. The rest of the logic should be put in / invoked from your event listener function
Remove the event listener you added in step #2.
Use ImageSnapshot.captureImage() or captureBitmapData() to capture the visual representation of the component.
Remove the component from the main Application
Process the image data as you need to.
I've used this to snapshot various Flex charting components for use in PDF's generated on the server side. After getting the BitmapData I use the PNGEncoder or JPEGEncoder classes to compress the data, then encode it in Base64 before uploading to the server.
I'm pretty sure you can use the draw() method in BitmapData without having your component on the DisplayList.
For example is use it when I need to modify images I load with the Loader Class. In the init handler I create a BitmapData instance and draw the Bitmap from the loadInfo.content property, then copyPixels() or whatever I need to modify the loaded image
So much of a UIComponent's layout can be tied to it's context. This is especially true for a lot of its derivatives (e.g. HBox) since the fluidity of the layout is tied to it's parent's size and the number of siblings sharing its parents space.
Additionally Flex can be a real pain to get to visually update. Often critical render functions aren't done synchronously ... there are callLater, callLater2 and other hacky approaches that make dealing with the auto-magical layout properties of UIComponents a major headache. Not even calling validateNow or updateDisplayList can guarantee that the layout will be correct on the current frame (instead of a few frames in the future).
I suggest the best thing you can do is not use a UIComponent and try and use a Sprite or other.
Your approach to attach it but make it invisible (alpha = 0, mouseEnabled = false, mouseChildren = false) is decent. You should listen for the FlexEvent.CREATION_COMPLETE callback before you are certain it is properly laid out. Then you can bitmapData.draw it and then remove it from the stage. If you must use UIComponents then I know of no better way.
You can call the lifecycle function manually before using the BitmapData.draw(). Do the following.
createChildren().
commitProperties().
updateDisplayList().
bmd.draw().
The first 2 steps are not 100% necessary, you can put all codes into updateDisplayList(). Because you invoke the function manually, you don't have to worry this is invoked by Flex framework many times.