读书笔记3:uwp布局原理与自定义布局设计

布局原理 

  布局的意义

  布局是页面编程的第一步,是总体把握页面上UI元素的显式。由于Windows10支持不同分辨率不同设备,布局显得越来越重要,也越来越复杂。。布局有着如下意义:

  1)代码逻辑:良好的布局会使代码逻辑非常清晰,不好的布局方案回事页面代码逻辑混乱。好的布局方案,要给予对各种布局控件的理解,然后充分的利用他们的特性去实现布局的效果。

  2)效率性能:布局不仅仅是界面UI的事情,他甚至会影响程序的运行效率。当界面要展示大量的控件时,布局的好坏就会直接影响到程序的效率。良好的布局实现逻辑会让程序即使在有大量控件的页面也能流畅地运行。

  3)动态适配:动态适配包括两个方面,一是Windows10对多种分辨率界面的适配;二是当页面的控件不确定时会产生动态适配的问题。好的布局方案可以使应用程序兼容各种分辨率的区别。对于动态产生的控件,在布局时就要有足够的空间来适配会变化的空间结构和页面变化。

  4)实现复杂的布局:有时候界面需要实现一些复杂的布局,而对应的Windows10 是没有这样的布局控件去支持这样的复杂的布局效果的,这时候就需要自定义布局的规律来解决这样的问题。

  布局系统

  布局系统是指对Windows10 布局面板所进行的布局过程的统称。要定义一个可视化对象,必须将它放置于Panel或其他布局面板中。Panel类定义了在屏幕上绘制所有面板里的成员(Children属性)的布局行为。布局是一个递归系统,实现在屏幕上对元素进行大小调整、定位和绘制。在整个布局过程中,布局系统对布局面板成员的处理分为两个过程:一是测量处理过程,而是排列处理过程。每当面板里的成员改变其位置时,布局系统就可能触发一个新的处理过程,重新处理上面所说的两个过程。调用布局系统,总会发生以下一系列操作:

  (1)第一次递归遍历每个布局面板的子元素的大小。

  (2)计算在FrameworkElement类的子类控件元素上定义的大小调整属性,例如Width、Height和Margin。

  (3)应用布局面板特定逻辑,例如StackPanel 的水平布局。

  (4)第二次递归遍历负责吧子元素排到自己的相对位置

  (5)把所有的子元素绘制到屏幕上

  因此,了解布局系统的特性是很重要的,因为不必要的调用可能导致应用性能变差。

  布局系统的重要方法和属性

  Windows10 的 UI 元素有两个非常重要的基类,他们的继承层次结构如下:

1 Windows.UI.Xaml.DependencyObject
2     Windows.UI.Xaml.UIElement
3         Windows.UI.Xaml.FrameworkElement

  1)UIElement类:

  UIElement类是具有可视外观并可以处理基本输入的大多数对象的基类。关于布局,UIElement类有两个重要属性和两个重要方法:

  DesiredSize属性:只读属性,类型是Size类,表示在布局过程中测量处理过程中计算的大小。

  RenderSize属性:只读属性,类型为Size类,表示UI元素最终呈现的大小。RenderSize是ArrangeOverride方法返回值。

  Public void Measure(Size availableSize)方法:Measure方法将更新UIElement 的 DesiredSize属性,测量UI元素的大小。如果在该 UI 元素上实现了FrameworkElement.MeasureOverride(System.Windows.Size)方法,将会用此方法形成递归布局更新。参数availableSize表示父对象可以为子对象分配的可用空间。子对象可以请求大于可用空间的空间。

  Public void Arrange(Rect finalRect)方法:Arrange 方法定位子对象并确定UIElement的大小。如果在该 UI 元素上实现了FrameworkElement.ArrangeOverride 方法,将会用此方法形成递归布局更新。参数finalRect表示布局中父对象为子对象计算的最终大小,作为System.Windows.Rect值提供。

  2)FrameworkElement类:

  FrameworkElement类是UIElement类的子类,为Windows10 布局中涉及的对象提供公共的API框架。FrameworkElement类中有两个和布局相关的virtual方法:MeasureOverride 方法和 ArrangeOverride 方法。如果为了满足特定的布局需求需要自定义布局面板,就需要重写MeasureOverride 和 ArrangeOverride 方法。

  protected virtual Size MearsureOverride(Size availableSize):提供Windows10 布局的度量处理过程的行为,可以重写该方法来定义自己的度量处理过程行为。参数availableSize 表示对象可以赋给子对象的可用大小,可以指定无穷大值(System.Double.PositiveInfinity),以指示对象的大小将调整为可用内容的大小,如果计算出来的值大于availableSize,将被截取同availableSize大小的部分。返回结果表示该对象在布局过程中基于不同因素未确定的所需的大小。

  protected virtual Size ArrangeOverride(Size finalSize):提供Windows10 布局的排列处理过程中的行为,可以重写该方法开定义其排列处理过程的行为。参数finalSize表示父级中此对象应用来排列自身及其子元素的最终区域。返回结果表示元素在布局中排列后使用的实际大小。

  自定义布局规则

  以下实现了一个新的自定义布局面板,将布局面板中的元素按照圆形的排列规则进行排列:

  1)创建布局类

  所有的布局面板都需要从Panel类派生,自定义其测量和排列的过程。Panel类中Children属性表示布局里的子对象,测量和排列的过程需要根据Children属性来获取面板中所有的子对象,再根据排列规矩对这些子对象进行测量和排列。

  Circle类的定义

 1 namespace Newdefine_panel
 2 {
 3     public class CirclePanel : Panel
 4     {
 5         //自定义半径变量
 6         private double _radius = 0;
 7         public CirclePanel() { }
 8         //注册半径依赖属性
 9         //"Radius"表示半径属性的名称
10         //typeof(double)表示半径属性类型
11         //typeof(CirclePanel)表示半径属性的归属这类型
12         // new PropertyMetadata(0.0, OnRadiusPropertyChanged)表示半径属性的元数据实例
13         public static readonly DependencyProperty RadiusProperty = DependencyProperty.RegisterAttached
14         ("Radius", typeof(double), typeof(CirclePanel), new PropertyMetadata(0.0, OnRadiusPropertyChanged));
15
16         //定义半径属性
17         public double Radius
18         {
19             get { return (double)GetValue(RadiusProperty); }
20             set { SetValue(RadiusProperty, value); }
21         }
22
23         //实现半径属性改变事件
24         private static void OnRadiusPropertyChanged(DependencyObject obj, DependencyPropertyChangedEventArgs e)
25         {
26             //获取触发属性改变的CirclePanel对象
27             CirclePanel target = (CirclePanel)obj;
28             //获取传递进来的值,并赋值给半径变量
29             target._radius = (double)e.NewValue;
30             //使排列状态失效,进行重新排列
31             target.InvalidateArrange();
32         }
33
34         //重载基类的MeasureOverride
35         protected override Size MeasureOverride(Size availableSize)
36         {
37             //处理测量子对象的逻辑
38             return new Size(width, heigh);
39         }
40         protected override Size ArrangeOverride(Size finalSize)
41         {
42              //处理排列子对象的逻辑
43              return finalSize;
44         }
45     }
46
47 }

 

 2)实现测量过程

  测量过程是在Override 的 MeasureOverride方法中实现的,该方法首先要遍历所有子对象,通过Measure()方法来测量子对象的大小,通过这些信息给自定义面板分配大小。

 1   protected override Size MeasureOverride(Size availableSize)
 2         {
 3             //最大宽度变量
 4             double maxElementWidth = 0;
 5             //遍历所有子对象,并调用子对象的Measure方法进行测量,取出宽度最大的子对象
 6             foreach (UIElement child in Children)
 7             {
 8                 //Measure方法返回DesiredSize属性
 9                 child.Measure(availableSize);
10                 maxElementWidth = Math.Max(child.DesiredSize.Width, maxElementWidth);
11             }
12             //取两个半径的大小和最大宽度的两倍作为面板宽度
13             double panelWidth = 2 * this.Radius + 2 * maxElementWidth;
14             //计算面板的实际大小并返回Size对象。
15             double width = Math.Min(panelWidth, availableSize.Width);
16             double heigh = Math.Min(panelWidth, availableSize.Height);
17             return new Size(width, heigh);
18         }

  

  3)实现排列过程

  排列过程在重载的ArrangeOverride方法中实现。在ArrangeOverride方法中通过自定义的规则一一排列子对象。在实例中要实现的是把子对象按照一个固定的圆形进行排列,为此要计算每个子对象所占的角度,计算器坐标,然后按照一定的角度对子对像进行旋转来适应圆形的布局。实现代码如下:

 1 protected override Size ArrangeOverride(Size finalSize)
 2         {
 3             //当前的角度,从0开始排列
 4             double degree = 0;
 5             //计算每个子对象所占用的角度的大小
 6             double degreeStep = (double)360 / this.Children.Count;
 7
 8             double mX = this.DesiredSize.Width / 2;
 9             double mY = this.DesiredSize.Height / 2;
10             //遍历所有的子对象进行排列
11             foreach(UIElement child in Children)
12             {
13                 //角度使用弧度制表示
14                 double angle = Math.PI * degree / 180.0;
15                 //根据弧度计算圆弧上的x,y值
16                 double x = Math.Cos(angle) * this._radius;
17                 double y = Math.Sin(angle) * this._radius;
18                 //使用变换效果让控件旋转角度degree
19                 RotateTransform rotateTransform = new RotateTransform();
20                 rotateTransform.Angle = degree;
21                 rotateTransform.CenterX = 0;
22                 rotateTransform.CenterY = 0;
23                 child.RenderTransform = rotateTransform;
24                 //排列子对像
25                 child.Arrange(new Rect(mX + x, mY + y, child.DesiredSize.Width, child.DesiredSize.Height));
26                 //角度递增
27                 degree += degreeStep;
28             }
29             return finalSize;
30         }

  

  4)布局规则应用

  自定义的圆形布局空间已经实现了。接下来在XAML页面中应用该布局面板来进行布局。在下面所示的XAML代码中还使用到了Slider控件的ValueChanged属性动态改变半径。

 1 //MainPage.XAMl代码
 2
 3 <Page
 4     x:Class="Newdefine_panel.MainPage"
 5     xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
 6     xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
 7     xmlns:local="using:Newdefine_panel"
 8     xmlns:d="http://schemas.microsoft.com/expression/blend/2008"
 9     xmlns:mc="http://schemas.openxmlformats.org/markup-compatibility/2006"
10     mc:Ignorable="d">
11
12     <Grid Background="{ThemeResource ApplicationPageBackgroundThemeBrush}">
13         <Grid.RowDefinitions>
14             <RowDefinition Height="Auto"/>
15             <RowDefinition Height="*"/>
16         </Grid.RowDefinitions>
17         <Slider Grid.Row="0" Value="5" ValueChanged="Slider_ValueChanged_1"></Slider>
18         <local:CirclePanel x:Name="circlePanel" Radius="50" Grid.Row="1"  HorizontalAlignment="Center" VerticalAlignment="Center">
19             <TextBlock>Start here</TextBlock>
20             <TextBlock>TextBlock 1</TextBlock>
21             <TextBlock>TextBlock 2</TextBlock>
22             <TextBlock>TextBlock 3</TextBlock>
23             <TextBlock>TextBlock 4</TextBlock>
24             <TextBlock>TextBlock 5</TextBlock>
25             <TextBlock>TextBlock 6</TextBlock>
26             <TextBlock>TextBlock 7</TextBlock>
27         </local:CirclePanel>
28     </Grid>
29 </Page>

以下通过Slider控件的ValueChanged来动态给圆形布局控件半径赋值

1 //MainPage.XAML.cs代码
2 private void Slider_ValueChanged_1(object sender, RangeBaseValueChangedEventArgs e)
3 {
4     if (circlePanel != null)
5     {
6         circlePanel.Radius = e.NewValue * 10;
7     }
8 }

  最终得到的应用程序运行效果如图所示:

时间: 2024-08-08 23:40:16

读书笔记3:uwp布局原理与自定义布局设计的相关文章

读书笔记:《写给大家看的设计书》

读书笔记:<写给大家看的设计书> <写给大家看的设计书>这本书本来是买给孩子看的,孩子对板报.杂志.名片等设计很感 兴趣,想看点基础的设计类的书籍,就给她找了一本.书到手后,我随手翻了翻发现对于我制作PPT还是很有帮助的,对于非专业设计人员来说,掌握4条设计原 则确实可以让设计感觉到非常专业,这几条原则应用于网站的设计也是同样有效. 全书三大部分,共14章,第一部分(第1-8章)最有用,讲述四大设计原则,第二部分(第9-11章)讲字体设计,第三部分有点像附录. 第一章 约书亚树 有

Hadoop读书笔记(十二)MapReduce自定义排序

Hadoop读书笔记系列文章:http://blog.csdn.net/caicongyang/article/category/2166855 1.说明: 对给出的两列数据首先按照第一列升序排列,当第一列相同时,第二列升序排列 数据格式: 3 3 3 2 3 1 2 2 2 1 1 1 2.代码 SortApp.java package sort; import java.io.DataInput; import java.io.DataOutput; import java.io.IOExc

【读书笔记】《编译原理》第一章 引论

第一章 引论 第一章 引论 1 语言处理器 2 一个编译器的结构 3 程序设计语言发展历程 5 编译技术的应用 1.1 语言处理器 编译器compiler:将源程序翻译成目标程序,生成目标代码快速,错误诊断效果差. 解释器interpreter:用户提供源程序和输入,产生输出,较慢,错误诊断效果好. java语言处理:Java源程序->字节码bytecode->虚拟机解释执行 语言处理系统:源程序--预处理器preprocessor--经过预处理的源程序--编译器--目标汇编程序(便于输出调试

【转】Android AlertDialog自定义布局

原文网址:https://blog.csdn.net/u010694658/article/details/53022294 由于开发中经常使用弹框,然而系统自带的弹框太局限,也不太美观,经常不能满足开发需求,所以就只能自定义布局.其实自定义布局很简单,没不要写出来,但是如果不写一遍的,后面遇到的话就感觉又会忘记,所以在次记一小笔,仅记一个最简单的例子,可以举一反三.  直接上代码 public class MainActivity extends Activity implements OnC

《Effective C++》第4章 设计与声明(2)-读书笔记

章节回顾: <Effective C++>第1章 让自己习惯C++-读书笔记 <Effective C++>第2章 构造/析构/赋值运算(1)-读书笔记 <Effective C++>第2章 构造/析构/赋值运算(2)-读书笔记 <Effective C++>第3章 资源管理(1)-读书笔记 <Effective C++>第3章 资源管理(2)-读书笔记 <Effective C++>第4章 设计与声明(1)-读书笔记 <Eff

《微服务设计》读书笔记大纲

cha1:微服务的概念--<微服务设计>读书笔记 cha2:微服务架构师的职责--<微服务设计读书笔记> cha3:建模:确定服务的边界--<微服务设计>读书笔记 cha4:微服务集成--<微服务设计>读书笔记 服务的协作:服务间的消息传递--<微服务设计>读书笔记 cha5:拆分:分解单块系统--<微服务设计>读书笔记 cha6:部署:持续集成(CI)与持续交付(CD)--<微服务设计>读书笔记 cha7:测试--<

&lt;读书笔记&gt;软件调试之道 :问题的核心-修复后的反思

声明:本文档的内容主要来源于书籍<软件调试修炼之道>作者Paul Butcher,属于读书笔记.欢迎转载! -------------------------------------------------------------------------------------------------- 有时尽管修复设计的是一个孤立的代码区,但你还是需要大局观,在修复缺陷之后花时间反思一下! 一旦确定了错误的来源,就可以采取措施避免它再发生!有些情况下,只不过是告诉你未来在在这一方面要更加小心

Collection View 自定义布局(custom flow layout)

Collection view自定义布局 一般我们自定义布局都会新建一个类,继承自UICollectionViewFlowLayout,然后重写几个方法: prepareLayout():当准备开始布局时调用这个方法,可以在这里计算一些属性,比如cell的尺寸. layoutAttributesForElementsInRect(rect: CGRect) -> [UICollectionViewLayoutAttributes]?:在这里返回布局属性. 实例(比较简单的例子,实际开发中可以进行

Swift - 使用网格(UICollectionView)的自定义布局实现复杂页面

网格UICollectionView除了使用流布局,还可以使用自定义布局.实现自定义布局需要继承UICollectionViewLayout,同时还要重载下面的三个方法: 1 2 3 4 5 6 7 8 9 10 11 12 // 这个方法返回每个单元格的位置和大小 override func layoutAttributesForItemAtIndexPath(indexPath: NSIndexPath)     -> UICollectionViewLayoutAttributes! {