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>
Related
I'm having a problem when I show an image on .NET MAUI, the size of the image is always bigger than it actually is (blue part in the image below).
Screenshot
My code is as follows:
<Grid>
<ScrollView>
<VerticalStackLayout>
<Image Source="https://cdn-5e5150f5f911c807c41ebdc8.closte.com/wp-content/uploads/IoT-development-kit-article-banner-scaled-900x400.jpg"
Aspect="AspectFit" BackgroundColor="Blue">
<Image.Margin>
<OnIdiom Phone="10" Tablet="20" Desktop="20"/>
</Image.Margin>
</Image>
</VerticalStackLayout>
</ScrollView>
</Grid>
Is there a way to keep the size of the image in proportion to the actual size?
I made some changes:
I tried using data binding in MVVM way.
I tried counting image ratio using platform code.
The following is my code,
For MainPage.xaml, the difference is that i use data binding for image Source and AspectRatio property which would be claimed in MainPageVeiwModel.
<Grid>
<ScrollView>
<VerticalStackLayout>
<a:AspectImage Source="{Binding ImageUrl}"
AspectRatio="{Binding AspectRatio}" Aspect="AspectFit" BackgroundColor="Blue">
<a:AspectImage.Margin>
<OnIdiom Phone="10" Tablet="20" Desktop="20"/>
</a:AspectImage.Margin>
</a:AspectImage>
</VerticalStackLayout>
</ScrollView>
</Grid>
For custom control AspectImage, the difference is that I changed AspectRatio to Bindable property as we use binding for this property. More info Bindable properties.
public class AspectImage : Image
{
public static readonly BindableProperty AspectRatioProperty = BindableProperty.Create("AspectRatio", typeof(double), typeof(AspectRatioContainer), null);
public double AspectRatio
{
get { return (double)GetValue(AspectRatioProperty); }
set { SetValue(AspectRatioProperty, value); }
}
public AspectImage()
{
SizeChanged += HandleSizeChanged;
}
private void HandleSizeChanged(object sender, EventArgs e)
{
if (this.Width > 0 && AspectRatio > 0)
{
var desiredHeightRequest = this.Width * AspectRatio;
if ((int)desiredHeightRequest != (int)HeightRequest)
{
this.HeightRequest = (int)desiredHeightRequest;
InvalidateMeasure();
}
}
}
}
For MainPageViewModel, we add AspectRatio and ImageUrl property for custom control and count AspectRatio.
public class MainPageViewModel
{
public string ImageUrl { get; set; }
public double AspectRatio { get; set; }
public MainPageViewModel()
{
ImageUrl = "https://cdn-5e5150f5f911c807c41ebdc8.closte.com/wp-content/uploads/IoT-development-kit-article-banner-scaled-900x400.jpg";
AspectRatio = CountAspectRatio(ImageUrl);
}
private double CountAspectRatio(string imageUrl)
{
var service = new GetImageSizeService();
Size imageSize = service.GetImageSize(imageUrl);
return imageSize.Height / imageSize.Width;
}
}
From above code in MainPageViewModel, we count AspectRatio by call platform code. If you are not familiar with it, i recommend this tutorial first: How To Write Platform-Specific Code in .NET MAUI.
To inject platform code in Maui (in Xamarin could use DependencyService):
First, in Project folder, create a new partial class, let's call it GetImageSizeService:
public partial class GetImageSizeService
{
public partial Size GetImageSize(string file);
}
Then creat another partial class in Platforms/iOS folder, called it GetImageSizeService also. Pay attention to the namespace should be the same as above file.
public partial class GetImageSizeService
{
public partial Size GetImageSize(string file)
{
NSData data = NSData.FromUrl(NSUrl.FromString(file));
UIImage image = UIImage.LoadFromData(data);
return new Size((double)image.Size.Width, (double)image.Size.Height);
}
}
Then in MainPageViewModel, we just call this service and count the AspectRatio.
=========================== First post=============
The link you add did inspire me. And if i understand your question correctly, you could try the following code which worked for me:
Create AspectImage custom control which set aspect ratio for width and height
public class AspectImage : Image
{
public double AspectRatio { get; set; }
public AspectImage()
{
SizeChanged += HandleSizeChanged;
}
private void HandleSizeChanged(object sender, EventArgs e)
{
if (this.Width > 0 && AspectRatio > 0)
{
var desiredHeightRequest = this.Width * AspectRatio;
if ((int)desiredHeightRequest != (int)HeightRequest)
{
this.HeightRequest = (int)desiredHeightRequest;
InvalidateMeasure();
}
}
}
}
For xaml, consume the AspectImage. Here the aspect ratio seems to be 4/9 Approximately equal to 0.44
<Grid>
<ScrollView>
<VerticalStackLayout>
<a:AspectImage Source="https://cdn-5e5150f5f911c807c41ebdc8.closte.com/wp-content/uploads/IoT-development-kit-article-banner-scaled-900x400.jpg"
AspectRatio="0.44" Aspect="AspectFit" BackgroundColor="Blue">
<a:AspectImage.Margin>
<OnIdiom Phone="10" Tablet="20" Desktop="20"/>
</a:AspectImage.Margin>
</a:AspectImage>
</VerticalStackLayout>
</ScrollView>
</Grid>
Hope it works for you.
I want to create a functionality to tap and add a pin image over another background image and the background image should be able to zoom and pan this is the XAML code for this, here the pinch zoom is not working but tap event is working fine
<ContentPage xmlns="http://xamarin.com/schemas/2014/forms"
xmlns:x="http://schemas.microsoft.com/winfx/2009/xaml"
xmlns:local="clr-namespace:POC"
xmlns:ui="clr-namespace:Vapolia.Lib.Ui;assembly=XamarinFormsGesture"
x:Class="POC.MainPage"
Title="Main Page">
<ScrollView AbsoluteLayout.LayoutFlags="All">
<local:PinchAndPanContainer>
<local:PinchAndPanContainer.Content >
<AbsoluteLayout x:Name="AbsoluteLayoutForImage">
<Image x:Name="FloorPlanImage"
Source="Capture2.png"
HeightRequest="400"
IsEnabled="True"
InputTransparent="True"
ui:Gesture.TapCommand2="{Binding TapCommand2}"/>//This Property
</AbsoluteLayout>
</local:PinchAndPanContainer.Content>
</local:PinchAndPanContainer>
</ScrollView>
in the cs file, this tap command is adding a pin image inside the absolute layout using the coordinates in Point.
public Command<Point> TapCommand2 => new Command<Point>(point =>
{
AddPin(point);
});
Now if we just remove ui:Gesture.TapCommand2="{Binding TapCommand2}" this property from the above code pinch and pan works fine.
For Tap event I used Vapolia.XamarinFormsGesture NuGet package and for pinch and pan used xamarin forms Gesture Recognizer
Can anyone help
Recently I did a similar functionality and ended up creating a CustomControl as below:
Note: I have used the FFImageLoadings CachedImage in my class in case you are not using FFImage just replace it with your defualt xamarin forms image.
And this has the following functionalities: PanSwipe, Zoom and DoubleTap to zoom.
using System;
using Xamarin.Forms;
using FFImageLoading.Forms;
public class ZoomableImage : CachedImage //In case not using ff image replace this with the Image control
{
private const double MIN_SCALE = 1;
private const double MAX_SCALE = 4;
private const double OVERSHOOT = 0.15;
private double StartScale, LastScale;
private double StartX, StartY;
public ZoomableImage()
{
var pinch = new PinchGestureRecognizer();
pinch.PinchUpdated += OnPinchUpdated;
GestureRecognizers.Add(pinch);
var pan = new PanGestureRecognizer();
pan.PanUpdated += OnPanUpdated;
GestureRecognizers.Add(pan);
var tap = new TapGestureRecognizer { NumberOfTapsRequired = 2 };
tap.Tapped += OnTapped;
GestureRecognizers.Add(tap);
Scale = MIN_SCALE;
TranslationX = TranslationY = 0;
AnchorX = AnchorY = 0;
}
protected override SizeRequest OnMeasure(double widthConstraint, double heightConstraint)
{
Scale = MIN_SCALE;
TranslationX = TranslationY = 0;
AnchorX = AnchorY = 0;
return base.OnMeasure(widthConstraint, heightConstraint);
}
private void OnTapped(object sender, EventArgs e)
{
if (Scale > MIN_SCALE)
{
this.ScaleTo(MIN_SCALE, 250, Easing.CubicInOut);
this.TranslateTo(0, 0, 250, Easing.CubicInOut);
}
else
{
AnchorX = AnchorY = 0.5; //TODO tapped position
this.ScaleTo(MAX_SCALE, 250, Easing.CubicInOut);
}
}
private void OnPanUpdated(object sender, PanUpdatedEventArgs e)
{
switch (e.StatusType)
{
case GestureStatus.Started:
StartX = (1 - AnchorX) * Width;
StartY = (1 - AnchorY) * Height;
break;
case GestureStatus.Running:
AnchorX = Clamp(1 - (StartX + e.TotalX) / Width, 0, 1);
AnchorY = Clamp(1 - (StartY + e.TotalY) / Height, 0, 1);
break;
}
}
private void OnPinchUpdated(object sender, PinchGestureUpdatedEventArgs e)
{
switch (e.Status)
{
case GestureStatus.Started:
LastScale = e.Scale;
StartScale = Scale;
AnchorX = e.ScaleOrigin.X;
AnchorY = e.ScaleOrigin.Y;
break;
case GestureStatus.Running:
if (e.Scale < 0 || Math.Abs(LastScale - e.Scale) > (LastScale * 1.3) - LastScale)
{ return; }
LastScale = e.Scale;
var current = Scale + (e.Scale - 1) * StartScale;
Scale = Clamp(current, MIN_SCALE * (1 - OVERSHOOT), MAX_SCALE * (1 + OVERSHOOT));
break;
case GestureStatus.Completed:
if (Scale > MAX_SCALE)
this.ScaleTo(MAX_SCALE, 250, Easing.SpringOut);
else if (Scale < MIN_SCALE)
this.ScaleTo(MIN_SCALE, 250, Easing.SpringOut);
break;
}
}
private T Clamp<T>(T value, T minimum, T maximum) where T: IComparable
{
if (value.CompareTo(minimum) < 0)
return minimum;
else if (value.CompareTo(maximum) > 0)
return maximum;
else
return value;
}
}
Good luck,
In case of queries kindly revert.
I used the solution from FreakyAli in my MVVM app. I just added his code as a .cs file in the ViewModels folder and referenced the new class in my XAML:
<?xml version="1.0" encoding="utf-8" ?>
<views:MvxContentPage x:TypeArguments="viewModels:ImageViewModel"
xmlns="http://xamarin.com/schemas/2014/forms"
xmlns:x="http://schemas.microsoft.com/winfx/2009/xaml"
xmlns:views="clr-namespace:MvvmCross.Forms.Views;assembly=MvvmCross.Forms"
xmlns:viewModels="clr-namespace:BLE.Client.ViewModels;assembly=BLE.Client"
x:Class="BLE.Client.Pages.ImagePage" Title="View Image">
<Grid>
<Grid.RowDefinitions>
<RowDefinition Height="*"></RowDefinition>
</Grid.RowDefinitions>
<StackLayout Grid.Row="0" Orientation="Horizontal" >
<viewModels:ZoomableImage x:Name="WaypointImage"
Source="{Binding MyImage}"
HorizontalOptions="FillAndExpand">
</viewModels:ZoomableImage>
</StackLayout>
</Grid>
</views:MvxContentPage>
C#:
private ImageSource _myImage;
public ImageSource MyImage
{
get => _myImage;
set
{
_myImage = value;
RaisePropertyChanged(() => MyImage);
}
}
I need to create a triangle at the corner of a label/frame like the pic below with a number/small text in it.But just a way to draw the corner would be a great start.
How Can you do you do that ?
Any sample anywhere. Many thanks
Instead Using Plugin for just Triangle you can just use BoxView and rotate it with 135 and give negative margin so half portion will only get visible.
I achieved this using NControl https://github.com/chrfalch/NControl
public class DiagonalControl : NControlView
{
public static readonly BindableProperty CornerRadiusBindableProperty =
BindableProperty.Create(nameof(CornerRadius), typeof(int), typeof(DiagonalControl), 8);
private Xamarin.Forms.Color _backgroundColor;
public DiagonalControl()
{
base.BackgroundColor = Xamarin.Forms.Color.Transparent;
}
public new Xamarin.Forms.Color BackgroundColor
{
get
{
return _backgroundColor;
}
set
{
_backgroundColor = value;
Invalidate();
}
}
public int CornerRadius
{
get
{
return (int)GetValue(CornerRadiusBindableProperty);
}
set
{
SetValue(CornerRadiusBindableProperty, value);
}
}
public override void Draw(ICanvas canvas, Rect rect)
{
base.Draw(canvas, rect);
canvas.FillPath(new PathOp[] {
new MoveTo (0,0),
new LineTo (rect.Width, rect.Height),
new LineTo (rect.Width, 0),
new ClosePath ()
}, new NGraphics.Color((Xamarin.Forms.Color.White).R, (Xamarin.Forms.Color.White).G, (Xamarin.Forms.Color.White).B));
}
}
Then in the XAML use it like
<customviews:DiagonalControl
x:FieldModifier="Public"
HeightRequest="50"
HorizontalOptions="End"
VerticalOptions="Start"
WidthRequest="50" />
Draw path directly in xaml from Xamarin.Forms 4.7.0
(bump into the same request, and have an update for others)
<Path
HorizontalOptions="End"
VerticalOptions="Start"
Data="M 0,0 L 36,0 36,36Z"
Fill="#70a33e"
Stroke="Gray" />
And more details:
https://learn.microsoft.com/en-us/xamarin/xamarin-forms/user-interface/shapes/
https://devblogs.microsoft.com/xamarin/xamarin-forms-shapes-and-paths/
UPDATED:
So, I'm unable to create IOS custom height ProgressBar.
I use the latest version of Xamarin.Forms.
.cs file:
public class SplashScreenProgressBar : ProgressBar
{
public static readonly BindableProperty TintColorProperty =
BindableProperty.Create<CustomProgressBar, Color>( p => p.TintColor, Color.Green);
public Color TintColor
{
get { return (Color) GetValue(TintColorProperty); }
set { SetValue(TintColorProperty, value); }
}
public static readonly BindableProperty HeightExtendedProperty =
BindableProperty.Create("HeightExtended", typeof(double), typeof(SplashScreenProgressBar), 10.0);
public double HeightExtended
{
get { return (double) GetValue(HeightExtendedProperty); }
set { SetValue(HeightExtendedProperty, value); }
}
public static readonly BindableProperty BackgroundColorExtendedProperty =
BindableProperty.Create("BackgroundColorExtended", typeof(Color), typeof(SplashScreenProgressBar),
Color.White);
public Color BackgroundColorExtended
{
get { return (Color) GetValue(BackgroundColorExtendedProperty); }
set { SetValue(BackgroundColorExtendedProperty, value); }
}
}
Here is iOS renderer:
public class SplashScreenProgressBarRenderer : ProgressBarRenderer
{
protected override void OnElementChanged(ElementChangedEventArgs<ProgressBar> e)
{
base.OnElementChanged(e);
var element = (SplashScreenProgressBar)Element;
this.Control.ProgressTintColor = element.TintColor.ToUIColor();
this.Control.TrackTintColor = element.BackgroundColorExtended.ToUIColor();
}
public override void LayoutSubviews()
{
base.LayoutSubviews();
var element = (SplashScreenProgressBar)Element;
var X = 1.0f;
var Y = (System.nfloat)element.HeightExtended;
CGAffineTransform transform = CGAffineTransform.MakeScale(X, Y);
this.Control.Transform = transform;
this.Control.ClipsToBounds = true;
this.Control.Layer.MasksToBounds = true;
this.Control.CornerRadius = 5;
}
}
xaml file:
<StackLayout HorizontalOptions="FillAndExpand" VerticalOptions="FillAndExpand" BackgroundColor="White" >
<StackLayout HorizontalOptions="FillAndExpand" VerticalOptions="FillAndExpand" Margin="50" BackgroundColor="White" >
<views:SplashScreenProgressBar x:Name="Progress"
TintColor="#5FA5F9"
HorizontalOptions="FillAndExpand"
VerticalOptions="Center"
BackgroundColorExtended="#FFF" />
</StackLayout>
</StackLayout>
But this way doesn't work.
I googled and tried almost all samples which I've found, but nothing happened.
Screenshot:
As you see on the screenshot corner radius is applied to ProgressBar, but height(scale) isn't applied.
In PCL
StackLayout is overlapped with status bar.
Add Margin on it.
<StackLayout Margin="50" xxxxx
In Renderer
ClipsToBounds ,Layer.MasksToBounds ,Layer.CornerRadius should be set on the Control not the Renderer
this.Control.Transform = transform;
this.Control.ClipsToBounds = true;
this.Control.Layer.MasksToBounds = true;
this.Control.Layer.CornerRadius = 5;
When use the custom renderer in ios, it always occupy the whole area of parent element. So, u need to update the progress bar frame once again in layoutsubview.
bool is rendered;
public override void LayoutSubviews()
{
if(!rendered)
{
Frame = new CGRect(x,y,width,height);
setNeedsdisplay
}
rendered=true;
}
So, I spent a lot of hours to researching and investigation.
And seems there xamarin.forms iOS bug for progress bar rounded corners.
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.