scroll specific child into view - flex4

I have following code, which make scroll to specific child. It works fine except when scroll to the last child. It works OK but it's not my expect behavior.The PROBLEM is when scroll to last child, I want the child to show on the top of viewport just like other ones. Some diagrams should help this a little better than words ever could.
Summary the issue:
1.when scroll to last child, is it possible to position it at (0,0) in the viewport?
2.I have 6 children, each one has 200 height. The contentHeight is 1200. when the verticalScrollPosition is 0, I invoke the viewport.getVerticalScrollPositionDelta(NavigationUnit.END), the returned value
is 900. So how the 900 is calculated?
following is the code:
<?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"
creationComplete="init()"
>
<fx:Script>
<![CDATA[
import components.MockUI;
import mx.collections.ArrayList;
import spark.core.NavigationUnit;
private function init():void {
createElements();
}
private function createElements():void {
for (var i:int = 0; i<6; i++) {
var ui:MockUI = new MockUI();
ui.text = "I'm " + i + " !";
ui.width = 300;
ui.height = 200;
viewport.addElement(ui);
}
}
//scroll to diffrent child, take attetion to the scrollRect
private function scrollToChild(index:int):void {
var delta:Number = returnSumHeight(index);
viewport.verticalScrollPosition = delta;
var scrollRect:Rectangle = viewport.scrollRect;
trace("viewport contentHeight: ", viewport.contentHeight);
trace("vp: ", viewport.verticalScrollPosition);
trace("scrollRect: ", scrollRect);
}
private function handleScroll(unit:uint):void {
trace("unit: ", unit);
var delta:Number = viewport.getVerticalScrollPositionDelta(unit);
trace("delta:", delta);
}
private function minus():void {
viewport.verticalScrollPosition -= 10;
trace("vp: ", viewport.verticalScrollPosition);
}
//return the sum height of children before index item
private function returnSumHeight(index:int):Number {
var sumHeight:Number = 0;
for(var i:int=0; i<index; i++) {
sumHeight += viewport.getElementAt(i).height;
}
return sumHeight;
}
]]>
</fx:Script>
<s:layout>
<s:VerticalLayout horizontalAlign="center" paddingTop="100"/>
</s:layout>
<s:HGroup>
<s:Label text="Select Child: "/>
<s:ComboBox id="comboBox"
dataProvider="{new ArrayList([0,1,2,3,4,5])}"
selectedIndex="0"
change="scrollToChild(comboBox.selectedIndex)"/>
</s:HGroup>
<s:Scroller>
<s:VGroup id="viewport" width="350" height="300" gap="0">
</s:VGroup>
</s:Scroller>
<s:Button label="MINUS" click="minus()"/>
<s:Button label="UP" click="handleScroll(NavigationUnit.UP)"/>
<s:Button label="DOWN" click="handleScroll(NavigationUnit.DOWN)"/>
<s:Button label="HOME" click="handleScroll(NavigationUnit.HOME)"/>
<s:Button label="END" click="handleScroll(NavigationUnit.END)"/>
</s:Application>
MOCKUI:
package components {
public class MockUI extends UIComponent {
private var label:Label;
private var _text:String;
private var textChanged:Boolean = false;
public function set text(value:String):void {
if(value == _text) {
return;
}
_text = value;
textChanged = true;
invalidateProperties();
}
public function get text():String {
return _text;
}
override protected function createChildren():void {
super.createChildren();
label = new Label();
addChild(label);
}
override protected function commitProperties():void {
super.commitProperties();
if(textChanged) {
textChanged = false;
label.text = _text;
}
}
override protected function updateDisplayList(unscaledWidth:Number, unscaledHeight:Number):void {
super.updateDisplayList(unscaledWidth, unscaledHeight);
label.width = unscaledWidth/2.0;
label.height = unscaledHeight/2.0;
label.x = unscaledWidth/2.0;
label.y = unscaledHeight/2.0;
graphics.clear();
graphics.lineStyle(2, 0xffff00);
graphics.drawRect(0, 0, unscaledWidth, unscaledHeight);
graphics.beginFill(0xff0000, 1);
graphics.drawRect(0,0,unscaledWidth, unscaledHeight);
graphics.endFill();
}
}
}

When scrolling to last child, is it possible to position it at (0,0) in the viewport?
No, it's not possible with the default code in the Flex SDK and due to the size of your viewport/elements. The only way to make that happen with the default code is to make your viewport the same size as one item (and all items have the same height).
The Scroller won't allow you to scroll past the end of the content (except as an effect in mobile apps). You could extend the Scroller class to allow this.
I have 6 children, each one has 200 height. The contentHeight is 1200. when the verticalScrollPosition is 0, I invoke the viewport.getVerticalScrollPositionDelta(NavigationUnit.END), the returned value is 900. So how the 900 is calculated?
The height of each child is 200, but the height of the container they are in is 300 pixels. Because the scroller won't allow you to scroll past the end of the content, it restricts the max value of scroll position to: contentHeight - viewportHeight (1200 - 300 = 900).
Think of the viewport as something that moves (in this case) vertically over the content. The content is 1200 pixels tall, but since the viewport is 300 pixels, we never need to set the viewport's Y position above 900 pixels to see the end of the content.

Related

How do I force a square layout on my AbsoluteLayout?

Anyone know why the following code doesn't execute the OnMeasure() in my Forms app?? I'm basically trying to force an AbsoluteLayout to have the same height/width to show up as a Square:
public class AbsoluteSquareLayout : AbsoluteLayout
{
protected override SizeRequest OnMeasure(double widthConstraint, double heightConstraint)
{
var size = Math.Min(widthConstraint, heightConstraint);
return base.OnMeasure(size, size);
}
}
Overriding size constraints in OnMeasure call doesn't guarantee final size - as the final Layout pass from parent layout will override this value. XF article here talks about this in detail.
In order to be able to achieve a square layout - you will have to update the parent layout to consider those constraints, and ensure it is passed in layout pass.
For example you can extend AbsoluteLayout to dynamically calculate the size constraints for children. This custom layout treats all children as squares by default. In order to override that behavior for a particular child - you can set attached property SquareLayout.IsSquare as false.
public class SquareLayout : AbsoluteLayout
{
public static readonly BindableProperty IsSquareProperty =
BindableProperty.CreateAttached("IsSquare",
typeof(bool),
typeof(SquareLayout),
defaultValue: true,
defaultBindingMode: BindingMode.OneWay);
public static bool GetIsSquare(BindableObject view)
{
return (bool)view.GetValue(IsSquareProperty);
}
public static void SetIsSquare(BindableObject view, bool value)
{
view.SetValue(IsSquareProperty, value);
}
Dictionary<View, Rectangle> _boundsCache = new Dictionary<View, Rectangle>();
protected override void LayoutChildren(double x, double y, double width, double height)
{
foreach(var child in Children)
{
var isSquare = GetIsSquare(child);
if(isSquare)
{
Rectangle bounds;
if (!_boundsCache.ContainsKey(child))
_boundsCache[child] = bounds = GetLayoutBounds(child);
else
bounds = _boundsCache[child];
var absFlags = GetLayoutFlags(child);
var widthIsProportional = (absFlags & AbsoluteLayoutFlags.WidthProportional) != 0;
var heightIsProportional = (absFlags & AbsoluteLayoutFlags.HeightProportional) != 0;
var childWidth = widthIsProportional ? bounds.Width * width : bounds.Width;
var childHeight = heightIsProportional ? bounds.Height * height : bounds.Height;
var size = Math.Min(childWidth, childHeight);
SetLayoutBounds(
child,
new Rectangle(
bounds.X,
bounds.Y,
(widthIsProportional ? (size / width) : size),
(heightIsProportional ? (size / height) : size)
)
);
}
}
base.LayoutChildren(x, y, width, height);
}
}
Sample usage:
<local:SquareLayout>
<AbsoluteLayout BackgroundColor="Green"
AbsoluteLayout.LayoutBounds=".1,.1,1,1"
AbsoluteLayout.LayoutFlags="All" />
<AbsoluteLayout BackgroundColor="Blue"
AbsoluteLayout.LayoutBounds=".5,.5,.2,.1"
AbsoluteLayout.LayoutFlags="All" />
<AbsoluteLayout BackgroundColor="Red"
AbsoluteLayout.LayoutBounds=".9,.9,200,200"
AbsoluteLayout.LayoutFlags="PositionProportional" />
<AbsoluteLayout BackgroundColor="Yellow"
AbsoluteLayout.LayoutBounds="10,20,.3,.3"
AbsoluteLayout.LayoutFlags="SizeProportional" />
<AbsoluteLayout BackgroundColor="Silver"
local:SquareLayout.IsSquare="false"
AbsoluteLayout.LayoutBounds=".9,.9,1,.1"
AbsoluteLayout.LayoutFlags="All" />
</local:SquareLayout>

How do I read a view's bounds in the OnAppearing() method?

Currently I have this:
protected override void OnAppearing()
{
var collapsedHeight = ReportFormButtonBar.Height;
var reportFormExpandedPosition = ReportForm.Bounds;
var reportFormCollapsedPosition = ReportForm.Bounds;
reportFormCollapsedPosition.Y = ( ReportForm.Height - collapsedHeight );
reportFormExpandedPosition.Y = 0;
ReportForm.TranslateTo( reportFormCollapsedPosition.X, reportFormCollapsedPosition.Y, 200, Easing.CubicOut );
base.OnAppearing();
}
Which is supposed to position the ReportForm in a collapsed position. To do that, I need to read some bounds. However, the bounds are all set to x:0, y:0 with width and height to -1. I've been struggling with this for a long while now and can't find any answer.
TLDR: What should I do to be able to read the correct bounds in this method? Or is there some other way to accomplish view setup?
To work with view bounds, I would recommend overriding OnSizeAllocated or SizeChanged event.
//in view class
protected override void OnSizeAllocated(double width, double height)
{
base.OnSizeAllocated(width, height);
// retreive view bounds here..
}
//OR..
ReportForm.SizeChanged += (sender, e) => {
if(Width > 0 && Height > 0)
{
// retreive view bounds here..
}
};

Xamarin Forms: Change the size of scrollview on translateto

What I am trying to do is:
I have a scrollview inside my view and it's height is like 2/9 of the parent height. Then user can translate this scrollview to make it bigger. However scrollview's size does not change obviously. So even though it is bigger scrollview's size remains the same, killing the point. I could make it bigger initially. However it won't scroll since the size is big enough not to scroll.
I don't know if was able to explain it right. Hope i did.
Regards.
Edit
-------------
Some code to explain my point further
This is the scrollview
public class TranslatableScrollView : ScrollView
{
public Action TranslateUp { get; set; }
public Action TranslateDown { get; set; }
bool SwipedUp;
public TranslatableScrollView()
{
SwipedUp = false;
Scrolled += async delegate {
if (!SwipedUp && ScrollY > 0) {
TranslateUp.Invoke ();
SwipedUp = true;
} else if (SwipedUp && ScrollY <= 0) {
TranslateDown.Invoke ();
SwipedUp = false;
}
};
}
}
And this is the code in the page
sv_footer = new TranslatableScrollView {
Content = new StackLayout {
VerticalOptions = LayoutOptions.FillAndExpand,
HorizontalOptions = LayoutOptions.FillAndExpand,
Children = {
l_details,
l_place
},
}
};
sv_footer.TranslateUp += new Action (async delegate {
Parent.ForceLayout();
await cv_scrollContainer.TranslateTo(0, -transX, aSpeed, easing);
});
sv_footer.TranslateDown += new Action (async delegate {
await cv_scrollContainer.TranslateTo(0, 0, aSpeed, easing);
});
cv_scrollContainer = new ContentView {
Content = sv_footer,
VerticalOptions = LayoutOptions.Fill
};
I put the scrollview inside a contentview otherwise Its scroll indexes becomes 0 when they are translated. Ref: Xamarin Forms: ScrollView returns to begging on TranslateTo
The problem was, translateTo only changes the position of the element. I could use scaleTo after the translation. However it changes the both dimensions. Someone from Xamarin Forums suggested me to use LayoutTo which I did not know existed. With layoutTo you can change both the size and location. Giving an instance of Rectangle type.

I need to remove an image from the stage when I drop it onto the trash can image in AS3

I need to remove the image from the stage whenever I drag and drop "into" the trash can. I have been looking at this forever and really cant seem to find a solution.
<fx:Declarations>
<!-- Place non-visual elements (e.g., services, value objects) here -->
</fx:Declarations>
<fx:Script>
<![CDATA[
import mx.controls.Image;
//array of movies
public var myArray:Array = ["batman.jpg","cabin.jpg","christmasVacation.jpg"];
//init function
public function init():void{
var trashImage:Image = new Image();
//set trash img
trashImage.source = "asset/trash.jpg";
//adds img to stage
trashGrp.addElement(trashImage);
//loops 4 times
for(var i:Number = 0; i < myArray.length; i++){
var arrayEntry:String = myArray[i];
//makes new image
var newImage:Image = new Image();
//sets the image source for each image
newImage.source = "asset/" + arrayEntry;
//adds the image to the stage
grpMovies.addElement(newImage);
//set drag/drop
newImage.addEventListener(MouseEvent.MOUSE_DOWN, dragMe);
newImage.addEventListener(MouseEvent.MOUSE_UP, dropMe);
}
}
//dragMe function
public function dragMe(e:MouseEvent):void
{
// Get the Image that saw the mouse-down.
var img:Image = Image(e.currentTarget);
// Use the built-in method to start dragging.
img.startDrag();
}
//dropMe function
public function dropMe(e:MouseEvent):void
{
// Get the Image that saw the mouse-up.
var img:Image = Image(e.currentTarget);
// Use the built-in method to stop dragging.
img.stopDrag();
-**this is where my issue is. I cant get it to remove it when the image is hit**
if (trashGrp.hitTestObject(img)) {
grpMovies.removeElement(img);
}
}
]]>
</fx:Script>
<s:TileGroup id="grpMovies" columnWidth="225" rowHeight="225" requestedRowCount="1"
paddingTop="20"/>
<s:HGroup id= "trashGrp" x="950" y="20"/>
</s:Application>
Check out the following code, I think it does what you want. I modified the event handler to be attached to the main container, so that it gets triggered properly when you release the mouse button on the trash can.
<?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"
creationComplete="init()">
<fx:Declarations>
<!-- Place non-visual elements (e.g., services, value objects) here -->
</fx:Declarations>
<fx:Script>
<![CDATA[
import mx.controls.Image;
//array of movies
public var myArray:Array = ["batman.jpg","cabin.jpg","christmasVacation.jpg"];
private var _draggedImage:Image;
//init function
public function init():void{
var trashImage:Image = new Image();
//set trash img
trashImage.source = "assets/trash.png";
trashImage.mouseEnabled = false;
trashImage.mouseChildren = false;
//adds img to stage
trashGrp.addElement(trashImage);
//loops 4 times
for(var i:Number = 0; i < myArray.length; i++){
var arrayEntry:String = myArray[i];
//makes new image
var newImage:Image = new Image();
//sets the image source for each image
newImage.source = "assets/"+arrayEntry;
//adds the image to the stage
grpMovies.addElement(newImage);
//set drag/drop
newImage.addEventListener(MouseEvent.MOUSE_DOWN, dragMe);
}
}
//dragMe function
public function dragMe(e:MouseEvent):void
{
// Get the Image that saw the mouse-down.
var img:Image = Image(e.currentTarget);
// Use the built-in method to start dragging.
img.startDrag();
_draggedImage = img;
this.addEventListener(MouseEvent.MOUSE_UP, dropMe);
}
//dropMe function
public function dropMe(e:MouseEvent):void
{
_draggedImage.stopDrag();
this.removeEventListener(MouseEvent.MOUSE_UP, dropMe);
if (trashGrp.hitTestObject(_draggedImage)) {
grpMovies.removeElement(_draggedImage);
_draggedImage = null;
}
}
]]>
</fx:Script>
<s:TileGroup id="grpMovies" columnWidth="225" rowHeight="225" requestedRowCount="1"
paddingTop="20"/>
<s:HGroup id= "trashGrp" x="950" y="20"/>
</s:Application>

Sprite image rendering incorrectly on android tablet

I have a sprite animation in my app and it's working fine for standard phone sized screens but whenever I put it on a tablet the animation gets glitchy and doesn't look right at all. I think it has something to do with the height and widths of the image. I've tried setting them as dp and sp but they still won't render right on the tablet.
Here's my animation xml
<LinearLayout
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:gravity="center_horizontal"
android:paddingLeft="100dp" >
<view
android:id="#+id/animation"
android:layout_width="180dp"
android:layout_height="220dp"
class="com.scale.AndroidAnimation" />
</LinearLayout>
Here's my animation code
public class AndroidAnimation extends View{
private Bitmap mAnimation;
private Rect mSRectangle;
private int mNoOfFrames;
private int mCurrentFrame;
private int mSpriteHeight;
private int mSpriteWidth;
public AndroidAnimation(Context context, AttributeSet aSet) {
super(context, aSet);
mSRectangle = new Rect(0,0,0,0);
mCurrentFrame = 0;
}
public void init(Bitmap theBitmap, int Height, int Width, int theFrameCount) {
mAnimation = theBitmap;
mSpriteHeight = Height;
mSpriteWidth = Width;
mSRectangle.top = 0;
mSRectangle.bottom = mSpriteHeight;
mSRectangle.left = 0;
mSRectangle.right = mSpriteWidth;
mNoOfFrames = theFrameCount;
mCurrentFrame = 0;
}
public void update() {
mCurrentFrame++;
mCurrentFrame %= mNoOfFrames;
mSRectangle.left = mCurrentFrame * mSpriteWidth;
mSRectangle.right = mSRectangle.left + mSpriteWidth;
}
#Override
public void onDraw(Canvas canvas) {
super.onDraw(canvas);
Rect dest = new Rect(0, 0, mSpriteWidth, mSpriteHeight);
canvas.drawBitmap(mAnimation, mSRectangle, dest, null);
}
}
Where I initialize it
anim = (AndroidAnimation) findViewById(R.id.animation);
anim.init(BitmapFactory.decodeResource(getResources(), R.drawable.connecting_sprite), 300, 170, 3);
mHandler.removeCallbacks(myTimerTask);
mHandler.postDelayed(myTimerTask, 300);
My handler that progresses it
public void run() {
mHandler.removeCallbacks(myTimerTask);
if (mCurrScreen == WAITING_SCREEN) {
mHandler.postDelayed(myTimerTask, 300);
}
anim.update();
anim.invalidate();
}
Thanks in advance!
It is good way to design seperate layout for tablet.Create layout-xlarge folder in res and put your new layout animation.xml file which is optimized for tablet screen.

Resources