I've got a problem in an ItemRenderer in Flex 3.5. I've looked at the other posts regarding this error but still can't figure it out. The ItemRenderer is part of an AdvancedDataGrid who's data provider is HierarchicalData. I'm getting the ArgumentError but the trace doesn't go to any of my code. I've gone through in debug mode tons of times but it looks like it doesn't happen until after my code runs. Quite strange.
The item renderer has a couple different parts. It figures out what row it should be drawing for based on the xml data and then adds labels and sprites appropriately. If anyone can help, that would be a great help! Thanks!
Here is one of the methods that gets called if the itemrenderer is on a certain row.
private function addLabels(planList:ArrayCollection):void {
height = 0;
var sprite:Sprite = new Sprite();
var curX:Number = (width / planList.length);
height = 110;
for each (var plan:Plan in planList) {
var label:Label = new Label();
label.text = plan.planner.label;
label.rotationZ = 270;
label.visible = true;
label.x = curX - 7;
//Draw line divider
curX += (width / planList.length);
addChild(label);
label.move(label.x, height - 30);
//Draw divider line
sprite.graphics.lineStyle(.5, 0x000000);
sprite.graphics.moveTo(label.x - ((width / planList.length) / 2) + 10.5, 0);
sprite.graphics.lineTo(label.x - ((width / planList.length) / 2) + 10.5, height - 28);
//Draw meatball
sprite.graphics.beginFill(0x00FF33);
sprite.graphics.drawCircle(label.x + 10, height - 15, 10);
sprite.graphics.endFill();
}
rawChildren.addChild(sprite);
}
There's another function that gets called on a different row, but if I comment out the code above everything works fine, so my guess is that the problem definitely lies there. Thanks for the help!
Here is where addLabels gets called:
override protected function createChildren():void {
removeAllChildren();
var count:int = rawChildren.numChildren;
for (var i:int = 0; i < count; i++) {
if (rawChildren.getChildAt(0).parent) {
rawChildren.removeChildAt(0);
}
}
var allPlans:ArrayCollection = new ArrayCollection();
if (_plan) {
getAllPlans(_plan, allPlans);
}
if (_name == "capability") {
}
else if (_name == "components") {
height = 130;
width = 335;
addLabels(allPlans); // <-- RIGHT HERE!
var sprite:Sprite = new Sprite();
sprite.graphics.lineStyle(.5, 0x000000);
sprite.graphics.moveTo(0, 0);
sprite.graphics.lineTo(width, 0);
sprite.graphics.moveTo(0, height - 28);
sprite.graphics.lineTo(width, height - 28);
rawChildren.addChild(sprite);
}
}
I've seen this kind of thing before. This error could be happening in the AdvancedDataGrid itself, not the itemRenderer.
See http://www.judahfrangipane.com/blog/?p=196 for more information.
If you simply want to draw something on specific rows, you might try extending the AdvancedDataGrid to override two functions:
override protected function drawHorizontalLine(s:Sprite, rowIndex:int, color:uint, y:Number):void {
// do some drawing
}
override protected function drawRowBackground(s:Sprite, rowIndex:int, y:Number, height:Number, color:uint, dataIndex:int):void {
// do some drawing
}
Here's the answer if anyone was looking at this..
I'm still not exactly sure what the problem was, but the AdvancedDataGrid certainly did not like me adding a Label as a child to the renderer. Here's how I got around it.. add a TextField as a child to the sprite, as shown:
var newLabel:TextField = new TextField();
newLabel.text = plan.planner.label;
newLabel.rotationZ = 270;
newLabel.visible = true;
newLabel.x = curX - (dividerWidth / 2) - ((newLabel.textHeight) / 1.5);
newLabel.y = height - (RADIUS * 2.5);
newLabel.antiAliasType = AntiAliasType.ADVANCED;
sprite.addChild(newLabel);
Hope this helps someone!
Related
I use flex3.5 and I have a
<mx:List id="globalchat_txt" x="0" y="0" width="100%" height="100%" variableRowHeight="true" dataProvider="{messages}" itemRenderer="chat.MessageChat">
I add some items to messages.
var message:Message = new Message(txt);
globalchat_txt.validateNow();
messages.addItem(message);
globalchat_txt.scrollToIndex(messages.length+1);
globalchat_txt.verticalScrollPosition = 99999;
globalchat_txt.validateNow();
I cannot make the list scroll to the last position !
it goes to last position, but there is alsways like 10-15pixels missing !
This is a known problem with List (both spark and mx) in Flex.
It can be solved by the following method (suggested by flexponential):
public static function scrollToBottom(list:List):void {
// update the verticalScrollPosition to the end of the List
// virtual layout may require us to validate a few times
var delta:Number = 0;
var count:int = 0;
while (count++ < 10) {
list.validateNow();
delta = list.layout.getVerticalScrollPositionDelta(NavigationUnit.END);
list.layout.verticalScrollPosition += delta;
if (delta == 0)
break;
}
}
I've also reported this problem as bug #33660 for Apache Flex some time ago...
We also encountered this problem. try this, extend the list and override scrollToIndex function(FLEX SDK3.5)
override public function scrollToIndex(index:int):Boolean
{
// when index=-1, do nothing
if (index == -1)
return false;
var success:Boolean = super.scrollToIndex(index);
if (success)
{
return true;
}
// if doesn't show complete, immediately scroll down
var item:ListRowInfo = rowInfo[index - verticalScrollPosition];
if (item.height + item.y > height)
{
verticalScrollPosition += 1;
return true;
}
return false;
}
I have the following fiddle which distills an issue I am having with a larger project
http://jsfiddle.net/zhaocnus/6N3v8/
in Firefox and Safari, this animation will start having a jittering effect left and right on odd zoom levels (zoom in/out using Ctrl+/- or Cmd+/- on Mac). I believe this is do to sub-pixel rendering issues and the differences between the various browsers round up or down pixels during the zoom calculations, but I have no idea how to fix it and am looking for any suggestions.
I can't use more modern CSS3 animation features as I need to support legacy browsers like IE7.
(code from fiddle below, can't seem to post without it, although not sure it makes sense without CSS and HTML)
// js spritemap animation
// constants
var COUNTER_MAX = 9,
OFFSET = -50,
FRAMERATE = 100;
// variables
var _counter = 0,
_animElm = document.getElementById('animation'),
_supportBgPosX = false;
// functions
function play() {
// update counter
_counter++;
if (_counter > COUNTER_MAX) {
_counter = 0;
}
// show current frame
if (_supportBgPosX) {
_animElm.style.backgroundPositionX = (_counter * OFFSET) + 'px';
} else {
_animElm.style.backgroundPosition = (_counter * OFFSET) + 'px 0';
}
// next frame
setTimeout(play, FRAMERATE);
}
// check if browser support backgroundPositionX
if (_animElm.style.backgroundPositionX != undefined) {
_supportBgPosX = true;
}
// start animation
play();
Instead of moving the background to the new frame, have a canvas tag re-draw the frame. The canvas tag handles sub-pixel interpretation independent of the browser and because of this you not only control the render (browser agnostic) but also solve the jitter issue as it's being re-drawn frame-by-frame into the canvas' dimensions in realtime.
Zooming is specifically tough because there's no reliable way to detect the zoom level from the browser using jQuery or plain-ole javascript.
Check out the demo here: http://jsfiddle.net/zhaocnus/Gr9TF/
*Credit goes to my coworker zhaocnus for the solution. I'm simply answering this question on his behalf.
// js spritemap animation
// constants
var COUNTER_MAX = 14,
OFFSET = -200,
FRAMERATE = 100;
// variables
var _counter = 0,
_sprite = document.getElementById("sprite"),
_canvas = document.getElementById("anim-canvas"),
_ctx = _canvas.getContext("2d"),
_img = null;
// functions
function play() {
// update counter
_counter++;
if (_counter > COUNTER_MAX) {
_counter = 0;
}
// show current frame
_ctx.clearRect(0, 0, _canvas.width, _canvas.height);
_ctx.drawImage(_img, _counter * OFFSET, 0);
// next frame
setTimeout(play, FRAMERATE);
}
function getStyle(oElm, strCssRule) {
var strValue = '';
if (document.defaultView && document.defaultView.getComputedStyle) {
strValue = document.defaultView.getComputedStyle(oElm, null).getPropertyValue(strCssRule);
} else if (oElm.currentStyle) {
var strCssRule = strCssRule.replace(_styleRegExp, function (strMatch, p1) {
return p1.toUpperCase();
});
strValue = oElm.currentStyle[strCssRule];
}
return String(strValue);
}
function initCanvas(callback) {
var url = getStyle(_sprite, 'background-image');
url = url.replace(/^url\(["']?/, '').replace(/["']?\)$/, '');
_img = new Image();
_img.onload = function(){
_ctx.drawImage(_img, 0, 0);
callback();
};
_img.src = url;
}
// start animation
initCanvas(play);
I'm using Flex 4.5.1 with AIR 2.7 (and Flash Builder 4.5.1) to build an app for the Blackberry Playbook.
The app has a large textarea that needs to be resized when the soft keyboard shows up. I am able to hook into the soft keyboard events and do things there.
Now, I want to resize the textarea to fit the remaining part of the screen when the keyboard shows up. I know the current and destination size and want to use a smooth animation to show the textarea resizing. (I can't already set the height and can see the textarea being correctly resized in the keyboard_activating event - but I want to do it through an animation). I've tried using the Animate class, the spark.effects.Move class and none of them seem to work in this case. They seem to run the animation but the screen does not get refreshed!
I don't want to use the built-in resizeAppForKeyboard property on the ViewNavigatorApplication. I have set to 'none' in the app descriptor so that part of it is fine.
Any ideas / thoughts? Is my approach correct at all? How would one go about resizing the textarea using an animation in the keyboard activating event? My code looks like:
In the main view (onCreationComplete event): (txNote is the textarea in question)
txNote.addEventListener(SoftKeyboardEvent.SOFT_KEYBOARD_ACTIVATE, function(e:SoftKeyboardEvent):void {
toggleEditMode(true);
});
txNote.addEventListener(SoftKeyboardEvent.SOFT_KEYBOARD_DEACTIVATE, function(e:SoftKeyboardEvent):void {
toggleEditMode(false);
});
private function toggleEditMode(keyboardActivated:Boolean):void {
trace("toggle edit: " + editMode + ", kb activating: " + keyboardActivated + ", txNote height = " + txNote.height);
editMode = keyboardActivated;
//we handle resize manually, because we want a nice animation happening and we want to resize only the text area - nothing else.
var y:Number = editMode ? -38 : 0;
var height:Number = editMode ? 218 : 455;
txNote.moveAndSize(y, height);
}
The code in txNote.moveAndSize:
public function moveAndSize(newY:Number, newHeight:Number):void {
//parent group uses BasicLayout
//(this.parent as Group).autoLayout = false;
//resizePath.valueFrom = this.height;
//resizePath.valueTo = newHeight;
//movePath.valueFrom = this.y;
//movePath.valueTo = newY;
//resizeEffect.heightFrom = this.height;
//resizeEffect.heightTo = height;
this.top = newY;
moveEffect.xFrom = this.x;
moveEffect.xTo = this.x;
moveEffect.yFrom = this.y;
moveEffect.yTo = newY;
moveEffect.end();
moveEffect.play([ this ]);
//this.move(x, newY);
//animate.play([ this ]);
//this.height = height;
//this.y = y;
//setLayoutBoundsSize(width, height);
//setLayoutBoundsPosition(x, y);
}
The moveEffect / movePath / animate various things I tried are set up in the txNote constructor as follows:
public class NotesArea extends TextArea {
// private var animate:Animate;
// private var movePath:SimpleMotionPath;
// private var resizePath:SimpleMotionPath;
private var moveEffect:Move;
// private var resizeEffect:Resize;
public function NotesArea() {
super();
// animate = new Animate();
// var paths:Vector.<MotionPath> = new Vector.<MotionPath>();
// movePath = new SimpleMotionPath("top");
// resizePath = new SimpleMotionPath("height");
// paths.push(movePath);
//paths.push(resizePath);
// animate.duration = 300;
// animate.motionPaths = paths;
// animate.addEventListener(EffectEvent.EFFECT_UPDATE, function(e:EffectEvent):void {
// trace("y = " + y);
// invalidateDisplayList();
// });
// animate.addEventListener(EffectEvent.EFFECT_END, function(e:EffectEvent):void {
// trace("EFFECT ended: y = " + y);
// });
moveEffect = new Move();
moveEffect.duration = 250;
moveEffect.repeatCount = 1;
moveEffect.addEventListener(EffectEvent.EFFECT_END, function (e:EffectEvent):void {
//(this.parent as Group).autoLayout = true;
trace("move effect ran. y = " + y + ", top = " + top);
});
//this.setStyle("moveEffect", moveEffect);
//
// resizeEffect = new Resize();
// resizeEffect.duration = 250;
// this.setStyle("resizeEffect", resizeEffect);
}
}
yourTextField.addEventListener(SoftKeyboardEvent.SOFT_KEYBOARD_DEACTIVATE, onActivating);
yourTextField.addEventListener(SoftKeyboardEvent.SOFT_KEYBOARD_ACTIVATING, onActivating);
yourTextField.addEventListener(SoftKeyboardEvent.SOFT_KEYBOARD_ACTIVATE, onActivating);
// in the event listener to the textField IE :
private function onActivating(event:SoftKeyboardEvent):void
{
//listen to the event; make your move here.
}
I'm writing a tool in Flex that lets me design composite sprites using layered bitmaps and then "bake" them into a low overhead single bitmapData. I've discovered a strange behavior I can't explain: toggling the "visible" property of my layers works twice for each layer (i.e., I can turn it off, then on again) and then never again for that layer-- the layer stays visible from that point on.
If I override "set visible" on the layer as such:
override public function set visible(value:Boolean):void
{
if(value == false) this.alpha = 0;
else {this.alpha = 1;}
}
The problem goes away and I can toggle "visibility" as much as I want. Any ideas what might be causing this?
Edit:
Here is the code that makes the call:
private function onVisibleChange():void
{
_layer.visible = layerVisible.selected;
changed();
}
The changed() method "bakes" the bitmap:
public function getBaked():BitmapData
{
var w:int = _composite.width + (_atmosphereOuterBlur * 2);
var h:int = _composite.height + (_atmosphereOuterBlur * 2);
var bmpData:BitmapData = new BitmapData(w,h,true,0x00000000);
var matrix:Matrix = new Matrix();
var bounds:Rectangle = this.getBounds(this);
matrix.translate(w/2,h/2);
bmpData.draw(this,matrix,null,null,new Rectangle(0,0,w,h),true);
return bmpData;
}
Incidentally, while the layer is still visible, using the Flex debugger I can verify that the layer's visible value is "false".
Wait a frame between setting visible and calling changed(). Use the invalidateProperties()/commitProperties() model.
private bool _isChanged = false;
private function onVisibleChange():void
{
_layer.visible = layerVisible.selected;
_isChanged = true;
invalidateProperties();
}
protected override function commitProperties():void {
super.commitProperties();
if (_isChanged) {
_isChanged = false;
changed();
}
}
Ok so I have a component, that has a function to remove itself as a popUp in its current Window, and add itself to a newly created Window.
It works, however, if the component has a child like a ComboBox, the drop down still pops up in the old window where it used to be, also scrollbars, and focus seems to behave incorrectly in the new window also.
It seems to me like Flex still thinks the component is a child of the original window, not the new window. I have no idea how to resolve this though.
Here is my code:
private var ownWindow:Window;
private var _inOwnWindow:Boolean;
private var _removedEffect:Move;
private var _openX:Number;
private var _openY:Number;
public function launchInNewWindow(e:Event):void
{
_openX = Application.application.nativeWindow.x + this.x + 5; //keep in same spot add 5 for systemChrom border
_openY = Application.application.nativeWindow.y + this.y + 30;//keep in same spot add 30 for systemChrom title
this.parent.removeChild(this);
ownWindow = new Window();
ownWindow.systemChrome = 'none';
ownWindow.type = NativeWindowType.LIGHTWEIGHT;
ownWindow.transparent = true;
ownWindow.setStyle('showFlexChrome', false);
ownWindow.width = this.width > 750 ? 750 : this.width;
ownWindow.height = this.height > 550 ? 550 : this.height;
edit.enabled = false;
_removedEffect = this.getStyle('removedEffect') as Move;
if(_removedEffect == null)
{
openNewWindow();
}
else
{
// Wait for removed effect to play before adding to new window
_removedEffect.addEventListener(EffectEvent.EFFECT_END,delayOpenInNewWindow);
}
}
private function delayOpenInNewWindow(e:Event = null):void
{
var t:Timer = new Timer(100,1);
t.addEventListener(TimerEvent.TIMER,openNewWindow);
t.start();
}
private function openNewWindow(e:Event = null):void
{
ownWindow.addChild(this);
ownWindow.width += 5; //add to show dropshadow
ownWindow.height += 10; //add to show dropshadow
ownWindow.open();
_inOwnWindow = true;
ownWindow.nativeWindow.x = _openX;
ownWindow.nativeWindow.y = _openY;
}
Any ideas?
Thanks!!
Before I give this a run, have you tried a callLater on the openNewWindow() line?
[ lame fix attempt, i know -- but given that there doesn't seem to be an event that you can listen for in the case that the removedEffect isn't null and it seems like a timer is your only option there, I think it's o.k. to give lame fix attempts :-) ]