I'm currently working on an Adobe AIR application which is targeting the iPad2 as the hardware platform, and can not get decent scrolling performance on one of the screens. I'm using a spark list, with a custom item renderer like so:
<s:List id="productList" top="116" bottom="0" left="10" right="10"
width="100%"
visible="true" includeInLayout="true"
height="0"
maxHeight="500"
opaqueBackground="#ffffff"
itemRenderer="myRenderer">
</s:List>
Originally, I was using an .mxml renderer, but after seeing the nasty performance I decided to roll my own, extending UIComponent (I've left off the package and braces to save on horizontal space):
import mx.controls.listClasses.IListItemRenderer;
import mx.core.UIComponent;
import mx.events.FlexEvent;
import mx.utils.ColorUtil;
import spark.components.Label;
import spark.components.TextInput;
public final class OrderViewProductLineTestIR extends UIComponent implements IListItemRenderer
{
public function OrderViewProductLineTestIR()
{
super();
}
// Internal variable for the property value.
private var _data:Object;
private var productName:Label;
private var orderQty:TextInput;
private var stockQty:TextInput;
// Make the data property bindable.
[Bindable("dataChange")]
// Define the getter method.
public function get data():Object
{
return _data;
}
// Define the setter method, and dispatch an event when the property
// changes to support data binding.
public function set data(value:Object):void
{
_data = value;
invalidateProperties();
dispatchEvent(new FlexEvent(FlexEvent.DATA_CHANGE));
}
override protected function createChildren():void
{
super.createChildren();
productName = new Label();
// productName.visible = true;
addChild(productName);
orderQty = new TextInput();
addChild(orderQty);
stockQty = new TextInput();
addChild(stockQty);
}
override protected function commitProperties():void
{
super.commitProperties();
productName.text = _data.Name;
}
override protected function updateDisplayList(unscaledWidth:Number, unscaledHeight:Number):void
{
super.updateDisplayList(unscaledWidth, unscaledHeight);
productName.move(0, 0);
productName.setActualSize(250, 48);
orderQty.move(270, 0);
orderQty.setActualSize(100, 48);
stockQty.move(390, 0);
stockQty.setActualSize(100, 48);
}
override protected function measure():void
{
super.measure();
measuredWidth = 490;
measuredHeight = 48;
}
}
As you can see this is pretty light-weight, yet my dataprovider contains upwards of 100 items, and 11 of them can be on screen at any one time. Everything I've read around increasing performance for scrolling revolves around using opaqueBackground and cacheAsBitmap, however no matter what I try neither help here. Using cacheAsBitmap at the list level doesn't help as the item renderer recycling kicks in once you've scrolled more than a couple of lines requiring the whole thing to be re-rendered, and using it at the item renderer level is still horribly slow when scrolling fast — presumably because many are being recycled at once during a very fast scroll.
I know the iPad should have no problem blitting a screenful of information in a frame at 60 fps, yet when I scroll quickly I'm seeing it struggle to make 10 fps (from sight). So the question: have I missed something obvious, or is this to be expected due to the number of layers (and vector rendering) involved when using AIR? For the record, I have tried changing the render mode for the application and tried changing the frame rate to eliminate the obvious.
This is a bit of guessing, but can the itemRenderer be optimized? Do all the children need to be positioned and sized every time component redraws? Do you need to update productName.text every time commitProperties run?
Here is how I might modify things:
import mx.controls.listClasses.IListItemRenderer;
import mx.core.UIComponent;
import mx.events.FlexEvent;
import mx.utils.ColorUtil;
import spark.components.Label;
import spark.components.TextInput;
public final class OrderViewProductLineTestIR extends UIComponent implements IListItemRenderer
{
public function OrderViewProductLineTestIR()
{
super();
}
// Internal variable for the property value.
private var _data:Object;
// Add a dataChanged property
private var dataChanged :Boolean = false
private var productName:Label;
private var orderQty:TextInput;
private var stockQty:TextInput;
// Make the data property bindable.
[Bindable("dataChange")]
// Define the getter method.
public function get data():Object
{
return _data;
}
// Define the setter method, and dispatch an event when the property
// changes to support data binding.
public function set data(value:Object):void
{
_data = value;
// switch the dataChanged flag
dataChanged = true;
invalidateProperties();
dispatchEvent(new FlexEvent(FlexEvent.DATA_CHANGE));
}
override protected function createChildren():void
{
super.createChildren();
productName = new Label();
// productName.visible = true;
addChild(productName);
orderQty = new TextInput();
addChild(orderQty);
stockQty = new TextInput();
addChild(stockQty);
}
override protected function commitProperties():void
{
super.commitProperties();
// Only update the display if the data actually changed
If(dataChanged){
productName.text = _data.Name;
dataChanged = false;
}
}
// add variable to tell whether the component's children have been sized and positioned or not
// since they have static locations, no need to set these each time
protected var compChildrenSized :Boolean = false;
override protected function updateDisplayList(unscaledWidth:Number, unscaledHeight:Number):void
{
super.updateDisplayList(unscaledWidth, unscaledHeight);
if(!compChildrenSized){
productName.move(0, 0);
productName.setActualSize(250, 48);
orderQty.move(270, 0);
orderQty.setActualSize(100, 48);
stockQty.move(390, 0);
stockQty.setActualSize(100, 48);
compChildrenSized = true;
}
}
override protected function measure():void
{
super.measure();
measuredWidth = 490;
measuredHeight = 48;
}
}
I guess I'll add that I'm not sure measure will ever run. What happens if you replace the textInputs with labels? Are you using Flex 4.6, and if so are you using StyleableStageText (AKA StageText) or the 4.5 skin which uses StyleableTextField? I wonder if StageText scrolling could kill performance because it hangs out above the Flash Display list.
What happens if you remove the textInput completely and replace with labels?
These are little things, and I'm not sure if they'll help.
Try just this on your "Spark List component"
1/ add a layout (horizontal or vertical depending on what you want ;)
2/ add attribute "useVirtualLayout=true" on your list component !
And tell me what's the result ... but i think it will be smoother (very very smoother),
because when you add your elements to the list, all the components are rendered, even if
they are out of the layout ... so whith "useVirtualLayout" just items that are on the viewport are rendered others are destroyed ...
See ya ! and have fun whith Flash Builder ! it's a so cool technology to have a multi support app !
A bit late. but.
Its true that its better to use starling and feather if you want something close to native performance.
But you can still optimise your renderer.
Please dont use binding anywhere in a Flex mobile application.
Extending UIComponent is the right thing to do.
I think it would be better to not use invalidation and set your properties in the data setter.
Hope it helps.
Related
I need to display <p:selectManyCheckbox> items with images. I tried to display images with in <p:selectOneRadio>. It works fine. I am programmatically adding components on UI. This is my code.
answerRadio.setLayout("custom"); //answerRadio is SelectOneRadio
customPnl = (PanelGrid) app.createComponent(PanelGrid.COMPONENT_TYPE);
customPnl.setId("pnl"+qstnCnt);
customPnl.setColumns(3);
radioBtn = (RadioButton) app.createComponent(RadioButton.COMPONENT_TYPE);
radioBtn.setId("opt"+qstnAnsIndx);
radioBtn.setFor("ID of answerRadio");
radioBtn.setItemIndex(ansIndx);
customPnl.getChildren().add(radioBtn);
outPnl.getChildren().add(answerRadio); //outPnl is OutputPanel that include answerRadio
outPnl.getChildren().add(customPnl);
That's <p:selectOneRadio> with images.
I'd like to use <p:selectManyCheckbox> in same way. But PrimeFaces has only a <p:radioButton> for custom layoue and not a <p:checkbox> like that. How can I achieve it anyway? How can I display <p:selectManyCheckbox> items with images ?
That's not possible with <p:selectManyCheckbox>. Your best bet is to just use a bunch of <p:selectBooleanCheckbox> components instead and change the model to be Map<Entity, Boolean> instead of List<Entity>. You can loop over it using <ui:repeat>.
E.g. (normal XHTML variant; I am not going to advocate the Java createComponent() equivalent):
<ui:repeat value="#{bean.entities}" var="entity">
<p:selectBooleanCheckbox value="#{bean.selection[entity]}" />
... (you can put here image, label, anything)
</ui:repeat>
with
private List<Entity> entites;
private Map<Entity, Boolean> selection;
#PostConstruct
public void init() {
entities = service.list();
selection = new HashMap<>(); // No need to prefill it!
}
To check which ones are selected, loop over the map in action method:
List<Entity> selectedEntities = new ArrayList<>();
for (Entry<Entity, Boolean> entry : selection.entrySet()) {
if (entry.getValue()) {
selectedEntities.add(entry.getKey());
}
}
I'm working on a random wave system for a game. The idea is that every 1000 points a movement pattern would be selected from around 50 possibilities. This would affect the speed, direction, and image of the selected item. I have devised a method that I think will work, but I'm unsure if this is going to cost too much memory to run.
public class engine extends MovieClip {
private var countK:Number = 0;
private var newWave:Boolean = true;
public function engine() {
stage.addEventListener(Event.ENTER_FRAME, update);
}
private function update():void {
checkCount();
checkNew();
}
private function checkCount():void {
if (count => 1000) {
newWave=true;
count = 0;
}
}
private function checkNew():void {
if(newWave) {
randomNumber();
newWave=false
}
}
Above is my quick idea of getting a random number to be generated every 1000 points. Points can be added in any way you want (just add say 20 to "Score" and 20 to "count" at the same time). Where I can a random number function in checkNew, I won't be pulling another function, it's simply there for the sake of legibility.
var newEnemy:mEnemy =new mEnemy();
stage.addChild(newEnemy);
EnemyArray.push(newEnemy);
trace(EnemyArray.length);
Above is some code that can add an instance of mEnemy to the stage. Now where I'm starting to loose it is, how can I translate the random number into a viable method of changing mEnemy's behaviour?
Is it wise to have 50 functions inside the mEnemy class and just before I addChild, I do something like newEnemy.WAVEfuncton1(); ? If that is the case, can I save code by getting it to select the function without writing a whole bunch of if statements?
Instead of;
if (randomN==1) {
newEnemy.WAVEfunction1();
}
if (randomN==2) {
newEnemy.WAVEfunction2();
}
....
Can I do;
newEnemy.WAVEfunction[randomN]();
This is also assuming that using functions inside the enemy is the best idea. Is it better to have the behaviours inside the engine class instead?
As you can see, I'm no programmer. I'm very new to this sort of thinking and I don't want to create a mistake that will destroy the performance of the game (not to mention picking up bad habits too!).
If you have taken the time to read this question, thank you! If you tolerate my ignorance, then thank you even more!
If the wave functions are just creating a single enemy of a certain type, it might make more sense to make an array with the details of each type like this: (I'm guessing at how your enemies work of course)
private const ENEMY_TYPES:Array = [
{speed:1, direction:90, image:1},
{speed:2, direction:45, image:2}
]
then change mEnemy() to set itself up according to the details you give it:
public function mEnemy(details:Object) {
mySpeed = details.speed;
...
That way, you can just write new mEnemy(ENEMY_TYPES[randomN]);
Alternatively, if you do need to have lots of separate wave functions, you can use the [ ] array access operator to access the properties of an object such as newEnemy by name (or this to reference the current object):
var exampleProperty:String = "Hello.";
this["exampleProperty"];
So you can run your wave functions by writing:
newEnemy["WAVEfunction" + String(randomN)]();
A 2-year old question and rather non-actual already but let me try myself here as I have just signed up.
As I understood, what are you proposing to do here is writing all 50 behaviour methods for each kind of Enemy, which is of course not good.
First, you can add the "behaviour" entity. So each enemy now has a behaviour property.
Next, you have to create a separate Behaviour class or interface, which will have 50 subclasses (Behaviour1...Behaviour50), each subclass implementing its own run() method. Note that this way you will be able to add or remove behaviours without touching anything else. A basic implementation would look like this:
public class Behaviour() {
public function run(e:Enemy):void {
e.y += 10;
}
}
So you see, it's not like enemy is doing something. It's the Behaviour that does something with the enemy it was passed to.
Next, you need a mechanism to get the proper subclass from a given random number.
What you need is a Factory - a static class that will return different types of Behaviours based on input params. Something like this:
public class BehaviourFactory {
public static getBehaviour(n:int):Behaviour {
switch(n) {
case 1: return new Behaviour1();
case 2: return new Behaviour2();
// etc.
}
}
}
Instead of having 50 choices inside a switch, you can also use the class definition:
var c:Class = getDefinitionByName('Behaviour' + your_random_number) as Class;
return new c;
(In further implementatons it can be cached, stored in an Array etc.) After you have a Factory, you just do:
var b:Behaviour = BehaviourFactory.getBehaviour(your_random_number);
Next, you can use different approaches depending of how exactly the behaviour changes. For example, if the enemy is born with a specific current behaviour and it doesn't change during the enemy's lifetime, you can just assign one of Behaviour subclasses to the Enemy's behaviour property:
public class Enemy {
public var behaviour:Behaviour;
public function Enemy(b:Behaviour) {
this.behaviour = b;
}
}
var e:Enemy = new Enemy(BehaviourFactory.getBehaviour(random_number));
e.behaviour.run(e);
This property of course can also be changed dynamically so the next time it is run the enemy will behave differently.
If the behaviour is global for all enemies and changes for all of them at once, you don't event need to have a property in an Enemy object. You just have a global Behaviour object and pass there an Enemy instance:
var e:Enemy = enemy_list[i];
current_behaviour.run(e);
it will take care of processing each active enemy according to the currently chosen behaviour.
Finally, there's more interesting way to implement behaviours. Suppose you have several behaviour types that don't have anything in common. Say, the Enemy can be Crawling, Flying, Shooting and Poisonous. So let's say you're attempting to implement all possible combinations: Flying, FlyingShooting, FlyingPoisonous, FlyingShootingPoisonous, etc. You would have to create a Behaviour subclass for each of these combinations despite them having very common basic parts.
There's an other way to go, called the Decorator pattern. You simply write a method for each single quality. Whenever you need a combination of qualities, you simply create object with first quality and wrap it into the object with the second quality and wrap it into the object with the third quality etc. So your base Behaviour class needs one addition:
public class Behaviour {
private var parent_bhv: Behaviour;
public function Behaviour(bhv:Behaviour = null) {
if (bhv) this.parent_bhv = bhv;
}
public function run(e:Enemy):void {
e.y += 10; // do what we need to do
if (this.parent_bhv) this.parent_bhv.run(e); // pass to a next bhv.
}
}
Let's create compound behaviour of number 1, 3 and 15:
var decorated_behaviour:Behaviour = BehaviourFactory.getDecoratedBehaviour([1, 3, 15]);
let's also add the corresponding BehaviourFactory method:
public class BehaviourFactory {
public static function getDecoratedBehaviour(bhv_list:Array):Behaviour {
var b:Behaviour = null;
for (var i:int = 0; i < bhv_list.length; i++) {
var c:Class = getDefinitionByName('Behaviour' + bhv_list[i]) as Class;
b = new c(b);
}
return b;
}
}
Now you're all set without having to code all possible combinations!
I'm using AvalonEdit in an app that runs my own custom-built language. I want to put in appropriate syntax highlighting into Avalon Edit. Normally this is done by defining the highlighting rules in an xml file by hand.
However, I don't want the highlighting rules to always be falling out of sync with the language grammar whenever I extend the language. So I'm hoping to use the grammar info that's already contained in my coco/R parser to automatically generate these rules.
So is there a way to programmatically add syntax highlighting rules to Avalon Edit?
Thanks
The below code worked for me at least.
Assembly assembly = Assembly.GetExecutingAssembly();
using (Stream s = assembly.GetManifestResourceStream("Your.xshd"))
{
using (XmlTextReader reader = new XmlTextReader(s))
{
//Load default Syntax Highlighting
InternalEditor.SyntaxHighlighting = HighlightingLoader.Load(reader, HighlightingManager.Instance);
// Dynamic syntax highlighting for your own purpose
var rules = InternalEditor.SyntaxHighlighting.MainRuleSet.Rules;
_HighlightingRule = new HighlightingRule();
_HighlightingRule.Color = new HighlightingColor()
{
Foreground = new CustomizedBrush(SomeColor)
};
String[] wordList = PseudoGetKeywords(); // Your own logic
String regex = String.Format(#"\b({0})\w*\b", String.Join("|", wordList));
_HighlightingRule.Regex = new Regex(regex);
rules.Add(_HighlightingRule);
}
}
internal sealed class CustomizedBrush : HighlightingBrush
{
private readonly SolidColorBrush brush;
public CustomizedBrush(Color color)
{
brush = CreateFrozenBrush(color);
}
public CustomizedBrush(System.Drawing.Color c)
{
var c2 = System.Windows.Media.Color.FromArgb(c.A, c.R, c.G, c.B);
brush = CreateFrozenBrush(c2);
}
public override Brush GetBrush(ITextRunConstructionContext context)
{
return brush;
}
public override string ToString()
{
return brush.ToString();
}
private static SolidColorBrush CreateFrozenBrush(Color color)
{
SolidColorBrush brush = new SolidColorBrush(color);
brush.Freeze();
return brush;
}
}
You can generate an .xshd file in memory using the object model in ICSharpCode.AvalonEdit.Highlighting.Xshd (XshdSyntaxDefinition etc.).
To convert it into an IHighlightingDefinition, use the HighlightingLoader.Load() method. You can also save it to disk (for debugging purposes) by applying the SaveXshdVisitor.
Alternatively, you could implement IHighlightingDefinition yourself and directly create HighlightingRuleSet instances.
I would like to know how to update a panel when we select a drop down chioce values, that is in onUpdate() method.
My custom panel has AjaxFallbackDefaultDataTable.
Below is Panel and drop down components code. When user selects date, I want to replace my entire Panel. Currently I have commened that target.addComponent code, but I want to have implementation here. Any suggestions?
List<DealHistory> dealHistoryList = ServicesCaller
.getAllDealHistoryRecords();
DealHistoryTablePanel dealHistoryTablePanel = new DealHistoryTablePanel(
"deal_history_table_panel", dealHistoryList);
dealHistoryTablePanel.setOutputMarkupId(true);
add(dealHistoryTablePanel);
IModel<List<? extends String>> dateChoices = new AbstractReadOnlyModel<List<? extends String>>() {
#Override
public List<String> getObject() {
List<String> list = new ArrayList<String>();
list.add("Last 3 months");
list.add("Last 6 months");
return list;
}
};
final DropDownChoice<String> datesDropDown = new DropDownChoice<String>(
"dates", new PropertyModel<String>(this, "selectedDate"),
dateChoices);
datesDropDown.add(new AjaxFormComponentUpdatingBehavior("onchange") {
#Override
protected void onUpdate(AjaxRequestTarget target) {
//target.addComponent(dealHistoryTablePanel);
}
});
add(datesDropDown);
You're definitely on the right track. The basic thing that will make it happen is having the
target.addComponent(dealHistoryTablePanel);
exactly where you have it, in an AjaxFormComponentUpdatingBehavior.
You'll also likely want to change the model in your DealHistoryTablePanel, probably by replacing the list it contains with one gotten by a different call to your service. To say anything more explicit, I'd have to see the code for DealHistoryTablePanel.
An example showing the updating of one DropDownChoice after the selction of one is instructive, though of course the component it updates is different.
I want to get a collection of all label controls that are part of a user control. I have the following code:
var labelControls = from Control ctl in this.Controls
where ctl.GetType() == typeof(Label)
select ctl;
but the result is zero results.
Please assist. Thanks.
Edit
I have also tried the following code without success.
this.Controls
.OfType<Label>()
.Where(ctl => ctl.ID.Contains("myPrefix"))
.ToList()
.ForEach(lbl => lbl.ForeColor = System.Drawing.Color.Black);
Again, without success.
Are you sure that the control whose child controls you are parsing actually directly contains Label controls? I suspect that it is a child of the main control that is hosting the labels, in which case, you need to recursively search through the UI tree to find the labels.
Something like:
public static IEnumerable<Label> DescendantLabels(this Control control)
{
return control.Controls.DescendantLabels();
}
public static IEnumerable<Label> DescendantLabels(this ControlCollection controls)
{
var childControls = controls.OfType<Label>();
foreach (Control control in controls)
{
childControls = childControls.Concat(control.DescendantLabels());
}
return childControls;
}
Controls.OfType<Label>() - thats all
For nested controls
public static class ext
{
public static List<Label> GetLabels(this Control control)
{
var chList = control.Controls.OfType<Label>().ToList();
chList.AddRange(((IEnumerable<Control>)control.Controls)
.SelectMany(c => c.GetLabels()));
return chList;
}
}
var labelControls = this.Controls.OfType<Label>();