I have a s:ViewNavigator component inside of s:TabbedViewNavigatorApplication. Currently it shows up at the bottom. How can I make it display at the top of the app just below action bar? I looke though all related skins in the flex sdk but I found nothing relating to position.
P.S. this is a mobile app in Flash Builder 4.6
In s:TabbedViewNavigatorApplication component:
var nav:ViewNavigator = new ViewNavigator();
nav.label = "Newest";
nav.firstView = HomeView;
nav.firstViewData = "recent";
nav.percentWidth = 100;
nav.percentHeight = 100;
this.addElement(nav);
nav.titleContent = [];
Changes in s:TabbedViewNavigatorApplication Skin:
(Swapped if statements)
(changed y position of tabBar by 60)
if (contentGroup.includeInLayout)
{
var contentGroupHeight:Number = (_isOverlay) ? unscaledHeight : Math.max(unscaledHeight - tabBarHeight, 0);
contentGroup.setLayoutBoundsSize(unscaledWidth, contentGroupHeight);
contentGroup.setLayoutBoundsPosition(0, 0);
}
if (tabBar.includeInLayout)
{
tabBarHeight = Math.min(tabBar.getPreferredBoundsHeight(), unscaledHeight);
tabBar.setLayoutBoundsSize(unscaledWidth, tabBarHeight);
tabBar.setLayoutBoundsPosition(0, 60); //unscaledHeight - tabBarHeight
tabBarHeight = tabBar.getLayoutBoundsHeight();
// backgroundAlpha is not a declared style on ButtonBar
// TabbedViewNavigatorButtonBarSkin implements for overlay support
var backgroundAlpha:Number = (_isOverlay) ? 0.75 : 1;
tabBar.setStyle("backgroundAlpha", backgroundAlpha);
}
Changes in ViewNavigator Skin:
(took away (-) contentGroupPosition from contentGroupHeight)
(multiplied contentGroupPosition by 2 so that other elements in the app would not be below the buttonBar. Action bar and buttonBar will have to be same size for this to work properly and There is no way to access the height of the button bar itself from vieNavigator skin as far as I can tell)
if (contentGroup.includeInLayout)
{
// If the hostComponent is in overlay mode, the contentGroup extends
// the entire bounds of the navigator and the alpha for the action
// bar changes
// If this changes, also update validateEstimatedSizesOfChild
var contentGroupHeight:Number = (_isOverlay) ? unscaledHeight : Math.max(unscaledHeight - actionBarHeight, 0);
var contentGroupPosition:Number = (_isOverlay) ? 0 : actionBarHeight;
contentGroup.setLayoutBoundsSize(unscaledWidth, contentGroupHeight-contentGroupPosition);
contentGroup.setLayoutBoundsPosition(0, contentGroupPosition*2);
}
Related
I am developing a desktop application. I am using ActionScript 3 via Adobe Animate CC. I designed the application, animated the GUI, and started coding. The main functions were successfully coded well, but I have added some additional features which are out of the scope of the application's original goals. All of this just to link the application to a website and some other information, which made me totally crazy because I've spent too much time with very simple if statement LOGIC!
Anyway, I created a menu with three MovieClip buttons. These menu button clicks affect one MovieClip that has a white background that moves with each click. I need there to be one background to show the beautiful effect of easeIn and easeOut animation tweens when clicking each button.
About clicked
Gallery clicked
Contact clicked
To make it easy to understand, I recreated the code with a very simple ball and 3 buttons. If you click first button, the ball moves to the right above the 2nd button. Then the first button should be unclickable, unless another button is clicked. If the second button is clicked, then the ball would move to the right above the third button. Then the second button should be unclickable also, unless another button is clicked. The same thing goes for the third button.
Now, if the first button is clicked again, the animation of the white background should not start from the default position when starting up the application!
It should animated back from its current position to the default position... and so on...
I replaced the white background with a ball for simplicity
This is very easy but I lost it with the eventListeners, eventHandlers, and if statements! :/
I also made this table which studies the cases:
I know my coding technique is not smart enough, but that is because I HATE using classes, packages, project folders... etc..
Even if the code runs too long & repeats, It would be better for me for simplicity, as programming is not my day-job!
Please, any help and quick response would be highly appreciated!
Code:
import flash.ui.Mouse;
import flash.events.MouseEvent;
one.addEventListener(MouseEvent.CLICK, moveToSecondPos);
two.addEventListener(MouseEvent.CLICK, moveToThirdPos);
//three.addEventListener(MouseEvent.CLICK, moveToFirstPos);
var buttonState:Boolean;
one.buttonState = 0;
two.buttonState = 0;
//three.buttonState = 0;
function moveToSecondPos(event:MouseEvent){
if(one.buttonState == 0){
theBall.gotoAndPlay("go1");
one.buttonState = 1;
}
else if(two.buttonState == 1){
theBall.gotoAndPlay("backToOne");
// two.buttonState = 0;
// one.buttonState = 1;
}
else{
//disable About Button
one.removeEventListener(MouseEvent.CLICK, moveToSecondPos);
}
}
function moveToThirdPos(event:MouseEvent){
if((two.buttonState == 0)&&(one.buttonState == 0)){
theBall.gotoAndPlay("goProducts");
two.buttonState = 1;
}
else if(one.buttonState == 1){
theBall.gotoAndPlay("go2");
// two.buttonState = 1;
// one.buttonState = 1;
}
else{
two.removeEventListener(MouseEvent.CLICK, moveToThirdPos);
}
}
//function moveToFirstPos(event:MouseEvent){
// if(three.buttonState == 0){
// theBall.gotoAndPlay("go3");
// three.buttonState = 1;
// }
// else{
// three.removeEventListener(MouseEvent.CLICK, moveToFirstPos);
// }
//}
First, you mentioned you wanted to have the
white background should not start from the default position when starting up the application
For that, you'll need a ShareObject or comparable method of saving and loading data.
Secondly, It appears you may be trying to do some of this with Scenes and Timeline' frames. I highly encourage you not to pursue that.
Below is a solution to the problems you mentioned. Notice that without your project, I had to recreate the scene. You can copy & paste the solution into a new scene and it will compile.
To prevent buttons from being clicked, you can remove the event listener by calling myButton.removeEventListener("click"). Alternatively, you can simply stop the object from responding to mouse events by setting its mouseEnabled property to false.
To animate the box smoothly, you can use the built-in Tween class. Alternatively, I'd direct you Greensock's TweenLite.
Because you appeared to want a series of labels to appear depending on the button clicked, I created an array which stores data specific to each button (btns:Array). By using an organized structure, it's easier to write re-useable code which doesn't depend on explicit functions. Note that there's only one btnListener() function which works for all of your buttons.
Solution
import flash.display.Sprite;
import flash.events.Event;
import flash.text.TextField;
import fl.motion.Color;
import fl.transitions.*;
import fl.transitions.easing.*;
// the list of buttons we want, along with the descriptions for each
var btns:Object = [
{
"label":"About",
"desc":"Learn more about us",
"btn":null
},
{
"label":"Gallery",
"desc":"See our photos",
"btn":null
},
{
"label":"Contact",
"desc":"Write, call, or message",
"btn":null
}
];
var nav:Sprite; // our navigation bar of buttons
var whiteBox:Sprite; // the "ball" with the content text
// where we save the coordinates of the whiteBox between loads
var settings:Object = SharedObject.getLocal("foo");
init();
function init():void {
// Create our navigation of three buttons
// our container for buttons
nav = new Sprite();
// reference to the last button
var last:Sprite = null;
// loop through the list of buttons
for each (var entry:Object in btns) {
// For each button, create a new button
var btn:Sprite = createButton(entry.label);
entry.btn = btn;
nav.addChild(btn);
// If there was a previous button, place this beside it.
if (last != null) {
btn.x = last.x + last.width + 1;
}
last = btn;
}
// Place the nav onscreen
addChild(nav);
nav.x = stage.stageWidth/2 - nav.width/2;
nav.y = stage.stageHeight - nav.height*2;
// Create the whitebox
whiteBox = new Sprite();
whiteBox.graphics.beginFill(0xFAFAFA);
whiteBox.graphics.drawRect(0, 0, 150, 50);
whiteBox.graphics.endFill();
var txt:TextField = new TextField();
txt.name = "txt";
txt.width = 150;
whiteBox.addChild(txt);
nav.addChild(whiteBox);
// Load the settings from the last run of the program.
if (settings.data.hasOwnProperty("x")) {
whiteBox.x = settings.data.x;
whiteBox.y = settings.data.y;
}
}
function createButton(label:String):Sprite {
// Creates a simple button
// Create the label
var txt:TextField = new TextField();
txt.text = label;
// Create the background
var btn:Sprite = new Sprite();
btn.graphics.beginFill(0xA1A1A1);
btn.graphics.drawRect(0, 0, txt.width + 60, txt.height + 30);
btn.graphics.endFill();
btn.addChild(txt);
btn.name = label;
txt.x = 30;
txt.y = 15;
// Hookup events
btn.addEventListener("mouseOver", btnListener);
btn.addEventListener("mouseOut", btnListener);
btn.addEventListener("click", btnListener);
return btn;
}
function btnListener(e:Event):void {
var btn:Sprite = e.currentTarget as Sprite;
var c:Color = new Color();
var entry:Object;
// Find our button in the list.
for each (entry in btns) {
if (entry.label == btn.name) {
break;
}
}
switch (e.type) {
case "mouseOver":
if (btn.mouseEnabled) {
c.setTint(0x0096ff, 0.5);
btn.transform.colorTransform = c;
}
break;
case "mouseOut":
if (btn.mouseEnabled) {
c.setTint(0x00, 0);
btn.transform.colorTransform = c;
}
break;
case "click":
// Set the text, and position of our whitebox
whiteBox.getChildAt(0)["text"] = entry.desc;
whiteBox.y = -whiteBox.height;
var tween:Tween = new Tween(whiteBox, "x", Regular.easeOut, whiteBox.x, btn.x, 0.35, true);
tween.addEventListener("motionFinish", saveState);
for each (var v:Object in btns) {
if (v.btn == btn) {
// make our button unclickable
btn.mouseEnabled = false;
c.setTint(0xFFFFFF, 0.5);
btn.transform.colorTransform = c;
} else {
// Make the other buttons clickable;
v.btn.mouseEnabled = true;
c.setTint(0x00, 0);
v.btn.transform.colorTransform = c;
}
}
break;
}
}
function saveState(e:Event):void {
settings.data.x = whiteBox.x;
settings.data.y = whiteBox.y;
settings.flush();
}
I apologize in advanced if this is a little confusing to understand, but I'll do the best I can to explain it.
Basically I've created a scrolling page that allows you have content that extends beyond the stage and swipe up or down to view it.
In this case, a movieclip the size of the stage masks an object under it (taller than the stage) and swiping up/down affects the objects y position, revealing more of it.
The part I'm stuck on is getting the "scroll bar" object on the side to match the position of the top and bottom of the overflow object;
Since the scroll bar has to stay on the stage, when the bottom of the object is reached the scroll bar ends up being off stage because they move at the same speed, so in this case, the scroll bar would need to move slower in order to be at the bottom when the other object is.
The way it's coded right now I can only seem to match either the top with
scrollbar.y = (e.target.y - barY);
the opposite is achieved by
scrollbar.y = (e.target.y + barY);
but I can't seem to achieve both simultaneously.
I'm coding this in AS3 (flash CC) with mobile being my desired platform to publish, and I'll attach my code as well as some screenshots below.
var ease:int = 6;
var targY:int = dragMe.y;
var barY:int = scrollbar.y;
var drag:Boolean = false;
var pos:Number = 0;
var minY:Number = 0 + dragMe.height / 2; // how low the top can go
var maxY:Number = stage.stageHeight - dragMe.height / 2; // how high the bottom can go
var barMax:Number = 0 + scrollbar.height; // how high the bar can go
var barMin:Number = stage.stageHeight - scrollbar.height; // how low the bar can go
dragMe.mask = mcMask;
mcMask.visible = false;
dragMe.addEventListener(Event.ENTER_FRAME, dragging);
dragMe.addEventListener(MouseEvent.MOUSE_DOWN, mouseDown);
stage.addEventListener(MouseEvent.MOUSE_UP, mouseUp);
function dragging(e:Event):void {
if (drag) {
targY = mouseY + pos;
}
// restrict scrolling to stage
targY = Math.min(targY, minY); // how low the top can go
targY = Math.max(targY, maxY); // how high the bottom can go
barY = Math.min(barY, barMin); // how low the bar can go
barY = Math.max(barY, barMax); // how high the bar can go
// Movement of the text
e.target.y += (targY - e.currentTarget.y) / ease;
// Movement of the bar
scrollbar.y = (e.target.y - barY);
}
function mouseUp(e:MouseEvent):void {
drag = false;
}
function mouseDown(e:MouseEvent):void {
pos = e.currentTarget.y - mouseY;
drag = true;
}
Any help would be greatly appreciated!
Grey = stage
Black = outside stage
Good:
http://i.stack.imgur.com/qvAoz.png
Bad:
http://i.stack.imgur.com/FvHIm.png
The part I'm stuck on is getting the "scroll bar" object on the side to match the position of the top and bottom of the overflow object;
Well, there's no need to get the scrollbar object to match the position of the overflow object, you should bind it to the coordinates of the masker object. Btw I usually gather all UI elements right in the IDE, and then parse them. For example, if I need to add a scroll bar, I need only three elements:
container - all content will be added right in it;
masker
Scrollbar movieclip, which consists of two MCs: bg and a bar.
In this case I don't need to place this elements in code, but just call in a function to handle this elements. The code itself is simple enough. You know the height of the masker, the height and the position of the container, and also the height of the scrollbar background and current position of the bar. All you need is to handle the events and react accordingly.
E.g. in case you are scrolling using your scrollbar, just translate current coordinates of the bar and update container's position. If you are dragging your content, update your bar's position accordingly.
Also, there's a better way to drag the bar:
private static function drag(e:MouseEvent):void
{
var myBounds:Rectangle = new Rectangle(bar.x, 0, 0, barBg.height-bar.height+8);
bar.startDrag(false, myBounds);
bar.addEventListener(Event.ENTER_FRAME, update);
}
static private function stopdrag(e:*):void
{
bar.stopDrag();
update(null);
bar.removeEventListener(Event.ENTER_FRAME, update);
}
static private function update(e:Event):void
{
var scroll_run_length:Number = barBg.height - bar.height + 8;
var modificator:Number = (content.height-masker.height) / scroll_run_length;
content.y = startContY -(bar.y * modificator);
}
This is my page, which shows the current location of the user:
In the bottom-left of the map control, there is an expand button. On click, the map control should expand to full-screen and the button should change to the collapse button. This would be easy but I want to animate the expand and collapse process. How can I do this?
For an overview of animation properties of XAML elements, look here...
http://windowsphonegeek.com/articles/wp7-animations-in-depthndash-overview-and-getting-started
For a Map, here is some C# code to animate its 'Height' property...
// assumes Map element is called 'map'
double height = map.Height;
double from, to;
// animate from 150 to 800, or vice versa
if (height == 150)
{
from = 150;
to = 800;
}
else
{
from = 800;
to = 150;
}
Storyboard sb = new Storyboard();
DoubleAnimation fillHeightAnimation = new DoubleAnimation();
fillHeightAnimation.From = from;
fillHeightAnimation.To = to;
fillHeightAnimation.Duration = new Duration(TimeSpan.FromSeconds(0.3));
Storyboard.SetTarget(fillHeightAnimation, map);
Storyboard.SetTargetProperty(fillHeightAnimation, new PropertyPath("Height"));
sb.Children.Add(fillHeightAnimation);
sb.Begin();
I basically have 5 images in library, which I've given af linkage name as so:
head_image0, head_image1, head_image2 etc...
On Stage I have two containers which I will be loading my images into.
- pictureContainer1_mc
- pictureContainer2_mc
Pr default I have already loaded an image into container1.
What will happen is that, when I click menu button2, then head_image2 will load into container2 and fade up from alpha 0 -> alpha 1 - giving a transition between the two images.
After the image has alpha'ed into container2, then I run a function to swap containers, so that container1 will hold the image currently loaded and container2 will be ready to load another picture upon menu button click.
This looks like the following:
//Function receiving an ID from the button click, which we can use to load the connected image
function headerFadeIn(receivedNumber:Number):void
{
//Getting container2 rdy to tween from 0 -> 1
pictureContainer2_mc.alpha = 0;
//Preparing image string - linkagename
var tempName:String = "header_image" + receivedNumber;
//Loading the image into container 1
var headImage:Bitmap = new Bitmap();
var classDefinition:Class = getDefinitionByName(tempName) as Class;
var img:BitmapData = new classDefinition(1,1);
headImage.bitmapData = img;
pictureContainer2_mc.addChild(headImage);
//Tweening currecntly added image from 0 -> 1
//When tween is done call function onFinishTween to swap containers
TweenLite.to(pictureContainer2_mc,1,{alpha:1, ease:menuEasing, onComplete:onFinishTween});
function onFinishTween():void
{
//Setting currently loaded image in container 1
var headImage:Bitmap = new Bitmap();
var classDefinition:Class = getDefinitionByName(tempName) as Class;
var img:BitmapData = new classDefinition(1,1);
headImage.bitmapData = img;
pictureContainer1_mc.addChild(headImage);
pictureContainer2_mc.alpha = 0;
}
}
This works for me. HOWEVER I'm experiencing some "lagging" issue. When I've clicked back and forward menu buttons to change images like 15 times, then the tweening begins to "lag" and wont happen in a smooth transition.
Any calls on this?
Could I do this in a smarter way?
I would suggest to create images only once, and reuse them. Also, after the tween, i would remove the old image and save it for the next time you need that image. Also, when you swap the image from container 2 into container 1, don't create a new one, just move the image from one container to the other.
Something like this, code is not tested, just to give you an idea.
var instances:Object = {};
var currentImage:String;
//Function receiving an ID from the button click, which we can use to load the connected image
function headerFadeIn(receivedNumber:Number):void
{
//Getting container2 rdy to tween from 0 -> 1
pictureContainer2_mc.alpha = 0;
//Preparing image string - linkagename
currentImage = "header_image" + receivedNumber;
var headImage:Bitmap = _getImage(currentImage);
pictureContainer2_mc.addChild(headImage);
//Tweening currecntly added image from 0 -> 1
//When tween is done call function onFinishTween to swap containers
TweenLite.to(pictureContainer2_mc,1,{alpha:1, ease:menuEasing, onComplete:onFinishTween});
function onFinishTween():void
{
//Setting currently loaded image in container 1
//cast returned DisplayObject into a Bitmap.
var headImage:Bitmap = pictureConainer2_mc.getChildByName(currentImage) as Bitmap;
pictureContainer1_mc.addChild(headImage);
pictureContainer2_mc.alpha = 0;
}
}
function _getImage(id:String):Bitmap{
if( instances[id] ) return instances[id];
//Loading the image into container 1
var headImage:Bitmap = new Bitmap();
var classDefinition:Class = getDefinitionByName(tempName) as Class;
var img:BitmapData = new classDefinition(1,1);
headImage.bitmapData = img;
instances[id] = headImage;
return headImage;
}
This is in conjunction to the question posed here:
JTabbedPane: Components before and after the tabs themselves
I want to attach a mouse listener that allows for dragging the constructed google chrome-like frame. Starting out, the initial dragging code is rather easy, and the mouse-dragging code at this Kirill-post can be used pretty much directly. I'd only want this behaviour if the user clicks and drags on the "title bar" of the frame, that is, the area where the tabs (the stick-uppers) reside. This is also easy - just change the dragging code to only accept clicks in the upper area of the JTabbedPane, the part that contains the tabs.
However, I want to reduce the grabbable area further, and only allow click-and-drag-frame in the area NOT occupied by the tabs (the stick-uppers - anyone have a better name for this GUI element?) - again quite like Google Chrome (Chrome also adds a bar above the tabs when in windowed mode, to easier get hold of the frame if many tabs are active. But Chrome do this perfect: It is possible to grab the window in the tabbed part that doesn't have tabs, and even in the small v's inbetween the tabs!)
What I'd effectively want to do, is to be able to attach the mouse listeners to the background of the GUI for the tabs - but how to accomplish something like this?
After looking at the solutions for this question (draggable tabs in Swing), I found that the actual TabbedPaneUI has some methods that can fix both problems: Only drag window in tab area, which turned out to be the hardest part, and not drag when above the tabs themselves. The relevant code follows, in the two sections marked with "// ::". The code is adapted from the question-mentioned Kirill-code. The code doesn't handle other cases than when the tabs are at top - which makes sense when considering what I want to do.
// mouse listener for dragging the host window
MouseAdapter adapter = new MouseAdapter() {
int lastX;
int lastY;
boolean _dragInitiated;
#Override
public void mousePressed(MouseEvent e) {
TabbedPaneUI ui = _windowTabs.getUI();
// :: Won't drag if we're positioned above a tab in tab area
if (ui.tabForCoordinate(_windowTabs, e.getX(), e.getY()) != -1) {
_dragInitiated = false;
return;
}
// :: Won't drag if we're below the tab area
int maxY = 0;
for (int i = 0; i < _windowTabs.getTabCount(); i++) {
Rectangle bounds = ui.getTabBounds(_windowTabs, i);
int y = bounds.y + bounds.height;
if (y > maxY) {
maxY = y;
}
}
_dragInitiated = true;
if (maxY > 0) {
if (e.getY() > maxY) {
_dragInitiated = false;
}
}
Point eventLocationOnScreen = e.getLocationOnScreen();
if (eventLocationOnScreen == null) {
Component source = (Component) e.getSource();
eventLocationOnScreen = new Point(e.getX() + source.getLocationOnScreen().x, e.getY()
+ source.getLocationOnScreen().y);
}
lastX = eventLocationOnScreen.x;
lastY = eventLocationOnScreen.y;
}
#Override
public void mouseDragged(MouseEvent e) {
if (!_dragInitiated) {
return;
}
Point eventLocationOnScreen = e.getLocationOnScreen();
if (eventLocationOnScreen == null) {
Component source = (Component) e.getSource();
eventLocationOnScreen = new Point(e.getX() + source.getLocationOnScreen().x, e.getY()
+ source.getLocationOnScreen().y);
}
int dx = eventLocationOnScreen.x - lastX;
int dy = eventLocationOnScreen.y - lastY;
Window win = POTabbedFrame.this;
Point loc = win.getLocation();
win.setLocation(loc.x + dx, loc.y + dy);
lastX = eventLocationOnScreen.x;
lastY = eventLocationOnScreen.y;
}
};
_windowTabs.addMouseListener(adapter);
_windowTabs.addMouseMotionListener(adapter);