AS3: Extending Loader, how to draw border on its content - apache-flex

I'm extending Loader(), is it possible to draw a border around the content without resorting to some hacks? I understand Loader can't have any additional children, otherwise I would just create a shape to the contents size and add it. But is there a way to coerce or cast the content object in order to use the drawing API on it?

Usually you would just add the content to a display object when it's loaded and manipulate it as much as you want:
var loader:Loader = new Loader();
loader.contentLoaderInfo.addEventListener(Event.COMPLETE, eventCompleteHandler);
loader.load(new URLRequest(URL));
function eventCompleteHandler(event:Event):void
{
addChild(createElementWithBorder(event.target.content));
}
function createElementWithBorder(content:DisplayObject):DisplayObject
{
var container:Sprite = new Sprite();
container.addChild(content);
var border:Shape = container.addChild(new Shape()) as Shape;
border.graphics.lineStyle(2,0x000000);
border.graphics.drawRect(0, 0, content.width,content.height);
return container;
}
... But if you still annoyed you cannot use the Loader as a DisplayObjectContainer, using composition may be the clue (the following is just a quick example and needs to be accommodated of course):
public class LoaderBorder extends Sprite
{
private var loader:Loader;
public function LoaderBorder()
{
loader = new Loader()
}
public function load(url:String):void
{
loader.contentLoaderInfo.addEventListener(Event.COMPLETE, eventCompleteHandler);
loader.load(new URLRequest(url));
}
private function eventCompleteHandler(event:Event):void
{
loader.contentLoaderInfo.removeEventListener(Event.COMPLETE, eventCompleteHandler);
var content:DisplayObject = event.target.content;
var border:Shape = new Shape();
border.graphics.lineStyle(2,0x000000);
border.graphics.drawRect(0, 0, content.width,content.height);
addChild(content);
addChild(border);
}
}
To be used like a Loader :
var test:LoaderBorder = new LoaderBorder()
addChild(test);
test.load(URL);

Yes you can CAST the content of the Loader.
When you load an image you can do this (after the image is fully loaded):
Bitmap(yourLoader.content);
And with a SWF, i assume you can do this (haven't tested it):
Sprite(yourLoader.content);

You can add a filter to the movieClip that contains the Loader.
I think "glow" or "drop shadow" could be a good effect for this. If you set it right.

Related

How do I get list item selection working when using a png mask in an item renderer in a Flex 4.5 mobile app?

I'm creating a mobile app in which I need to show a calendar with months at the top. The months are part of a component that extends from SkinnableDataContainer (and has some custom scrolling/behaviour - which is why I did'nt use a spark list). I need the months to be shown as a 'trapezium' shaped tab and so I'm using a png image as a mask in the item renderer for the component.
When the mask is not applied, it all works well - the months render, the list/data container selection works when I click on a month and so on.
When the mask is applied, it renders well, scrolling and everything else seems to work well - but when I click on a month, nothing happens visually. And from the trace statements in my code, it appears list item selection is not changing. Looks like mouse clicks are not working.
Any ideas on how to fix this?
I've looked for similar sounding questions (but ask the opposite thing) here on SO. (http://stackoverflow.com/questions/1741172/restrict-mouseevents-to-mask-in-flex-skin)
Regards,
Krishna
Code:
public class TopCalendarMonthRenderer extends LabelItemRenderer {
[Embed(source="/assets/trapezium_alpha.png")]
private static var TrapeziumMask:Class;
private static var trapeziumMaskInstance:BitmapAsset;
override protected function createChildren():void {
super.createChildren();
setLabelProperties();
createMask();
}
private function createMask():void {
if (!this.maskShape){
if (!trapeziumMaskInstance){
trapeziumMaskInstance = (new TrapeziumMask()) as BitmapAsset;
}
maskShape = new Sprite();
//maskShape.visible = false;
//maskShape.mouseEnabled = false;
maskShape.cacheAsBitmap = true;
this.cacheAsBitmap = true;
this.addChild(maskShape);
//this.hitArea = maskShape;
}
}
override protected function drawBackground(unscaledWidth:Number, unscaledHeight:Number):void {
//don't call the parent's draw: because we draw our own background
var bgColor:uint = 0x555555;
if (this.selected)
bgColor = backgroundColor;
var g:Graphics = this.graphics;
g.beginFill(bgColor);
g.drawRoundRectComplex(0, 0, unscaledWidth, unscaledHeight, 3, 3, 0, 0);
g.endFill();
//TODO: make the mask a hitArea - so the user can interact with it - HOW?
drawMask();
}
private function drawMask():void {
var g:Graphics = maskShape.graphics;
var img:BitmapData = trapeziumMaskInstance.bitmapData;
g.beginBitmapFill(img, null, false, true);
//g.beginGradientFill(GradientType.RADIAL, [0xffffff, 0xff0000], [1, 0], [0, 255]);
//g.beginFill(0xff0000);
g.drawRect(0, 0, img.width, img.height);
g.endFill();
this.mask = maskShape;
//this.hitArea = maskShape;
}
}
I finally found this:
http://aaronhardy.com/flex/displayobject-quirks-and-tips/
This explains how setting a PNG image as an alpha mask on a DisplayObject breaks mouse events.
A work around is also provided in that article - which worked for me :)

Change mouse cursor to a bitmap (Flex 4)?

In Flex 4, how can I change the cursor to a Bitmap image determined at runtime? All the examples I've seen use CursorManager.setCursor to set the cursor to a class specified at compile time.
What I want to do is change the cursor to a bitmap whose bitmapData is determined by the context.
package cursor
{
import flash.display.BitmapData;
import flash.display.PixelSnapping;
import mx.core.BitmapAsset;
public class RuntimeBitmap1 extends BitmapAsset
{
public static var staticBitmapData:BitmapData;
public function RuntimeBitmap1()
{
super(staticBitmapData);
}
}
}
Usage:
var bitmapData:BitmapData = new BitmapData(50, 50, false, 0x88888888);
RuntimeBitmap1.staticBitmapData = bitmapData;
cursorManager.setCursor(RuntimeBitmap1, 0);
I wanted to draw a UIComponent as a cursor.
I managed it using a combination of Maxims answer and this Flex Cookbox article. The only change I had to make to Maxim answer was a s follows:
public function RuntimeBitmap1()
{
super(RuntimeBitmap1.staticBitmapData);
}
Otherwise staticBitmapData came through as null in the constructor.
Here are a few simple steps to change the default cursor with a bitmap image:
Create your cursor of type Bitmap by using an image of your choice. You can also set the bitmapData dynamically during runtime.
var DEFAULT_CURSOR_IMAGE : Class;
var myCursorBitmap : Bitmap;
...
myCursorBitmap = new DEFAULT_CURSOR_IMAGE();
Register to receive mouse move events and update cursor position accordingly.
function onMouseMove(event : MouseEvent) : void
{
myCursorBitmap.x = event.localX;
myCursorBitmap.y = event.localY;
}
Hide the real cursor by using Mouse.hide().
Show your custom cursor. You may update cursor shape later by setting bitmapData dynamically.
addChild(myCursorBitmap);
...
myCursorBitmap.bitmapData = myNewCursor;
To restore the default cursor, remove your cursor bitmap from the stage and call Mouse.show().

TextField in AS3 - programming a click listener

I want to add a simple piece of text to the stage and add a listener to do something when the user clicks it.
Here's my TextLink class:
package some.package
{
import flash.display.Sprite;
import flash.external.ExternalInterface;
import flash.text.TextField;
import flash.text.TextFieldAutoSize;
public class TextLink extends Sprite
{
public var tf:TextField = new TextField();
public var bspr:Sprite = new Sprite();
public function TextLink(tx:int, ty:int, tft:String):void
{
tf.text = tft;
tf.x = tx;
tf.y = ty;
tf.autoSize = TextFieldAutoSize.LEFT;
bspr.addChild(tf);
this.addChild(tf);
}
}
}
And here is the way I am calling it, along with the listener:
public function test_array_of_objects():void
{
var tmp:TextLink = new TextLink(30, 30, "some text");
tmp.addEventListener(MouseEvent.CLICK, roverNotify);
addChild(tmp);
}
protected function roverNotify(e:Event):void
{
ExternalInterface.call("console.log", "got a click");
}
...But I don't get a message for some reason.
I've imported everything successfully. Any thoughts on what else I can try?
Is your TextLink class an event dispatcher? You're trying to add a listener to the TextLink object, but the click listener needs to be attached to the text field that you're using inside TextLink. TextLink needs to be a DisplayObject of some kind to inherit the dispatching capabilities.
Also, constructors should not specify a return type (since they're just returning themselves) -- the :void should not be there where your TextLink constructor is.
Does function TextLink require something like this at the beginning:
var tf:Text = new Text();
Is the problem with clicking the Sprite or getting the event to fire? If it's the former you could try adding the code below.
tmp.mouseChildren = false;
tmp.buttonMode = true;
ExternalInterface.call("console.log", "got a click");
You have a JavaScript function defined like this??:
function console.log(inputString) {
//do something
}
Edit: Nevermind the above, forgot about Firebug.
Also, TextLink doesn't need to be an event dispatcher, though you may want to have TextLink set its mouseChildren property to false (unless you need to be able to select that text), so that you don't inadvertently trigger events on the TextField, and buttonMode to true.
Edit: Also, what's the point of?:
var bspr:Sprite = new Sprite();
bspr.addChild(tf);
Final edit
How about this? http://code.google.com/p/fbug/issues/detail?id=1494
Yes, you are correct, in FF3 the console is injected only when the page has
javascript and uses window.console.
If you put any js that accesses the console before the Flash loads it should work, eg
<script>
var triggerFirebugConsole = window.console;
</script>
Let us know if this works. It's unlikely that we can fix this soon.

How to prevent a component from being dragged out of the stage in Flex 3

I think there is a simple solution to this question, just not simple enough for me to find it.
Question:
How do you constrain a TitleWindow in Flex 3 from being dragged off the screen/stage? Is there a way to restrict the TitleWindow to the viewing area?
Example: Let's say I have an application that take 100% of the screen. Next, I create a TitleWindow via the PopUpManager. I can then proceed to click and hold (drag) that window off the screen, then release the mouse button. That window is now lost off-screen somewhere. Is there a way to keep the window from being dragged beyond the viewing area?
Thanks for the help in advance.
this is a very old post, but here's another way of doing it:
Whether you are extending the component or not, in the TitleWindow definition add the following line: move:"doMove(event)"
Import the Application library (import mx.core.Application;)
and add the doMove function:
private function doMove(event:Event):void
{//keeps TW inside layout
var appW:Number=Application.application.width;
var appH:Number=Application.application.height;
if(this.x+this.width>appW)
{
this.x=appW-this.width;
}
if(this.x<0)
{
this.x=0;
}
if(this.y+this.height>appH)
{
this.y=appH-this.height;
}
if(this.y<0)
{
this.y=0;
}
}
For flex 4 the answer is here: http://blog.flexexamples.com/2010/01/20/constraining-the-movement-on-a-spark-titlewindow-container-in-flex-4/
You can set its isPopUp property to false to prevent it from being dragged in the first place.
var popupWin:TitleWindow = PopUpManager.createPopUp(this, TitleWindow);
PopUpManager.centerPopUp(popupWin);
popupWin.isPopUp = false;
I don't know if the DragManager class in flex supports bounds checking, but if you really want to allow dragging but limit its bounds, you can still set isPopUp to false and implement the dragging code yourself so that the component never goes outside the limits specified by you. Check startDrag() method for an example. Bounds rectangle is the key.
Flex 4
<s:TitleWindow xmlns:fx="http://ns.adobe.com/mxml/2009"
xmlns:s="library://ns.adobe.com/flex/spark"
xmlns:mx="library://ns.adobe.com/flex/mx"
windowMoving="windowMovingHandler(event)">
.
.
.
protected function windowMovingHandler(event:TitleWindowBoundsEvent):void
{
var appBounds:Rectangle = parentApplication.getBounds(DisplayObject(parentApplication));
if(!appBounds.containsRect(event.afterBounds)){
event.preventDefault();
}
}
// for better precision, corect appBounds manualy, or, instead of "parentApplication.getBounds..." create new rectangle of your application size
Subclass the TitleWindow and add a canvas over the title bar as a drag proxy. Then you can explicity call startDrag with a boundary rectangle.
This is pretty skeletal, but should put you on the path...
The reason for the proxy is you may get some weird behavior when you click the titleBar label if you don't have the canvas over it.
public class MyTitleWindow extends TitleWindow
{
public var titleBarOverlay:Canvas;
override protected function createChildren():void
{
super.createChildren();
if(!titleBarOverlay)
{
titleBarOverlay = new Canvas();
titleBarOverlay.width = this.width;
titleBarOverlay.height = this.titleBar.height;
titleBarOverlay.alpha = 0;
titleBarOverlay.setStyle("backgroundColor", 0x000000);
rawChildren.addChild(titleBarOverlay);
}
addListeners();
}
override protected function updateDisplayList(w:Number, h:Number):void
{
super.updateDisplayList(w, h);
titleBarOverlay.width = this.width;
titleBarOverlay.height = this.titleBar.height;
}
private function addListeners():void
{
titleBarOverlay.addEventListener(MouseEvent.MOUSE_DOWN, onTitleBarPress, false, 0, true);
titleBarOverlay.addEventListener(MouseEvent.MOUSE_UP, onTitleBarRelease, false, 0, true);
}
private function onTitleBarPress(event:MouseEvent):void
{
// Here you can set the boundary using owner, parent, parentApplication, etc.
this.startDrag(false, new Rectangle(0, 0, parent.width - this.width, parent.height - this.height));
}
private function onTitleBarRelease(event:Event):void
{
this.stopDrag();
}
}
You could simply override the move function and prevent "illegal" movement (it is called internally by the Panel drag management).
I think that you also should listen on stage resize, because reducing it (e.g. if the user resize the browser window) could send your popup out of stage even without actually moving it.
public class MyTitleWindow extends TitleWindow {
public function MyTitleWindow() {
// use a weak listener, or remember to remove it
stage.addEventListener(Event.RESIZE, onStageResize,
false, EventPriority.DEFAULT, true);
}
private function onStageResize(event:Event):void {
restoreOutOfStage();
}
override public function move(x:Number, y:Number):void {
super.move(x, y);
restoreOutOfStage();
}
private function restoreOutOfStage():void {
// avoid the popup from being positioned completely out of stage
// (use the actual width/height of the popup instead of 50 if you
// want to keep your *entire* popup on stage)
var topContainer:DisplayObjectContainer =
Application.application.parentDocument;
var minX:int = 50 - width;
var maxX:int = topContainer.width - 50;
var minY:int = 0;
var maxY:int = topContainer.height - 50;
if (x > maxX)
x = maxX
else if (x < minX)
x = minX;
if (y > maxY)
y = maxY
else if (y < minY)
y = minY;
}
}
In your TitleWindow's creationComplete handler add the following:
this.moveArea.visible=false;
This will do the job.
On the other hand, if you have a custom skin, you can remove the "moveArea" part. This should work, too.

possible to display an arrayCollection of Sprites in a List component?

I have an arrayCollection of objects that extend Sprite, and have bitmaps within them.
I want to display these in a list (or some other component that would allow a user to scroll through them, and see their associated data.)
When I do: myList.dataProvider = myArrayCollection
the list just shows a bunch of lines of [Object, Item] instead of the visual sprites.
Here is a simplified version of my Object:
public class myUIC extends UIComponent
{
public var mySprite:Sprite = new Sprite;
[Embed(source="assets/BGimage.png")]
public var BGimage:Class;
public var myBitmap:Bitmap;
public var wordText:TextField = new TextField;
public function myUIC(myWord:String)
{
this.wordText.text = myWord;
this.myBitmap = new BGimage;
this.mySprite.addChild(this.myBitmap);
this.mySprite.addChild(this.wordText);
this.addChild(this.mySprite);
}
}
Tried many different ways to get it to show up in a List, but can't do it.
See this tutorial: Flex Examples - displaying icons in a flex list control
Sounds like you may want to try writing a simple item renderer (perhaps based off UIComponent) that adds the associated sprite the display list of the render using addChild().
try rawChildren.addChild for adding the Sprite
Here, try using an itemRenderer something like this. It ought to work with any generic DisplayObject. It's grabbing the width and height from the assigned data property, so you might need to set variableRowHeight to true in your actual list for it to work as expected.
package
{
import flash.display.DisplayObject;
import mx.controls.listClasses.IListItemRenderer;
import mx.core.UIComponent;
import mx.events.FlexEvent;
/*
Extending UIComponent means we can add Sprites (or any DisplayObject)
with addChild() directly, instead of going through the rawChildren property.
Plus, in this case, we don't need the extra overhead of Canvas's layout code.
IListItemRenderer lets us use it as a List's itemRenderer. UIComponent already
implements all of IListItemRenderer except for the data property
*/
public class SpriteRenderer extends UIComponent implements IListItemRenderer
{
// Implementing the data property for IListItemRenderer is really easy,
// you can find example code in the LiveDocs for IDataRenderer
private var _data:Object;
[Bindable("dataChange")]
public function get data():Object
{
return _data;
}
public function set data(value:Object):void
{
if (value !== _data) {
// We need to make sure to remove any previous data object from the child list
// since itemRenderers are recycled
if (_data is DisplayObject && contains(_data as DisplayObject)) {
removeChild(_data as DisplayObject);
}
_data = value;
// Now we just make sure that the new data object is something we can add
// and add it
if (_data is DisplayObject) {
this.width = (_data as DisplayObject).width;
this.height = (_data as DisplayObject).height;
addChild(_data as DisplayObject);
}
dispatchEvent(new FlexEvent(FlexEvent.DATA_CHANGE));
}
}
public function SpriteRenderer()
{
super();
}
}
}

Resources