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.
Related
I have a class in Flex that crates a custom IconItemRenderer by extending the IconItemRenderer base class. I'm using this custom item within a list and listen to mouse press. Depending on the location of the mouse press, I have different options. One of which is to navigate to a different view. I know how to use the change listener of the list to push to a new view but don't want to implement it. The idea for the mouse click is that depending on the location, I can remove elements from the list or open up the current element.
For the life of me I cannot find a method to navigate to a new view from within the IconItemRenderer. This is the code I'm using, both the class and the list where I implement it.
package components
{
import spark.components.Button;
import spark.components.IconItemRenderer;
import spark.utils.MultiDPIBitmapSource;
public class DeleteItemRenderer extends IconItemRenderer
{
private var btn:Button;
private var xIcon:MultiDPIBitmapSource;
public function DeleteItemRenderer()
{
super();
super.iconWidth = super.iconHeight = 40;
super.labelField = 'title';
super.decorator = "assets/delete.png";
}
override public function set data(value:Object):void{
super.data = value;
}
override protected function layoutContents(unscaledWidth:Number, unscaledHeight:Number):void{
setElementPosition(decoratorDisplay, unscaledWidth-40, 5);
setElementSize(decoratorDisplay, 40, 40);
}
override protected function measure():void{
measuredHeight = 50;
}
override protected function drawBackground(unscaledWidth:Number, unscaledHeight:Number):void{
graphics.beginFill(0xffffff);
graphics.drawRect(0, 0, unscaledWidth, unscaledHeight);
graphics.endFill();
decoratorDisplay.smooth = true;
graphics.lineStyle(1,0xcccccc,1);
graphics.drawRect(0, 0, unscaledWidth, unscaledHeight);
graphics.endFill();
}
}
}
List
<s:List id="survey_list" visible="true" width="98%" height="70%" contentBackgroundColor="#FFFFFF" horizontalCenter="0">
<s:itemRenderer>
<fx:Component>
<components:DeleteItemRenderer width="99.9%" height="98%" verticalAlign="top" click="detectActionPress();">
<fx:Script>
<![CDATA[
import spark.components.ViewNavigator;
import spark.components.View;
import mx.events.FlexEvent;
import mx.core.FlexGlobals;
import mx.core.UIComponent;
import spark.components.List;
private var application:UIComponent = FlexGlobals.topLevelApplication as UIComponent;
private var pressOpen:Number = application.width - 40;
//private var _navigator:ViewNavigator = FlexGlobals.topLevelApplication.navigation; //navigation is not defined uhhhh why????
private function detectActionPress():void{
var localX:Number = this.mouseX;
if(localX <= pressOpen){
engangeElement();
}
else{
deleteElement();
}
}
private function deleteElement():void{
var parentList:List = owner as List;
parentList.dataProvider.removeItemAt(parentList.dataProvider.getItemIndex(data));
trace('element removed');
}
private function engangeElement():void{
var parentList:List = owner as List;
var _test:ViewNavigator = this.parentDocument as ViewNavigator;
//this.parentApplication.navigator.pushView(views.UnfinishedSurvey, parentList.selectedItem.shortcode)
_test.pushView(views.UnfinishedSurvey, parentList.selectedItem.shortcode);
}
]]>
</fx:Script>
</components:DeleteItemRenderer>
</fx:Component>
</s:itemRenderer>
</s:List>
Any ideas how I could push a new view from engageElement();
Why don't I have access to the navigator?
Thanks
Generally; I would recommend dispatching an event from inside the itemRenderer. Be sure that the event bubbles.
You can listen to the event on a class which is a hierarchical parent of the itemRenderer / List.
In the event handler you can access the navigator.
I wrote about the approaches here; which may provide more details.
There are alternate ways to do this. You could use a class that has an instance to the navigator and inject that into your renderer using some type of dependency injenction (DI) framework. Robotlegs and Swiz are two ActionScript based frameworks that support this.
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 was laying out my Flex components using mxml and had them working correctly. But then I wanted to switch them over to Actionscript because I wanted them to extend a base component that provides default functionality.
I've go the code working except that my components that used to fill the entire space using width="100%" and height="100%" appear to display just using the default sizes. Do you know How I can get them to take up the entire space again?
Here is a test component I am playing with that exhibits the problem.
Main.mxml
<?xml version="1.0" encoding="utf-8"?>
<mx:Application xmlns:mx="http://www.adobe.com/2006/mxml" xmlns:bbq="components.*"
backgroundGradientColors="[#000000, #3333dd]"
minWidth="480" minHeight="320"
layout="vertical" verticalAlign="top" horizontalAlign="center"
paddingLeft="0" paddingRight="0" paddingTop="30" paddingBottom="0"
width="100%" height="100%" >
<mx:VBox backgroundColor="0xcccc66">
<mx:ViewStack id="mainViewStack" width="100%" height="100%" color="0x323232">
<bbq:TestComp id="testComp" height="100%" width="100%" />
<bbq:ResultsBox />
</mx:ViewStack>
</mx:VBox>
TestComp.as
package components {
import mx.containers.VBox;
import mx.containers.Panel;
import flash.events.*;
public class TestComp extends VBox {
private var p:Panel;
function TestComp(){
super();
percentHeight = 100;
percentWidth = 100;
}
override protected function createChildren():void {
super.createChildren();
p = new Panel();
p.percentHeight = 100;
p.percentWidth = 100;
p.title = "Test Comp";
addChild(p);
}
}
}
Add inherited method calls; calls to super() methods;
import mx.containers.VBox;
import mx.containers.Panel;
import flash.events.*;
public class TestComp extends VBox {
private var p:Panel;
function TestComp(){
super(); // add this line
percentHeight = 100;
percentWidth = 100;
}
override protected function createChildren():void {
super.createChildren(); // add this line
p = new Panel();
p.percentHeight = 100;
p.percentWidth = 100;
p.title = "Test Comp";
addChild(p);
}
}
In you mxml:
<local:TestComp width="100%" height="100%" />
I think I might know what it is. I think I explicitly set the width and height of one of the other components in the viewstack, and I think that affected the viewstack itself so that all of the other components that were added to it also go those dimensions.
resizeToContent=true
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);
}
}
}
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>