I want MyCanvas to draw lines on itself, but they seem to be drawn behind the background. What's going on and how should I do this?
Main.mxml
<?xml version="1.0" encoding="utf-8"?>
<mx:Application xmlns:mx="http://www.adobe.com/2006/mxml" xmlns:my="*">
<my:MyCanvas width="300" height="300" id="myCanvas"></my:MyCanvas>
<mx:Button label="Draw" click="myCanvas.Draw();"></mx:Button>
</mx:Application>
MyCanvas.as
package
{
import mx.containers.Canvas;
public class MyCanvas extends Canvas
{
public function MyCanvas()
{
this.setStyle("backgroundColor", "white");
}
public function Draw():void
{
graphics.lineStyle(1);
graphics.moveTo( -10, -10);
graphics.lineTo(width + 10, height + 10);
}
}
}
Thanks.
The Graphics object of the DisplayObject itself is always positioned at the bottom of the DisplayList. In this case it is possible that an overlapping child is added by the superclass, perhaps when you apply the white background (not sure I'm not Flex expert).
However it may be safer to try to add a specifically dedicated child to draw into.
This way you can have more control over its visibility :
package
{
import mx.containers.Canvas;
public class MyCanvas extends Canvas
{
private var shape:Shape;
public function MyCanvas()
{
this.setStyle("backgroundColor", "white");
addChild(shape = new Shape());
}
public function Draw():void
{
shape.graphics.lineStyle(1);
shape.graphics.moveTo( -10, -10);
shape.graphics.lineTo(width + 10, height + 10);
}
}
}
Related
Simple and straight forward.
I extended a mx.controls.TextInput to create a custom component with a different behavior.
I'm trying to set the text property on the keyDownHandler(), and for some reason it doesn't work as I expected. The text on the component just kinda ignore the change.
I'm using Flex 3.6.
Down here is a simple example code that explains what's going on:
<?xml version="1.0" encoding="utf-8"?>
<mx:Application xmlns:mx="http://www.adobe.com/2006/mxml" layout="absolute" xmlns:customcomponent="com.test.customcomponent.*">
<customcomponent:TextTest x="20" y="20"/>
</mx:Application>
And below de AS class:
package com.test.customcomponent
{
import flash.events.KeyboardEvent;
import mx.controls.TextInput;
public class TextTest extends TextInput
{
public function TextTest()
{
super();
}
override protected function keyDownHandler(event:KeyboardEvent):void{
text = "lol. It doesn't work";
}
}
}
You need to prevent the default key down event.
override protected function keyDownHandler(event:KeyboardEvent):void{
text = "lol. It doesn't work";
event.preventDefault();
event.stopImmediatePropagation();
event.stopPropagation()
}
In order to be able to prevent the default handling, you have to have a high enough priority on your handler (which keyDownHandler() does not have). This means you need to register your own method with priority > 0.
You can try like this:
public function MyTextInput() {
addEventListener(KeyboardEvent.KEY_DOWN, yourHandler,
false, EventPriority.BINDING, true);
...
}
private function yourHandler(event : KeyboardEvent) : void {
// stop further handling
event.preventDefault();
event.stopImmediatePropagation();
event.stopPropagation();
// do your work here
text = ...;
}
As requested, I'm giving a more detailed description of the problem...
First off I have this MXML:
<mx:Panel x="270" y="10" width="690" height="680" id="mainContainerPanel">
<mx:Canvas x="270" y="10" width="670" height="640" id="canvas" initialize="onInit()">
<mx:Script>
protected function onInit():void
{
spiro = new Spirograph(canvas);
}
</mx:Script>
</mx:Canvas>
</mx:Panel>
This passes the tag object to the constructor of my main AS3 class file. This works fine, where in the constructor I have:
function Spirograph(canvas:Canvas):void
{
mainContainer = canvas;
mainContainer.graphics.beginFill(0xFFFFFF);
mainContainer.graphics.drawRect(0, 0, 670, 640);
mainContainer.graphics.endFill();
}
At the moment, I am adding all Sprite objects to the mainContainer by using a wrapper class called SpriteUIContainer:
package includes
{
import flash.display.Sprite;
import mx.core.UIComponent;
public class SpriteUIContainer extends UIComponent
{
public function SpriteUIContainer(sprite:Sprite)
{
super();
this.explicitWidth = sprite.width;
this.explicitHeight = sprite.height;
this.x = sprite.x;
this.y = sprite.y;
addChild(sprite);
}
}
}
which is used in the following way:
private var circCentre:Sprite = new Sprite();
circCentre.x = mainContainer.width / 2;
circCentre.y = mainContainer.height / 2;
circCentre.graphics.lineStyle(3, 0xD0B917);
circCentre.graphics.beginFill(0xF2DF56, 0.2);
circCentre.graphics.drawCircle(20, 20, 50);
circCentre.graphics.endFill();
mainContainer.addChildAt(new SpriteUIContainer(circCentre), 1);
The circCentre Sprite never appears on screen and I don't understand how I can get it to appear.
Any help much appreciated!
By 'attached' do you mean you are adding new sprites to the UIComponent using the addChild method?
If so; You'll have to add code to size and position that new 'sprite' relative to the other objects in the container. There is no such code in your example. I recommend implementing this code in updateDisplayList().
More information on updateDisplayList() is in this Flex Component Lifecycle documentation.
If you need more help, you'll have to expand on how your adding new sprites to the UIComponent instance; and perhaps show an example where you add more than one.
I have a prompt string to be displayed in my combobox - this needs to be displayed in italics. When user makes any selection from the list - i need to change the style of the displayed content.
My css file:
.promptStyle
{
fontStyle: italic;
}
ComboBox.withPrompt
{
color: #FF0000;
fontWeight: normal;
textInputStyleName: promptStyle;
}
.regularStyle
{
fontStyle: normal;
}
ComboBox.withoutPrompt
{
color: black;
fontWeight: normal;
textInputStyleName: regularStyle;
}
My MXML file:
<?xml version="1.0" encoding="utf-8"?>
<mx:Application xmlns:mx="http://www.adobe.com/2006/mxml" layout="absolute"
minWidth="955" minHeight="600" initialize="init()">
<mx:Script>
<![CDATA[
[Bindable]
private var content:Array=new Array("Red", "Blue", "Green");
private function init():void {
StyleManager.loadStyleDeclarations("combos/combo_style.swf");
}
private function changeStyle():void {
var index:int = promptBox.selectedIndex;
if(index != -1)
promptBox.setStyle("styleName","withoutPrompt");
}
]]>
</mx:Script>
<mx:ComboBox id="promptBox" prompt="Select a color" dataProvider="{content}"
styleName="withPrompt" change="changeStyle()"/>
</mx:Application>
I am able to see the style change happening because the color changes; but the change specific to textInputStyleName does not get applied. Any help would be appreciated.
You should assign the style to the internal TextInput subcomponent, but for this you have to derive your own PromptingComboBox to access the protected textInput property.
I think the following class does basically what you want and should give you an idea:
public class PromptingComboBox extends ComboBox implements IFactory
{
private var _dropDown: List = null;
public function PromptingComboBox()
{
super.dropdownFactory = this;
}
public function newInstance(): *
{
_dropDown = new List();
_dropDown.addEventListener(ListEvent.CHANGE, onChangeDropDownList);
return _dropDown;
}
override protected function createChildren():void
{
super.createChildren();
this.textInput.setStyle("fontStyle", "italic");
}
private function onChangeDropDownList(event: Event): void
{
this.textInput.setStyle("fontStyle", "");
}
}
Thank you :) I was able to get it to work by sub-classing ComboBox as you suggested. Updating textInputStyleName in my css would have been a much cleaner solution for me since this is a huge existing application and now i have to get into several MXMLs and change the control to use the custom control instead - im guessing this is a bug in flex?
Anyways, thanks for your help!
I'm having trouble to resize my custom UIComponent that wrap flash.media.Video object (The reason I choose this way is because mx.control.VideoDisplay doesn't support streaming playback that available in flash.media.Video that is attachNetStream()). Once I create a 320x240 Video size and remove it from its parent, I can't replace it with another one, bigger or smaller.
Here's my code (this one only capture Camera not NetStream).
package media
{
import flash.media.Camera;
import flash.media.Video;
import mx.controls.VideoDisplay;
import mx.core.UIComponent;
public class VideoUI extends UIComponent
{
private var video:Video;
public function VideoUI(width:int, height:int)
{
super();
video = new Video(width, height);
var cam:Camera = Camera.getCamera();
video.attachCamera(cam);
addChild(video);
}
}
}
The other part,
<?xml version="1.0" encoding="utf-8"?>
<mx:Application xmlns:mx="http://www.adobe.com/2006/mxml" layout="absolute">
<mx:Script>
<![CDATA[
import media.VideoUI;
private function addVideoOutput():void
{
// initial video size
var video:VideoUI = new VideoUI(160,120);
HBoxVideo.addChild(video);
}
protected function resizeVideo(event:MouseEvent):void
{
var videoList:Array = HBoxVideo.getChildren();
for (var i:int = 0; i < videoList.length; i++)
{
var video:VideoUI = videoList.pop();
HBoxVideo.removeChild(video);
// new size that produce the previous size :(
video = new VideoUI(320, 240);
HBoxVideo.addChild(video);
}
}
]]>
</mx:Script>
<mx:Button click="addVideoOutput()" x="10" y="265" label="add"/>
<mx:HBox x="10" y="10" width="100%" id="HBoxVideo">
</mx:HBox>
<mx:Button x="58" y="265" label="resize" click="resizeVideo(event)" id="resizeButton"/>
</mx:Application>
Thank you very much.
By default, new instances of the Video class are 320 pixels wide by 240 pixels high. You will need access to your video in the VideoUI Class so that you can change the width and height.
As follows:
Change all appearances of your video variable in VideoUI.as to
_video
and apply a getter.
New Video UI Class
package media
{
import flash.media.Camera;
import flash.media.Video;
import mx.core.UIComponent;
public class VideoUI extends UIComponent
{
private var _video:Video;
public function VideoUI(width:int, height:int)
{
super();
_video = new Video(width, height);
var cam:Camera = Camera.getCamera();
_video.attachCamera(cam);
addChild(_video);
}
public function get video():Video{
return _video;
}
}
}
Replace in your main mxml file
video = new VideoUI(320, 240);
with
video.video.width=320;
video.video.height=240;
Note: You should rename your VideoUI instance to videoui or the sorts. It is a little confusing. You can also move this to your VideoUI Class or make a method. The choice is yours.
I have a page-like flex application. In my main application is a holder (a canvas) that displays all the pages. The problem is that the components that are loaded inside the holder are bigger than the holder is when the main application starts. The holder height is set to 100% but still he gets scrollbars when I load a bigger component inside of him then himself.
How can I solve this problem? And if it isn't solvable would it work correct if I use a viewstack?
--update---
the holder looks like this:
<mx:canvas height="100%">
</canvas>
But the end of the application is 500 pixels lower so canvas height is 500 pixels high. Now I put a new child inside the holder that is 1000 pixels high. Now the holder hight should be 1000 pixels high
Prior to adding the component to the main canvas check the size of the child component to see if its bigger than the parent. If it is then you can use scaleX and scaleY on the object to scale it down to fit in your defined area.
What you declared in your question is the default behavior for any flex container... put larger things in it and you'll get scrollbars. To eliminate the scrollbars use:
horizontalScrollPolicy="off"
verticalScrollPolicy="off
Edit:
Use "resizeToContent" on a tabNavigator or viewStack... or you can bind the width of the parent container to the width of the current child.
This is some code I was playing around with for an auto sizing canvas
// save as AutoResizeCanvas.as
package
{
import mx.containers.Canvas;
import mx.core.Container;
import mx.core.UIComponent;
public class AutoResizeCanvas extends Canvas
{
public function AutoResizeCanvas()
{
super();
}
override protected function measure():void
{
super.measure();
var nh:int = 0;
var nw:int = 0;
for( var n:int = 0; n< this.numChildren; n++)
{
var c:UIComponent = this.getChildAt( n) as UIComponent;
if( c is Container)
{
var cc:Container= c as Container;
nh += /*cc.viewMetricsAndPadding.top + */cc.viewMetricsAndPadding.bottom + c.height;
nw = Math.max( nw, cc.viewMetricsAndPadding.left + cc.viewMetricsAndPadding.right + c.width);
}
else
{
nh += c.height;
nw = Math.max( c.width, nw);
}
}
this.measuredWidth = nw;
this.measuredHeight = nh;
}
}
}
// test code, paste into your project's main mxml
<?xml version="1.0" encoding="utf-8"?>
<mx:Application xmlns:mx="http://www.adobe.com/2006/mxml" layout="absolute" xmlns:local="*">
<mx:Script>
<![CDATA[
import mx.containers.Panel;
import mx.containers.HBox;
private function addOne():void
{
var hb:Panel = new Panel();
hb.height = 100;
hb.width = 100;
hb.y = rc.numChildren * 110;
rc.addChild( hb);
}
]]>
</mx:Script>
<local:AutoResizeCanvas id="rc" verticalScrollPolicy="off" borderStyle="solid"/>
<mx:Button x="497" y="10" label="Add HBox" click="addOne()"/>
</mx:Application>