Windows Phone 8 - Expand/Collapse Map control - windows-phone-8

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();

Related

Match scroll bar scrolling to content scrolling - AS3

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);
}

put a "back" button in front on an embed map

I've integrate a map from MapQuest in my Adobe Air app (in AS3).
The map is taking all the screen as I want.
BUT, I'd like to add a "back" button in order to go back to the previous menu.
Here's my code :
var NoumeaNord:TileMap = new TileMap("KEYcode");
//set the size of the map
NoumeaNord.size = new Size(800, 533);
//add the map to the sprite.
addChild(NoumeaNord);
NoumeaNord.addControl(new SMLargeZoomControl());
NoumeaNord.addControl(new MouseWheelZoomControl());
NoumeaNord.addShape(new Poi(new LatLng(-22.2758000,166.4580000)));
NoumeaNord.setCenter(new LatLng(-22.2758000,166.4580000),15);
function addBackBtn():void{
var back:MovieClip;
back = new backBtn
addChild(back);
back.x = 0;
back.y = 400;
setChildIndex(back,0);
}
Don't know why but the BackBtn won't be in front of the map !
I've tried with setChildIndex(back,-1); but it makes an error : "RangeError: Error #2006: index is off limit".
Any idea ?
In this situation I use holders like this:
private var bgHolder:Sprite;
private var contentHolder:Sprite;
private var menuHolder:Sprite;
Somewhere in the constructor I run a method that will set my holders. For example:
function setHolders()
{
bgHolder = new Sprite();
addChild(bgHolder);
contentHolder = new Sprite();
addChild(contentHolder);
menuHolder = new Sprite();
addChild(menuHolder);
}
Then I just add my content to the desired holder in any order between destination holder and I can always be sure to add my content to the correct "layer";
So just add your back button to the menuHolder:
menuHolder.addChild(back);
And don't worry about adding it below the map or out of index, because the map, in this case, would be added to the contentHolder!
Hope that helps!
that's because you add your back button first
just make sure that you first add the map then add the button

Windows Phone 8: How can I make a map's pushpin textbox to stay on top of any other elements on the map?

I have a map with 100 pushpins. Every time I tap a pushpin, a textbox with description is opened near that pushpin (only 1 textbox can be opened at a time, when you tap a pushpin, the previous opened textbox is closed first), but sometimes the textbox is not on top of other pushpins, other pushpins appear above the textbox, making it hard to read the description. I've tried using Canvas and Canvas.ZIndex, but nothing worked properly.
I had a similar issue, and I solved it my removing and adding the object again whenever it was tapped.
MapLayer theLayer = new MapLayer();
MapOverlay theOverlay = new MapOverlay()
{
GeoCoordinate = new GeoCoordinate(lat, lng)
};
var pp = new StackPanel { Background = new SolidColorBrush(Colors.Black), Orientation = System.Windows.Controls.Orientation.Vertical };
var img = new Image()
{
Source = new BitmapImage(new Uri(url, UriKind.Absolute)),
Width = 50,
Height = 50
};
pp.Children.Add(img);
img.Tap += (object emitter, System.Windows.Input.GestureEventArgs e) => {
theLayer.Remove(theOverlay);
theLayer.Add(theOverlay);
};
theOverlay.Content = pp;
theLayer.Add(theOverlay);
Hope this helps!

ViewNavigator display top of application

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);
}

Actionscript Image with clickable spots

Can any one help in suggesting a solution for the following:
i have a large image, consider it as a map, i want to put this image in a viewer that is smaller than the image and i have to be able to scroll the image by clicking and dragging it.
and i want to put in this image a clickable spots in a specified x and y coordinated, and be able to click the spots.
when clicking any spot in the image, the image will be changed with a new spots.. and so on..
can you help in suggesting what is the best object to load the image in and be able to do all the mentioned points.
Thanks in advance.
This is easier than you think. You have a few goals to consider:
"i want to put this image in a viewer that is smaller than the image": You dont need anything special to do this. The concept of this is simply that you have a mask overlay where you want the large image visible.
var viewer:Sprite = new Sprite; //200x200
var imageMask:Sprite = new Sprite; //200x200
var imageContainer:Sprite = new Sprite; //400x500
imageContainer.mask = imageMask;
viewer.addChild(imageContainer);
//this will allow you to visibly see only 200x200 of the
//imageContainer at any time
"i have to be able to scroll the image by clicking and dragging it": This is a little more logic as the imageContainer will have to move in the -(negative) direction of the mouse. Add some listeners to check for mouse actions, and drag as required.
var allowDrag:Boolean = false;
imageContainer.addEventListener(MouseEvent.MOUSE_DOWN, onMouseDown);
imageContainer.addEventListener(MouseEvent.MOUSE_UP, onMouseUp);
imageContainer.addEventListener(MouseEvent.MOUSE_MOVE, onMouseMove);
function onMouseDown(e:Event):void{
allowDrag = true;
}
function onMouseUp(e:Event):void{
allowDrag = false;
}
function onMouseMove(e:Event):void{
//return if not dragging
if(!allowDrag) return;
//move the imageContainer in a -(negative) direction of the mouse
//use an index relative to the size of the viewer and imageContainer
var speed:Number = 0.5;
imageContainer.x -= (viewer.width/imageContainer.width)*speed;
imageContainer.y -= (viewer.height/imageContainer.height)*speed;
//clean the positions so the image remains within the viewer
if(imageContainer.x > 0) imageContainer.x = 0;
if(imageContainer.x < -viewer.width) imageContainer.x = -viewer.width;
if(imageContainer.y > 0) imageContainer.y = 0;
if(imageContainer.y < -viewer.height) imageContainer.y = -viewer.height;
}
"i want to put in this image a clickable spots in a specified x and y coordinated, and be able to click the spots": This also requires a little more thinking. In this case what you want to do is create [hotspots] on the image that are clickable, when clicked = do actions.
//USAGE
//define the click area coords
var clickCoords:Rectangle = new Rectangle();
clickCoords.x = 10; //starts at x 10
clickCoords.y = 10; //starts at y 10
clickCoords.width = 100; //100 wide
clickCoords.height = 100; //100 tall
//add the click listener
var clickArea:Sprite = hotSpot(imageContainer,clickCoords);
clickArea.addEventListener(MouseEvent.CLICK, onHotSoptClick);
//hot spot factory
function hotSpot(target:Sprite,coords:Rectangle):Sprite{
//create the hotspot
var hs:Sprite = new Sprite;
hs.graphics.beginFill(0,0);
hs.graphics.drawRect(0,0,coords.width,coords.height);
hs.graphics.endFill();
//add the hotspot to the target
hs.x = coords.x;
hs.y = coords.y;
target.addChild(hs);
}
function onHotSoptClick(e:MouseEvent):void{
//do something
}
IMPORTANT:
You may want to keep a list of hot spots you create so you can do garbage cleanup, and you plan on dynamically generating hotspots per image... then YOU MUST keep an active list of hot spots and remove when not in use.
You can catch the events MouseDown, MouseUp, MouseMove, MouseOut, on your viewing window, this way you can control exactly what do you want to do.
Here is the pseudo-code:
reset()
{
isDown=false;
downPointX=0;
downPointY=0;
distanceX=0;
distanceY=0;
}
onMouseDown()
{
isDown=true;
downPointX=mouseX;
downPointY=mouseY;
}
onMouseUp()
{
if(distanceX+distanceY==0 and isDown)
click(downPointX,downPointY);
reset();
}
onMouseMove()
{
if isDown then
distanceX=mouseX-downPointX;
distanceY=mouseY-downPointY;
drag(distanceX,distanceY);
endif;
}
onMouseOut()
{
reset();
}
drag(distanceX,distanceY)
{
change your map coordinates
}
click(downPointX,downPointY)
{
if(inSpot(downPointX,downPointY)==true)
changeMap();
endif;
}
changeMap()
{
change your maps and spots
}
avoid implementing any event for your spots sprites or you can get unexpected results.
You can check these for more information
http://help.adobe.com/en_US/FlashPlatform/reference/actionscript/3/flash/display/Sprite.html#eventSummary