So my mario project must include a staple of Mario's movement, and that of course is the option to jump a short height or a fairly large one. As we all know, holding down the jump button makes him jump higher, that's what my goal is here. In my case, that button is X and I am unsure of how to do that.
This is currently my unsuccessful attempt, and gravity is set to 0.87 by default in my variables.
This is in my keyDownHandler (when the key is pressed)
if (event.keyCode == Keyboard.X && onGround == true)
{
vy += jumpForce;
holdJump = true;
onGround = false;
if(holdJump == true && onGround == false)
{
_mario.y += 1;
}
else
{
vy = vy + (grav * 0.20);
holdJump = false;
}
This is in my keyUpHandler (when the key is not pressed/let go)
if (event.keyCode == Keyboard.X)
{
if (holdJump == false)
{
accy = 0;
gravity = 0.80;
incSpeedY = 0;
}
}
Ok, I've extended my comment.
You can use standard vy=vyLast-g*(t-tLast), and just set vyLast to min(0,vyLast) when jump key is released, and set it to jump starting speed when jump key is pressed on the ground.
Here is the sample Adobe Air application with jumping red circle. It implements the logics that I've described into the comment:
<?xml version="1.0" encoding="utf-8"?>
<s:WindowedApplication xmlns:fx="http://ns.adobe.com/mxml/2009"
xmlns:s="library://ns.adobe.com/flex/spark"
xmlns:mx="library://ns.adobe.com/flex/mx"
>
<fx:Script>
<![CDATA[
import flash.utils.getTimer;
import mx.graphics.SolidColor;
public var marioY:Number = 0; //jump height above ground (meters)
public var g:Number = -9.81; //meter/(second^2)
public var lastTime:Number = NaN;
public var lastVerticalSpeed:Number = 0;//speed of a flight -meters/second
public var jumpSpeed:Number = 10;//initial jump speed - meters/second
public var timeRatio:Number = 1000;//milliseconds in a second
public var heightRatio:Number = 50; //pixels/meter
protected function get landed():Boolean{
return marioY <= 0 && lastVerticalSpeed <= 0;
}
protected function onKeyDown(event:KeyboardEvent):void{
if(event.keyCode==Keyboard.UP && landed){
lastVerticalSpeed = jumpSpeed;
trace('fly!');
}
}
protected function onKeyUp(event:KeyboardEvent):void{
if(event.keyCode==Keyboard.UP){
lastVerticalSpeed = Math.min(0,lastVerticalSpeed);
trace('fall!');
}
}
protected function onEnterFrame(event:Event):void{
if(!isNaN(lastTime)){
var deltaTime:Number = (getTimer() - lastTime)/timeRatio;
marioY+=lastVerticalSpeed*deltaTime;
if(landed){
lastVerticalSpeed=0;
marioY=0;
}else{
lastVerticalSpeed+=g*deltaTime;
}
}
mario.y=area.height-marioY*heightRatio-20;
lastTime = getTimer();
}
]]>
</fx:Script>
<s:Group width="100%" height="100%" keyDown="onKeyDown(event)" keyUp="onKeyUp(event)"
enterFrame="onEnterFrame(event)" id="area"
creationComplete="area.setFocus()"
>
<s:Rect width="100%" height="100%" fill="{new SolidColor(0x0000FF)}"/>
<s:Ellipse id="mario" width="10" height="10" fill="{new SolidColor(0xFF0000)}"
y="100" x="100"
/>
</s:Group>
</s:WindowedApplication>
Related
I am trying to put a gap of 20-30px between the Alert box buttons(YES and NO).
but unable to find such styling point in flex. I have tried horizontal-gap, and also padding, but in vain.
Below is the sample code i am trying, which i found when browsing through sites.
<?xml version="1.0" encoding="utf-8"?>
<mx:Application name="Alert_style_test"
xmlns:mx="http://www.adobe.com/2006/mxml"
layout="vertical"
verticalAlign="middle"
backgroundColor="white"
creationComplete="showAlert()">
<!-- Used by the Alert control. -->
<mx:String id="message">The quick brown fox jumped over the lazy dog.
The quick brown fox jumped over the lazy dog.</mx:String>
<mx:String id="title">The quick brown fox jumped over the lazy dog?</mx:String>
<mx:Script>
<![CDATA[
import mx.controls.Alert;
private var a:Alert;
private function showAlert():void {
Alert.yesLabel = "Yes";
Alert.noLabel = "No";
Alert.buttonWidth = 50;
a = Alert.show(
message,
title,
Alert.NO | Alert.YES
);
/* Make the Alert form's text non-selectable. */
a.mx_internal::alertForm.mx_internal::textField.selectable = false;
}
]]>
</mx:Script>
<mx:Style>
Alert{
color : #124332;
background-color: #ffffff;
header-colors : #243322, #243322;
header-height:19;
drop-shadow-enabled: true;
drop-shadow-color :#243322;
corner-radius :6;
border-style :solid;
border-thickness: 1;
border-color : #243322;
footer-colors : #243322, #ffffff;
title-style-name : "title";
horizontal-gap:500;
horizontal-separator-skin:white;
}
.title{
font-family :Verdana;
font-size :10;
font-weight :bold;
color :#ffffff;
}
.alertButton {
letterSpacing: 0;
fontSize: 11;
cornerRadius: 10;
fontWeight: normal;
textRollOverColor: white;
color: red;
horizontal-gap:-500;
}
</mx:Style>
<!-- Click to launch Alert control. -->
<mx:Button label="Launch Alert" click="showAlert();" />
</mx:Application>
Try something like this:
Add FlexEvent.UPDATE_COMPLETE to alertForm in your alert:
a.mx_internal::alertForm.addEventListener(FlexEvent.UPDATE_COMPLETE, alertForm_updateHandler);
And in this handler copy some stuff from original alertForm updateDisplayList method:
private function alertForm_updateHandler(event:FlexEvent):void
{
var form:UIComponent = a.mx_internal::alertForm;
var buttons:Array = a.mx_internal::alertForm.mx_internal::buttons;
var newX:Number;
var newY:Number;
var newWidth:Number;
newWidth = buttons.length * (buttons[0].width + 120) - 120;
newX = Math.round((form.width - newWidth) / 2);
for (var i:int = 0; i < buttons.length; i++)
{
buttons[i].x = newX
buttons[i].tabIndex = i + 1;
newX += buttons[i].width + 120;
}
}
where 120 is your new gap.
Hope this can be useful.
The only idea I have is to implement a new class as a child of TitleWindow or Panel and to add all the features you want to have. I know it is not the best solution, but you can try it.
Here are my proposals:
//Application
<?xml version="1.0" encoding="utf-8"?>
<mx:Application xmlns:fx="http://ns.adobe.com/mxml/2009"
xmlns:s="library://ns.adobe.com/flex/spark"
xmlns:mx="library://ns.adobe.com/flex/mx"
minWidth="955" minHeight="600">
<fx:Script>
<![CDATA[
import mx.events.CloseEvent;
private function onBtnClick():void
{
var title:String = "The quick brown fox jumped over the lazy dog?";
var message:String = "The quick brown fox jumped over the lazy dog. The quick brown fox jumped over the lazy dog.";
AdvancedAlert.buttonWidth = 70;
AdvancedAlert.buttonGap = 50;
AdvancedAlert.commonWidth = 400;
AdvancedAlert.show(message, title, AdvancedAlert.YES | AdvancedAlert.NO | AdvancedAlert.CANCEL, this._closeHandler);
}
private function _closeHandler(evt:CloseEvent):void
{
switch (evt.detail)
{
case AdvancedAlert.YES:
trace("yes");
break;
case AdvancedAlert.NO:
trace("no");
break;
case AdvancedAlert.OK:
trace("ok");
break;
case AdvancedAlert.CANCEL:
trace("cancel");
break;
}
}
]]>
</fx:Script>
<mx:Button click="onBtnClick()" label="Alert"/>
</mx:Application>
//Alert implementation
<?xml version="1.0" encoding="utf-8"?>
<mx:TitleWindow xmlns:fx="http://ns.adobe.com/mxml/2009"
xmlns:mx="library://ns.adobe.com/flex/mx"
layout="absolute"
width="{commonWidth}"
height="110"
styleName="titleWindow"
verticalScrollPolicy="off"
horizontalScrollPolicy="off"
creationComplete="onCreationComplete()" >
<fx:Style>
#namespace mx "library://ns.adobe.com/flex/mx";
.windowStyles {
color: #ffffff;
}
.titleWindow {
borderAlpha: 1.0;
borderColor: #8a9faa;
backgroundColor: #8a9faa;
cornerRadius: 5;
}
</fx:Style>
<fx:Script>
<![CDATA[
import mx.collections.ArrayCollection;
import mx.controls.Button;
import mx.core.FlexGlobals;
import mx.events.CloseEvent;
import mx.managers.PopUpManager;
public static const YES:uint = 0x0001;
public static const NO:uint = 0x0002;
public static const OK:uint = 0x0004;
public static const CANCEL:uint= 0x0008;
[Bindable]private var message:String;
[Bindable]public static var buttonGap:int = 20;
public static var buttonWidth:int = 80;
[Bindable]public static var commonWidth:int = 80;
[Bindable]private var buttonHeight:int = 25;
private var buttons:uint;
private var buttonNames:ArrayCollection = new ArrayCollection();
private function onCreationComplete():void
{
addButtons();
}
private function addButtons():void
{
buttonNames.removeAll();
hbButtons.removeAllChildren();
if ((YES & buttons) != 0)
buttonNames.addItem("Yes");
if ((NO & buttons) != 0)
buttonNames.addItem("No");
if ((OK & buttons) != 0)
buttonNames.addItem("Ok");
if ((CANCEL & buttons) != 0)
buttonNames.addItem("Cancel");
for each (var bn:String in buttonNames)
{
var btn:Button = new Button();
btn.width = buttonWidth;
btn.height = buttonHeight;
btn.label = bn;
btn.name = bn;
btn.addEventListener(MouseEvent.CLICK, onBtnClick);
hbButtons.addChild(btn);
}
}
private function onBtnClick(evt:Event):void
{
var currentButtonName:String = (evt.currentTarget as Button).name;
var closeEvent:CloseEvent = new CloseEvent(CloseEvent.CLOSE);
switch (currentButtonName)
{
case "Yes":
closeEvent.detail = YES;
break;
case "No":
closeEvent.detail = NO;
break;
case "Ok":
closeEvent.detail = OK;
break;
case "Cancel":
closeEvent.detail = CANCEL;
break;
}
this.dispatchEvent(closeEvent);
PopUpManager.removePopUp(this);
}
public static function show(message:String = "", title:String = "", buttons:uint = 0x4, closeHandler:Function = null):void
{
var advancedAlert:AdvancedAlert = new AdvancedAlert();
advancedAlert.message = message;
advancedAlert.title = title;
advancedAlert.buttons = buttons;
if (closeHandler != null)
advancedAlert.addEventListener(CloseEvent.CLOSE, closeHandler);
PopUpManager.addPopUp(advancedAlert, Sprite(FlexGlobals.topLevelApplication), true);
PopUpManager.centerPopUp(advancedAlert);
}
]]>
</fx:Script>
<mx:VBox id="vbMain" width="100%" height="100%">
<mx:Text id="txtMessage" text="{message}" width="100%" height="35" color="0xffffff" selectable="false"/>
<mx:HBox id="hbButtons" height="{buttonHeight}" width="100%" horizontalGap="{buttonGap}" horizontalAlign="center"/>
</mx:VBox>
</mx:TitleWindow>
I am trying to place 5 cards overlapping 30% on one another. and trying to give a movement to it using mouse events . It should drop within the 5 cards only, not outside of that.
I have learned the drag and drop events and executed it, but i cannot place the card within the 5 cards .
Please somebody help me in this. Please Check the Below Code:
<?xml version="1.0" encoding="utf-8"?>
<mx:Application xmlns:mx="http://www.adobe.com/2006/mxml" layout="absolute" creationComplete="carcan();">
<mx:Script>
<![CDATA[
import mx.containers.Canvas;
import mx.controls.Image;
private var images:Array;
private var images1:Array;
private const IMAGE_COUNT:uint = 5;
private var img:Image;
private var img1:Image;
private var points:flash.geom.Point;
private var offset_x:int;
private var offset_y:int;
private var canal:Canvas;
private var doDrag:Boolean;
[Embed(source='cards/2C.png')]
private var Image0:Class;
[Embed(source='cards/2D.png')]
private var Image1:Class;
[Embed(source='cards/2H.png')]
private var Image2:Class;
[Embed(source='cards/2S.png')]
private var Image3:Class;
[Embed(source='cards/3C.png')]
private var Image4:Class;
public function carcan():void
{
canal = new Canvas();
canal.setStyle("backgroundColor","blue");
canal.x=100;
canal.y=50;
canal.width=500;
canal.height=400;
this.addChild(canal);
init();
}
public function init():void
{
images = new Array(IMAGE_COUNT);
for (var i:int = 0; i < IMAGE_COUNT; i++)
{
img= new Image();
img1= new Image();
images[i] = this["Image" + i];
trace(images[i]);
img.x=(i*30)+50;
img.source=images[i];
img.id="Image"+i;
canal.addChild(img);
img.addEventListener(MouseEvent.MOUSE_DOWN, md);
img.addEventListener(MouseEvent.MOUSE_MOVE, mm);
canal.addEventListener(MouseEvent.MOUSE_OUT,smu);
img.addEventListener(MouseEvent.MOUSE_UP, mu);
}
}
public function smu(event:MouseEvent):void
{
img.alpha=1;
img.stopDrag();
doDrag=false;
setCards();
}
public function mo(event:MouseEvent):void
{
if(doDrag==true)
{
img.addEventListener(MouseEvent.MOUSE_DOWN, md);
img.addEventListener(MouseEvent.MOUSE_UP, mu);
img.stopDrag();
img.alpha=1;
img.removeEventListener(MouseEvent.MOUSE_MOVE, mm);
}
else
{
img.addEventListener(MouseEvent.MOUSE_MOVE, mm);
}
}
public function md(event:MouseEvent):void
{
img = new Image();
doDrag=true;
canal.setChildIndex(Image(event.target),images.length-1);
img.addEventListener(MouseEvent.MOUSE_MOVE, mm);
}
public function mm(event:MouseEvent):void
{
if(doDrag==true)
{
points = new Point();
images = new Array(IMAGE_COUNT);
img = new Image();
img = Image(event.target);
points.x=event.target.x;
points.y=event.target.y;
points = localToGlobal(points);
img.x=points.x;
img.y=points.y;
img.alpha=0.7;
img.addEventListener(MouseEvent.MOUSE_UP, mu);
var boundar:flash.geom.Rectangle = new Rectangle(this.x, this.y, 250, 100);
}
}
public function mu(event:MouseEvent):void
{
img.alpha=1;
canal.stopDrag();
doDrag=false;
canal.stopDrag();
doDrag=false;
var current:Image = event.currentTarget as Image;
var num1:int = current.x;
if(num1 < 50){
canal.setChildIndex(current, images.length-5);
current.y=0;
setCards();
}
if(num1 > 50 && num1 < 80){
canal.setChildIndex(current, images.length-4);
current.y=0;
setCards();
}
if(num1 > 80 && num1 < 110){
canal.setChildIndex(current, images.length-3);
current.y=0;
setCards();
}
if(num1 > 110 && num1 < 140){
canal.setChildIndex(current, images.length-2);
current.y=0;
setCards();
}
if(num1 > 140 && num1 < 170){
canal.setChildIndex(current, images.length-2);
current.y=0;
setCards();
}
if(num1 > 170){
canal.setChildIndex(current, images.length-1);
current.y=0;
setCards();
}
}
private function setCards():void{
var b:int = 0;
var a:int;
var cardsArray:Array = canal.getChildren();
for(a = 0;a < cardsArray.length; a++)
{
canal.getChildAt(a).x = 50+b;
b=b+30;
canal.getChildAt(a).y=0;
}
}
]]>
</mx:Script>
</mx:Application>
PS: I am trying to replace the drag and drop events with mouse events and get the same functionality using mouse events. Please somebody help me in this.
Hope Below Code may help you: - You can use Drag and drop as per below code and create it in AS. I have created it in MXML which will give you some idea what you are looking for: -
<?xml version="1.0" encoding="utf-8"?>
<s:Application xmlns:fx="http://ns.adobe.com/mxml/2009"
xmlns:s="library://ns.adobe.com/flex/spark"
xmlns:mx="library://ns.adobe.com/flex/mx" minWidth="955" minHeight="600" xmlns:local="*">
<fx:Declarations>
<!-- Place non-visual elements (e.g., services, value objects) here -->
</fx:Declarations>
<fx:Script>
<![CDATA[
import mx.core.DragSource;
import mx.core.UIComponent;
import mx.events.DragEvent;
import mx.managers.DragManager;
private function doDragEnter(event:DragEvent):void
{
DragManager.acceptDragDrop(UIComponent(event.target));
}
private function doDragDrop(event:DragEvent):void
{
var img:RummyItemRenderer;
if (event.dragInitiator.parent == mainCanvas)
{
img = event.dragInitiator as RummyItemRenderer;
//mainCanvas.swapChildren(img, event.currentTarget as RummyItemRenderer);
var index:Number = mainCanvas.getChildIndex(event.currentTarget as RummyItemRenderer);
mainCanvas.removeChild(img);
mainCanvas.addChildAt(img,index);
setCardsPosition();
}
}
private function setCardsPosition():void{
var b:int = 0;
var a:int;
var cardsArray:Array = mainCanvas.getChildren();
for(a = 0;a < cardsArray.length; a++)
{
mainCanvas.getChildAt(a).x = 50+b;
b = b+30;
mainCanvas.getChildAt(a).y=20;
}
}
private function doDragStart(event:MouseEvent):void
{
var dragInitiator:RummyItemRenderer = event.currentTarget as RummyItemRenderer;
var dragSource:DragSource = new DragSource();
DragManager.doDrag(dragInitiator, dragSource, event);
}
]]>
</fx:Script>
<mx:Canvas id="mainCanvas" backgroundColor="#DDDDDD" width="400" height="200" x="50" y="50">
<local:RummyItemRenderer id="firstID" x="{mainCanvas.x}" y="20" width="200" height="80%" backgroundColor="#FF0000"
mouseDown="doDragStart(event)" dragEnter="doDragEnter(event)" dragDrop="doDragDrop(event)"
setImageSource="myImageURL"/>
<local:RummyItemRenderer id="secondID" x="{mainCanvas.x + 30}" y="20" width="200" height="80%" backgroundColor="#00FF00"
mouseDown="doDragStart(event)" dragEnter="doDragEnter(event)" dragDrop="doDragDrop(event)"
setImageSource="myImageURL"/>
<local:RummyItemRenderer id="thirdID" x="{mainCanvas.x + 60}" y="20" width="200" height="80%" backgroundColor="#0000FF"
mouseDown="doDragStart(event)" dragEnter="doDragEnter(event)" dragDrop="doDragDrop(event)"
setImageSource="myImageURL"/>
</mx:Canvas>
</s:Application>
RummyItemRenderer.mxml
<?xml version="1.0" encoding="utf-8"?>
<mx:Canvas xmlns:fx="http://ns.adobe.com/mxml/2009"
xmlns:s="library://ns.adobe.com/flex/spark"
xmlns:mx="library://ns.adobe.com/flex/mx" width="100%" height="100%">
<fx:Declarations>
<!-- Place non-visual elements (e.g., services, value objects) here -->
</fx:Declarations>
<fx:Script>
<![CDATA[
[Bindable]
private var _setImageSource:String;
public function get setImageSource():String
{
return _setImageSource;
}
public function set setImageSource(sourceURL:String):void
{
_setImageSource = sourceURL;
}
]]>
</fx:Script>
<s:Image id="imageID" source="{_setImageSource}"/>
</mx:Canvas>
I adopted the code from this website,
http://blog.ninjacaptain.com/2010/03/flex-chart-zoom-window/
Note that the bug mentioned in the web blog concerning displaying datatips appears to have been fixed (at least as of 4.5.1 SDK). I'm able to see the datatips fine.
The code works well except for one issue. I've added the complete code below so you can just copy and paste as a new Flex application and run it.
The problem is simply when the user clicks once without dragging, which gives the following error (make sure you click it AS SOON AS the app first runs):
TypeError: Error #1009: Cannot access a property or method of a null object reference.
at mx.charts.chartClasses::CartesianDataCanvas/localToData()[E:\dev\4.5.1\frameworks\projects\charts\src\mx\charts\chartClasses\CartesianDataCanvas.as:580]
Is there a way to capture when the user clicks without dragging like this and call some function to handle it? Or, any way to avoid this error? Thanks for any comments/suggestions.
The code is:
<?xml version="1.0" encoding="utf-8"?>
<s:Application xmlns:fx="http://ns.adobe.com/mxml/2009"
xmlns:s="library://ns.adobe.com/flex/spark"
xmlns:mx="library://ns.adobe.com/flex/mx"
initialize="init()" width="600" height="520">
<fx:Script>
<![CDATA[
[Bindable]
private var profits:Array;
private var dragStart:Point;
private var dragEnd:Point;
private var zooming:Boolean;
// initializes the data provider with random data
private function init():void{
profits = new Array({month: 0, profit: 15});
for(var i:int=1; i<40; i++)
profits.push({month: i, profit: Math.round(Math.random()*25-10)+profits[i-1].profit});
}
// sets the start point of the zoom window
private function startDraw(e:MouseEvent):void{
zooming = true;
dragStart = new Point(series1.mouseX, series1.mouseY);
}
// draws the zoom window as your mouse moves
private function showDraw(e:MouseEvent):void{
if(zooming){
dragEnd = new Point(series1.mouseX, series1.mouseY);
// finds the top-left and bottom-right ponits of the zoom window
var TL:Point = new Point(); // top-left point
var BR:Point = new Point(); // bottom-right point
if(dragStart.x < dragEnd.x){
TL.x = dragStart.x;
BR.x = dragEnd.x;
}
else{
TL.x = dragEnd.x;
BR.x = dragStart.x;
}
if(dragStart.y < dragEnd.y){
TL.y = dragStart.y;
BR.y = dragEnd.y;
}
else{
TL.y = dragEnd.y;
BR.y = dragStart.y;
}
// prevents the zoom window from going off the canvas
if(TL.x < 0) TL.x = 0;
if(BR.x > chartCanvas.width-1) BR.x = chartCanvas.width-1;
if(TL.y < 0) TL.y = 0;
if(BR.y > chartCanvas.height-1) BR.y = chartCanvas.height-1;
// draw the actual zoom window
chartCanvas.graphics.clear();
chartCanvas.graphics.lineStyle(1, 0x000000, 0.25);
chartCanvas.graphics.beginFill(0xd4e3f0,0.5);
chartCanvas.graphics.drawRect(TL.x, TL.y, BR.x-TL.x, BR.y-TL.y);
chartCanvas.graphics.endFill();
}
}
// clears the drawing canvas and sets the new max/mins
private function finishDraw(e:MouseEvent):void{
zooming = false;
chartCanvas.graphics.clear();
// converts the drag coordinates into axis data points
var chartValStart:Array = chartCanvas.localToData(dragStart);
var chartValEnd:Array = chartCanvas.localToData(dragEnd);
// sets the new maximum and minimum for both axes
haxis.minimum = (chartValStart[0] < chartValEnd[0]) ? chartValStart[0] : chartValEnd[0];
haxis.maximum = (chartValStart[0] < chartValEnd[0]) ? chartValEnd[0] : chartValStart[0];
vaxis.minimum = (chartValStart[1] < chartValEnd[1]) ? chartValStart[1] : chartValEnd[1];
vaxis.maximum = (chartValStart[1] < chartValEnd[1]) ? chartValEnd[1] : chartValStart[1];
}
// resets the axis max/mins
private function resetZoom():void{
haxis.minimum = NaN;
haxis.maximum = NaN;
vaxis.minimum = NaN;
vaxis.maximum = NaN;
}
]]>
</fx:Script>
<s:VGroup>
<mx:Panel title="Line Chart">
<mx:LineChart id="chart1"
mouseDown="startDraw(event)"
mouseMove="showDraw(event)"
mouseUp="finishDraw(event)"
width="510">
<!-- zoom window is drawn here -->
<mx:annotationElements>
<mx:CartesianDataCanvas id="chartCanvas"/>
</mx:annotationElements>
<mx:horizontalAxis>
<mx:LinearAxis id="haxis"/>
</mx:horizontalAxis>
<mx:verticalAxis>
<mx:LinearAxis id="vaxis"/>
</mx:verticalAxis>
<mx:series>
<mx:LineSeries filterData="false" id="series1" xField="month" yField="profit"
displayName="Profit" dataProvider="{profits}"/>
</mx:series>
</mx:LineChart>
</mx:Panel>
<mx:Button label="Reset Zoom" click="resetZoom()" />
</s:VGroup>
</s:Application>
UPDATE:
Here's the solution, in case it's useful to others. I've added an if statement to check for null dragStart and dragEnd values, as discussed in the answer below. Also, I've removed the drop shadow that flex places by default on the line series, so a warning doesn't appear if the zoom area the user selects is too small.
<?xml version="1.0" encoding="utf-8"?>
<s:Application xmlns:fx="http://ns.adobe.com/mxml/2009"
xmlns:s="library://ns.adobe.com/flex/spark"
xmlns:mx="library://ns.adobe.com/flex/mx"
initialize="init()" width="600" height="520">
<fx:Script>
<![CDATA[
[Bindable]
private var profits:Array;
private var dragStart:Point;
private var dragEnd:Point;
private var zooming:Boolean;
// initializes the data provider with random data
private function init():void{
profits = new Array({month: 0, profit: 15});
for(var i:int=1; i<40; i++)
profits.push({month: i, profit: Math.round(Math.random()*25-10)+profits[i-1].profit});
}
// sets the start point of the zoom window
private function startDraw(e:MouseEvent):void{
zooming = true;
dragStart = new Point(series1.mouseX, series1.mouseY);
}
// draws the zoom window as your mouse moves
private function showDraw(e:MouseEvent):void{
if(zooming){
dragEnd = new Point(series1.mouseX, series1.mouseY);
// finds the top-left and bottom-right ponits of the zoom window
var TL:Point = new Point(); // top-left point
var BR:Point = new Point(); // bottom-right point
if(dragStart.x < dragEnd.x){
TL.x = dragStart.x;
BR.x = dragEnd.x;
}
else{
TL.x = dragEnd.x;
BR.x = dragStart.x;
}
if(dragStart.y < dragEnd.y){
TL.y = dragStart.y;
BR.y = dragEnd.y;
}
else{
TL.y = dragEnd.y;
BR.y = dragStart.y;
}
// prevents the zoom window from going off the canvas
if(TL.x < 0) TL.x = 0;
if(BR.x > chartCanvas.width-1) BR.x = chartCanvas.width-1;
if(TL.y < 0) TL.y = 0;
if(BR.y > chartCanvas.height-1) BR.y = chartCanvas.height-1;
// draw the actual zoom window
chartCanvas.graphics.clear();
chartCanvas.graphics.lineStyle(1, 0x000000, 0.25);
chartCanvas.graphics.beginFill(0xd4e3f0,0.5);
chartCanvas.graphics.drawRect(TL.x, TL.y, BR.x-TL.x, BR.y-TL.y);
chartCanvas.graphics.endFill();
}
}
// clears the drawing canvas and sets the new max/mins
private function finishDraw(e:MouseEvent):void{
zooming = false;
chartCanvas.graphics.clear();
if (dragStart && dragEnd) { // Solution to original posted quesion
// converts the drag coordinates into axis data points
var chartValStart:Array = chartCanvas.localToData(dragStart);
var chartValEnd:Array = chartCanvas.localToData(dragEnd);
// sets the new maximum and minimum for both axes
haxis.minimum = (chartValStart[0] < chartValEnd[0]) ? chartValStart[0] : chartValEnd[0];
haxis.maximum = (chartValStart[0] < chartValEnd[0]) ? chartValEnd[0] : chartValStart[0];
vaxis.minimum = (chartValStart[1] < chartValEnd[1]) ? chartValStart[1] : chartValEnd[1];
vaxis.maximum = (chartValStart[1] < chartValEnd[1]) ? chartValEnd[1] : chartValStart[1];
}
// reset values for next time
dragStart=null;
dragEnd=null;
}
// resets the axis max/mins
private function resetZoom():void{
haxis.minimum = NaN;
haxis.maximum = NaN;
vaxis.minimum = NaN;
vaxis.maximum = NaN;
}
]]>
</fx:Script>
<s:VGroup>
<mx:Panel title="Line Chart">
<mx:LineChart id="chart1"
mouseDown="startDraw(event)"
mouseMove="showDraw(event)"
mouseUp="finishDraw(event)"
width="510">
<!-- zoom window is drawn here -->
<mx:annotationElements>
<mx:CartesianDataCanvas id="chartCanvas"/>
</mx:annotationElements>
<mx:horizontalAxis>
<mx:LinearAxis id="haxis"/>
</mx:horizontalAxis>
<mx:verticalAxis>
<mx:LinearAxis id="vaxis"/>
</mx:verticalAxis>
<mx:series>
<mx:LineSeries filterData="false" id="series1" xField="month" yField="profit"
displayName="Profit" dataProvider="{profits}"/>
</mx:series>
<mx:seriesFilters>
<fx:Array/>
</mx:seriesFilters>
</mx:LineChart>
</mx:Panel>
<mx:Button label="Reset Zoom" click="resetZoom()" />
</s:VGroup>
</s:Application>
You are getting this error because the variable dragEnd is never set if the user just clicks the mouse. The easiest way to prevent this would be check for null values inside your finishDraw function:
private function finishDraw(e:MouseEvent):void
{
zooming = false;
chartCanvas.clear();
if(dragStart && dragEnd)
{
//your stuff here
//...
}
//reset values for next time
dragStart=null;
dragEnd=null;
}
And this should avoid any further 1009 Errors there. Beware I am getting some Warnings if I drag a tiny small zoom window and then release the mouse because Flash has a limit on how big a DisplayObject can be, so you should validate also the dimensions of the zoom window.
Hope this helps!
I'm trying to use a spark HSlider on a mobile device. I can get the thumb to move just fine in response to the accelerometer. But there are two problems:
1) the thumb flies right off the end of the screen when you tilt it in either direction. I've put in code below to try to stop this but it doesn't work.
2) I've got a little meter needle that is supposed to follow the position of the thumb, i.e. it points at it. This works fine when I drag the thumb -- when the change event is firing -- but not when the accelerometer is making it move. I think (not sure) that this might work if I could force the change event to fire?
Edit: The trace statements show that h.value isn't changing when it's the that accelerometer makes the thumb move. Also I removed a duplicate call to init().
Code:
<?xml version="1.0" encoding="utf-8"?>
<s:View xmlns:fx="http://ns.adobe.com/mxml/2009"
xmlns:s="library://ns.adobe.com/flex/spark" title="HomeView" creationComplete="init()">
<fx:Script>
<![CDATA[
import flash.sensors.Accelerometer;
import mx.events.FlexEvent;
public var a:Accelerometer = new Accelerometer();
private var xSpeed:Number = 0;
private var newX:Number = 0;
private function init():void
{
this.addEventListener( Event.ADDED_TO_STAGE , addedToStageHandler );
}
private function addedToStageHandler(e:Event):void
{
h.value = h.minimum;
needle.rotationZ = h.value + 180;
if(Accelerometer.isSupported)
{
a.addEventListener(AccelerometerEvent.UPDATE, readA);
stage.autoOrients = false;
}
}
private function readA(e:AccelerometerEvent):void
{
xSpeed -= e.accelerationX * 5;
newX = h.thumb.x + xSpeed;
if(h.thumb.x < 0 )
{
newX = 0;
xSpeed = 0;
}
else if (newX > stage.stageWidth - h.thumb.width) //(newX > stage.stageWidth - RADIUS)
{
newX = stage.stageWidth - h.thumb.width;
xSpeed = 0;
}
else
{
newX += xSpeed
}
h.thumb.x += xSpeed;
trace("thumb.x = " + h.thumb.x);
needle.rotationZ = h.value + 180;
trace("h.value = " + h.value);
trace("needle.rotationZ = " + needle.rotationZ);
}
protected function h_changeHandler(event:Event):void
{
// TODO Auto-generated method stub
needle.rotationZ = h.value + 180;
trace(needle.rotationZ);
}
]]>
</fx:Script>
<s:HSlider change="h_changeHandler(event)" minimum="-54" maximum="54" id="h" width="300" x="158" y="74"/>
<s:BorderContainer x="158" y="200" width="300" height="1">
<s:Rect id="needle" x="150" y="0" width="2" height="100" rotation="180">
<s:fill>
<s:SolidColor color="#FF0000"/>
</s:fill>
</s:Rect>
</s:BorderContainer>
</s:View>
Looks like you are moving the thumb's position directly. You should instead be changing the HSlider.value property and letting the component position the thumb accordingly.
The change event only fires if the value of the HSlider is changed via a user interaction (like a drag/click). Sounds like you might want to listen for the valueCommit event which will fire whenever the value of the HSlider changes whether that is programmatically or via user interaction.
How can I prevent CTRL+A from functioning with editable TextField()
The previous example only works with Flex Text and TextArea objects, this works with all flash.text.* objects.
<?xml version="1.0" encoding="utf-8"?>
<mx:Application xmlns:mx="http://www.adobe.com/2006/mxml" layout="absolute" creationComplete="init()">
<mx:Script>
<![CDATA[
import mx.core.UIComponent;
private var t:TextField;
private function init():void
{
t = new TextField();
t.height = 80;
t.width = 100;
t.type = TextFieldType.INPUT;
t.multiline = true;
var c:UIComponent = new UIComponent();
c.addChild( t );
foo.addChild( c );
addEventListener( KeyboardEvent.KEY_UP, edit );
addEventListener( KeyboardEvent.KEY_DOWN, edit );
}
private function edit( event:KeyboardEvent ):void
{
if( event.type == KeyboardEvent.KEY_DOWN && event.ctrlKey )
{
t.type = TextFieldType.DYNAMIC; // Dynamic texts cannot be edited. You might be able to remove this line.
t.selectable = false; // If selectable is false, then Ctrl-a won't do anything.
}
else
{
t.type = TextFieldType.INPUT;
t.selectable = true;
}
}
]]>
</mx:Script>
<mx:Canvas id="foo" height="90" width="110" backgroundColor="#FFFFFF" />
</mx:Application>
Not tested, but perhaps you could catch the selectAll event on the TextField and prevent it bubbling up, or clear the selection (not sure when the event is fired).
Use the setFocus function paired with a KeyboardEvent listener:
<xml version="1.0"?>
<!-- menus/SimpleMenuControl.mxml -->
<mx:Application xmlns:mx="http://www.adobe.com/2006/mxml" width="800" initialize="init()" >
<mx:TextInput id="nom"/>
<mx:Script>
<![CDATA[
private function init():void
{
addEventListener( KeyboardEvent.KEY_UP, edit );
addEventListener( KeyboardEvent.KEY_DOWN, edit );
}
private function edit( event:KeyboardEvent ):void
{
if( event.type == KeyboardEvent.KEY_DOWN && event.ctrlKey ) setFocus();
else nom.setFocus();
nom.selectionEndIndex = nom.selectionBeginIndex = nom.text.length;
}
]]>
</mx:Script>
</mx:Application>
The setFocus means that the Text object will no longer listen to any keyboard events.
I would not recommend using the enabled property as that will gray-out the textarea.