第二十六章:自定义布局(十三)

简介: Layout和LayoutToVisualElement定义了一组转换属性。这些是AnchorX,AnchorY,Rotation,RotationX,RotationY,Scale,TranslationX和TranslationY,它们根本不影响布局。

Layout和LayoutTo
VisualElement定义了一组转换属性。这些是AnchorX,AnchorY,Rotation,RotationX,RotationY,Scale,TranslationX和TranslationY,它们根本不影响布局。换句话说,设置这些属性不会生成对InvalidateMeasure或InvalidateLayout的调用。从GetSizeRequest返回的元素大小不受这些属性的影响。布局调用大小和位置元素,就好像这些属性不存在一样。
这意味着您可以为这些属性设置动画,而不会生成一堆布局循环。在ViewExtensions中定义为扩展方法的TranslateTo,ScaleTo,RotateTo,RotateXTo和RotateYTo动画方法完全独立于布局。
但是,ViewExtensions还定义了一个名为LayoutTo的方法,该方法对Layout方法进行动画调用。这会导致更改元素相对于其父元素的布局大小或位置,并设置元素的Bounds,X,Y,Width和Height属性的新值。
因此,使用LayoutTo需要采取一些预防措施。
例如,假设一个元素是StackLayout的子元素。当StackLayout获取LayoutChildren调用时,它将调用该元素上的Layout来调整大小并将其定位在相对于自身的特定位置。假设您的程序然后在该元素上调用LayoutTo以赋予它新的大小和位置。 StackLayout不知道这一点,所以如果StackLayout经历另一个布局周期,它会将元素移回它认为应该的位置。如果您仍然需要将元素放在StackLayout认为应该的位置之外的其他位置,您可能希望将处理程序附加到StackLayout的LayoutChanged事件并调用Layout或再次对该元素运行LayoutTo动画。
另一个问题是在具有许多子项的布局上运行LayoutTo动画。当然,这是允许的,但请记住,布局将获得对其Layout方法的大量调用,因此在动画正在进行时也会调用LayoutChildren方法。对于每个对LayoutChildren覆盖的调用,布局类将尝试布置其所有子代(当然,其中一些子代可能是带子代的其他布局),并且动画可能会变得非常不稳定。
但是您可以使用LayoutTo动画和Layout方法之间的关系来实现一些有趣的效果。元素必须将其Layout方法调用为在屏幕上可见,但调用LayoutTo必须满足该要求。
这是一个派生自CartesianLayout的类,名为AnimatedCartesianLayout。它定义了两个可绑定属性(不附加可绑定属性)来控制动画,而不是调用Layout并设置Rotation属性,它调用LayoutTo和(可选)RotateTo:

namespace Xamarin.FormsBook.Toolkit
{
    public class AnimatedCartesianLayout : CartesianLayout
    {
        public static readonly BindableProperty AnimationDurationProperty =
            BindableProperty.Create(
                "AnimatedDuration",
                typeof(int),
                typeof(AnimatedCartesianLayout),
                1000);
        public int AnimationDuration
        {
            set { SetValue(AnimationDurationProperty, value); }
            get { return (int)GetValue(AnimationDurationProperty); }
        }
        public static readonly BindableProperty AnimateRotationProperty =
            BindableProperty.Create(
                "AnimateRotation",
                typeof(bool),
                typeof(AnimatedCartesianLayout),
                true);
        public bool AnimateRotation
        {
            set { SetValue(AnimateRotationProperty, value); }
            get { return (bool)GetValue(AnimateRotationProperty); }
        }
        protected override void LayoutChildren(double x, double y, double width, double height)
        {
            foreach (View child in Children)
            {
                if (!child.IsVisible)
                    continue;
                double angle;
                Rectangle bounds = GetChildBounds(child, x, y, width, height, out angle);
                // Lay out the child.
                if (child.Bounds.Equals(new Rectangle(0, 0, -1, -1)))
                {
                    child.Layout(new Rectangle(x + width / 2, y + height / 2, 0, 0));
                }
                child.LayoutTo(bounds, (uint)AnimationDuration);
                // Rotate the child.
                if (AnimateRotation)
                {
                    child.RotateTo(angle, (uint)AnimationDuration);
                }
                else
                {
                    child.Rotation = angle;
                }
            }
        }
    }
}

唯一棘手的部分涉及一个尚未收到第一个布局调用的孩子。 这样的子项的Bounds属性是矩形(0,0,-1,-1),LayoutTo动画将使用该值作为动画的起始点。 在这种情况下,LayoutChildren方法首先调用Layout以将子项定位在中心并为其指定大小(0,0)。
AnimatedUnitCube程序的XAML文件几乎与UnitCube程序相同,但AnimatedCartesianLayout的动画持续时间为3秒:

<ContentPage xmlns="http://xamarin.com/schemas/2014/forms"
             xmlns:x="http://schemas.microsoft.com/winfx/2009/xaml"
             xmlns:toolkit=
                 "clr-namespace:Xamarin.FormsBook.Toolkit;assembly=Xamarin.FormsBook.Toolkit"
             x:Class="AnimatedUnitCube.AnimatedUnitCubePage">
 
    <toolkit:AnimatedCartesianLayout BackgroundColor="Yellow"
             AnimationDuration="3000"
             HorizontalOptions="Center"
             VerticalOptions="Center">
        <toolkit:AnimatedCartesianLayout.Resources>
            <ResourceDictionary>
                <Style x:Key="baseStyle" TargetType="BoxView">
                    <Setter Property="Color" Value="Blue" />
                    <Setter Property="toolkit:CartesianLayout.Thickness" Value="3" />
                </Style>
                <Style x:Key="hiddenStyle" TargetType="BoxView"
                       BasedOn="{StaticResource baseStyle}">
                    <Setter Property="Opacity" Value="0.25" />
                </Style>
                <!-- Implicit style. -->
                <Style TargetType="BoxView"
                       BasedOn="{StaticResource baseStyle}" />
 
            </ResourceDictionary>
        </toolkit:AnimatedCartesianLayout.Resources>
        __
 
    </toolkit:AnimatedCartesianLayout>
</ContentPage>

以下屏幕截图显示了从左到右几乎到多维数据集完成点的进度:
2019_05_26_163549
根据它们的定义方式,某些水平线根本不会旋转,而其他水平线(例如底部的水平线)必须旋转180度。
如您所知,近年来用户界面变得更加生动和动态,因此使用LayoutTo而不是Layout来探索各种可能的技术可以成为冒险程序员追求的全新领域。

目录
相关文章
|
JavaScript Android开发
第二十六章:自定义布局(十二)
更多附加的可绑定属性附加的可绑定属性也可以在XAML中设置并使用Style设置。 为了了解它是如何工作的,让我们检查一个名为CartesianLayout的类,它模仿一个二维的,四象限的笛卡尔坐标系。
521 0
|
JavaScript Android开发
第二十六章:自定义布局(十一)
重叠的子项Layout 类可以在其子项上调用Layout方法,以便子项重叠吗?是的,但这可能会在你的脑海中提出另一个问题:什么决定孩子们的呈现顺序?哪些孩子看似坐在前台,可能部分或完全掩盖了背景中显示的其他孩子?在某些图形环境中,程序员可以访问名为Z-index的值。
606 0
|
JavaScript Android开发
第二十六章:自定义布局(十)
不允许无约束的尺寸!有时您希望在屏幕上看到所有内容,可能是一系列大小统一的行和列。您可以使用带有星号定义的所有行和列定义的Grid执行类似的操作,以使它们具有相同的大小。唯一的问题是您可能还希望行数和列数基于子节点数,并针对屏幕空间的最佳使用进行了优化。
802 0
|
存储 缓存 JavaScript
第二十六章:自定义布局(九)
编码的一些规则从上面的讨论中,您可以为自己的Layout 衍生物制定几个规则:规则1:如果布局类定义了诸如间距或方向等属性,则这些属性应由可绑定属性支持。 在大多数情况下,这些可绑定属性的属性更改处理程序应调用InvalidateLayout。
2041 0
|
JavaScript Android开发
第二十六章:自定义布局(八)
失效假设您已在页面上组装了一些布局和视图,并且由于某种原因,代码隐藏文件(或者可能是触发器或行为)会更改Button的文本,或者可能只是字体大小或属性。 该更改可能会影响按钮的大小,这可能会对页面其余部分的布局更改产生连锁反应。
3387 0
|
JavaScript Android开发
第二十六章:自定义布局(七)
垂直和水平定位简化在VerticalStack中,LayoutChildren覆盖的末尾是一个switch语句,它有助于根据子级的HorizontalOptions属性设置水平定位每个子级。 这是整个方法: public class VerticalStack : Layout<View> { ...
854 0
|
JavaScript Android开发
第二十六章:自定义布局(六)
从Layout派生 我们现在拥有足够的知识来创建我们自己的布局类。布局中涉及的大多数公共和受保护方法都是由非泛型布局类定义的。 Layout 类派生自Layout,并将泛型类型约束为View及其派生类。
742 0
|
JavaScript Android开发 iOS开发
第二十六章:自定义布局(五)
内视过程中本章到目前为止提供的大部分信息都是从包含派生自各种元素(如StackLayout,ScrollView和Label)的类的测试程序汇编而来,覆盖虚拟方法(如GetSizeRequest,OnSizeRequest,OnSizeAllocated和LayoutChildren) ,并使用System.Diagnostics命名空间中的Debug.WriteLine方法在Visual Studio或Xamarin Studio的“输出”窗口中显示信息。
743 0
|
Android开发
第二十六章:自定义布局(四)
无限约束现在这里有一些标记,起初看起来与前面的例子非常相似,但有很大的不同: <ContentPage __ Padding="20"> <StackLayout> <Label Text="Sample text" /> __ </StackLayout> </ContentPage> ContentPage仍然使用参数(0,0,360,640)进行初始布局调用,而LayoutChildren覆盖的参数是(20,20,320,600)。
679 0
|
JavaScript Android开发
第二十六章:自定义布局(三)
约束和大小请求您刚刚看到LayoutChildren覆盖在某些情况下如何仅基于LayoutChildren参数调用其子或子上的Layout。 但在更一般的情况下,LayoutChildren需要在调用子项的Layout方法之前知道其子项的大小。
660 0