In Flex, I am trying to design 3 buttons similar to the image uploaded at
http://www.freeimagehosting.net/uploads/f14d58b49e.jpg
The mouse over/click on image should work only on red colored area of the button.
How can I manage the Mouse clicks or Irregular Button shapes in Flex?
Thnx ... Atul
Check this out: flexlib > ImageMap.
Taken from stackOverflow
Use button skins based on a vector graphic (e.g., one made in Illustrator), save each state as a named symbol in the document, then export as SWF. Reference the skins as follows:
.stepButton {
upSkin: Embed(source="myfile.swf", symbol="StepButton");
downSkin: Embed(source="myfile.swf", symbol="StepButtonDown");
overSkin: Embed(source="myfile.swf", symbol="StepButtonOver");
disabledSkin: Embed(source="myfile.swf", symbol="StepButtonDisabled");
}
Flash will automatically determine the hit area from the visible portion. This example (not called "myfile.swf") is working for us right now in an application.
Create ArrowButtonsHolder class by inheriting from Canvas
Create 3 children classes also inherited from Canvas. For example LeftArrowButton, MiddleArrowButton, RightArrowButton
public class LeftArrowButton:Canvas {
protected override function updateDisplayList(unscaledWidth:Number, unscaledHeight:Number):void
{
super.updateDisplayList(unscaledWidth, unscaledHeight);
// draw your arrow here
// use graphics to do it
graphics.beginFill(0xFF0000);
graphics.lineStyle(1, 0x000000);
graphics.moveTo(0, 0);
graphics.lineTo(30, 0);
graphics.lineTo(50, 25);
graphics.lineTo(30, 50);
graphics.lineTo(0, 50);
graphics.lineTo(0, 0);
graphics.endFill();
}
}
You also can create general class ArrowButton and inherit another 3 from that class and override drawing function
Add this 3 child button object to ArrowButtonsHolder by overriding createChildren():void method
public class ArrowButtonsHolder:Canvas {
// ...
private var leftArrowButton:LeftArrowButton;
private var middleArrowButton:MiddleArrowButton;
private var rightArrowButton:RightArrowButton;
// ...
protected override function createChildren():void {
super();
// create buttons
leftArrowButton = new LeftArrowButton();
middleArrowButton = new LeftArrowButton();
rightArrowButton = new LeftArrowButton();
// add them to canvas
addChild(leftArrowButton);
addChild(middleArrowButton);
addChild(rightArrowButton);
// position these button by adjusting x, y
leftArrowButton.x = 0;
middleArrowButton.x = 50;
rightArrowButton.x = 100;
// assign event listeners
leftArrowButton.addEventListener(MouseEvent.CLICK, onLeftArrowButtonClick);
middleArrowButton.addEventListener(MouseEvent.CLICK, onMiddleArrowButtonClick);
rightArrowButton.addEventListener(MouseEvent.CLICK, onRightArrowButtonClick);
}
private onLeftArrowButtonClick(event:MouseEvent):void
{
trace("Left button clicked");
}
// .. etc for another 2 methods implemented here
}
PS: There might be tons of syntax mistakes in my code but you should get general idea how to do it
Related
FlashBuilder/Spark mobile project view action bar.
Currently if you set the chromeColor of a button in the actionbar it only shows up when on the press state of the button. It does not change the color of the button default state. I could not find any way to style it.
After some digging I found that TransparentActionButtonSkin.as was overriding the drawBrackground function and specifically removing chromeColor and only allowing it to show on a the button down state.
I overrode that "little gem" with my own class.
package view_components
{
import mx.core.mx_internal;
import spark.skins.mobile.TransparentActionButtonSkin;
use namespace mx_internal;
public class ActionbarColoredButton extends TransparentActionButtonSkin
{
public function ActionbarColoredButton()
{
super();
}
override protected function drawBackground(unscaledWidth:Number, unscaledHeight:Number):void
{
// omit super.drawBackground() to drawRect instead
// only draw chromeColor in down state (transparent hit zone otherwise)
//NO, I want colored action buttons
var chromeColor:uint = getStyle(fillColorStyleName);
var chromeAlpha:Number = 1;
graphics.beginFill(chromeColor, chromeAlpha);
graphics.drawRect(0, 0, unscaledWidth, unscaledHeight);
graphics.endFill();
}
}
}
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 :)
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().
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.
How would one create a custom MXML component in flex which is based on an existing component but draws an overlay over this existing component in certain situations.
Ideally, the new component should be based on (derive from) the exiting component so that occurrences of the existing component could just be swapped out with the new one.
I tried to override updateDisplayList() in the new component and to paint the overlay using this.graphics. This resulted in the overlay being drawn underneath the children of the existing component. I also tried to do the drawing upon receiving a render-event which lead to similar results.
When the external condition which should trigger the display of the overlay changes, I call invalidateDisplayList() on my new component. That works to trigger the drawing for both cases described above. The remaining problem seems to be to figure out how to draw on top of all the other components once they are added.
The following example should illustrate what I tried to do; when overlayEnabled was set and the component's invalidateDisplayList() method was called, the red rectangle would get painted in the background....
// NewComponent.mxml
<ExistingComponent ...>
<mx:Script>
...
public var overlayEnabled:Boolean;
override protected updateDisplayList(...) {
super.updateDisplayList(...)
if (overlayEnabled) {
var g:Graphics = this.graphics;
g.beginFill(0xFF0000, 0.5);
g.drawRect(0, 0, width, height);
g.endFill();
}
}
...
</mx:Script>
</ExistingComponent>
Also, feel free to suggest different approaches.
You will have to add a DisplayObject for you overlay and insure when you call updateDisplayList that it is place on the top of the other.
public var overlayEnabled:Boolean;
public overlayHolder:(whatever display object you want to use)
override protected updateDisplayList(...) {
super.updateDisplayList(...)
if (overlayEnabled) {
if (overlayHolder.parent != this){
addChild(overlayHolder);
} else {
if (numChildren > 0)
setChildIndex(overlayHolder, numChildren-1);
}
var g:Graphics = overlayHolder.graphics;
g.beginFill(0xFF0000, 0.5);
g.drawRect(0, 0, width, height);
g.endFill();
} else if (overlayHolder.parent == this) {
removeChild(overlayHolder);
}
}
Edit:
One property you can use to add your overlay to the display list can be the rawchildren:
package {
import flash.display.Graphics;
import flash.display.Sprite;
import mx.containers.VBox;
public class MyVBox extends VBox {
public var overlayEnabled : Boolean = true;
public var overlay : Sprite = new Sprite();
public function MyVBox() {
super();
}
protected override function updateDisplayList(unscaledWidth : Number, unscaledHeight : Number) : void {
super.updateDisplayList(unscaledWidth, unscaledHeight);
if (overlayEnabled) {
if (overlay.parent != this) {
rawChildren.addChild(overlay);
} else {
if (rawChildren.numChildren > 0)
rawChildren.setChildIndex(overlay, rawChildren.numChildren - 1);
}
var g : Graphics = overlay.graphics;
g.beginFill(0xFF0000, 0.5);
g.drawRect(0, 0, width, height);
g.endFill();
} else if (overlay.parent == this) {
rawChildren.removeChild(overlay);
}
}
}
}