在上一篇中,老周用一个示例,演示了框架视图的创建过程,在本篇中,老周将给大伙伴们说一下 Composition 构建 UI 的一些“零件”。
UI Composition 有一个核心类——对,就是 Compositor 类,它是总生产车间,组成 UI 的各种元素都可以由它来创建,所以,你会看到,它公开了 K 个以 Create 开头的方法。我们在组建 UI 时所用到的各种元素都可以调用这些以 Create 打头的方法来创建。
要让用户在窗口上看到你所构建的 UI 元素,可视化树中至少需要一 Visual 对象。Visual 是一个基类,在组成可视化树时,我们有三种 Visual 可以用。
第一种是 ContainerVisual,它表示一容器,可以向它的 Children 集合中添加子元素,以构建复杂的可视化对象。
第二种是 SpriteVisual,它从 ContainerVisual 类派生,所以也支持添加子元素。Sprite 是“小仙女”的意思,表明这个类不仅仅是个容器,它自身就可以绘制可视化内容,所以这个类用得还是比较多的。它主要公开了 Brush 属性,可以使用各种画刷来绘制内容。比如,单种颜色填充的画刷,渐变画刷,绘制图形的画刷等。
第三种是 LayerVisual,它也是容器元素,它类似于在视图中创建一个图层,并可以在其中继续添加子元素。
要呈现多彩灿烂的内容,你需要画刷,因此,UI Compositon 提供各种用途的画刷。
1、单色画刷,只有一种颜色对对定可视化元素进行填充。
2、渐变画刷,可以设置多种颜色过度。
3、Drawing Surface ,这个强大,相当于一块自由画布,你可以在上面画各种东东。你可以画文本,画圆形,画线,画大象,画野鸭子。甚至你可以在上面画一个正在播放的视频。绘制代码是要用C++来写,不过我们可以结合 Win 2D 组件一起用,这个老周后面会绘出例子的。
4、效果画刷。这种画刷主要用来产生一些视觉效果,比如模糊、锐化、颜色叠加、反相等等。
有了可视化对象和画刷,我们基本上可以弄出很多东西来。不过,可能大伙伴们会想了,要是能模拟一些真实场景就更佳了。于是,灯光就出场了。灯光就是模拟我们现实世界中的各种光源,比如小灯泡发出的点光,手电筒照射的光,或者照射范围更大的环境光(如白炽灯)等。
1、SpotLight,这种光源有点像手电筒的光,发散出去后会产生一个锥形区域。
2、PointLight,类似于一盏小灯泡,光源是一个点,但它可以向四周照射。
3、DistantLight,这种光有点像汽车的远光灯(晚上开车时别乱开远光啊,会害死人的),也像太阳光。总之,这种光源照射面很大,而且光很强,传播距离远。
4、AmbientLight,这种光就像你家里,房间里安装的白炽灯,或者是新型的节能灯,白白的光,可以照亮整个屋子,即环境光。
老周这么一说,你一定会觉得没意思,都不知道灯光照起来是什么效果。别急,老周后面会给例子的,你会看到效果的。
有刷子,有灯泡,有画布,你大概觉得差不多了,可以画出很多大作了。是的,不过,要是你的大作能够动起来,是不是更生动了呢。比如你画了猪八戒,手里拿着个大西瓜,要是能让八戒动起来,尤其是嘴巴,一下一下地啃西瓜皮,那该多好。因此,你还需要动画。
1、关键帧动画。这个应该好懂,就是可以在某个时刻设置一个关键帧,然后关键帧之间会自动以时间为基准产生过度动画。
2、表达式。即使用一个计算公式来生成动画所需要的值。比如你要对可视化元素的不透明度进行动画处理,输入 Opacity * 0.5,表示每次的不透明度变化,最终值都是上一次计算结果的一半,比如,不透明度为 100%,进行动画后变成 50%,再进行一次动画后就成 25% …… 要注意的是,表达式产生的动画是不能控制时间的。
以上内容你不必太认真看,就当作常识,大致涉猎一下就 OK 了,后续的博文中,老周会逐个介绍的。接下来,我们要了解一下如何构建 UI 树。
咱们一起来动动手,学习编程一定要动手的(当然,动脑子是必须的),如果你能用脚打字,也可以动脚。
记得老周一一篇中说过的吧,我们要先实现自己的一个视图,即实现 IFrameworkView 接口。
class DemoView : IFrameworkView { …… }
然后,我们声明几个私有字段。
Compositor mCompositor = null; CoreWindow mWindow = null; SpriteVisual rootVisual = null;
要组装 UI 构件,我们需要一个 Compositor 类的实例,在这个例子中,我打算用 SpriteVisual 作为 UI 树的根,它既可以用画刷绘制内容,也可以添加子元素,正可谓是一物两用。
接着,进行初始化,此时我们可以实例化 Compositor 对象,或者实例化其他我们需要的东西。注意此时不要组建 UI 树,因为窗口还未初始化,此时建UI树会发生异常。
public void Initialize(CoreApplicationView applicationView) { mCompositor = new Compositor(); }
Load方法我们这里没啥要加载的,就留空吧。
public void Load(string entryPoint) { // 留着以后用来养金鱼 }
接下来是重点,SetWindow 方法,此时窗口已经可用,所以此时可以组建 UI 树了。
public void SetWindow(CoreWindow window) { mWindow = window; // 创建根元素 rootVisual = mCompositor.CreateSpriteVisual(); // 这个是重点,必须要有这个 target CompositionTarget target = mCompositor.CreateTargetForCurrentView(); // 指定 UI 根元素 target.Root = rootVisual; // 这时候UI元素上是空白的 // 为了能看到东西,我们画点东西上去 CompositionColorBrush backBrush = mCompositor.CreateColorBrush(Colors.Blue); rootVisual.Brush = backBrush; }
这里有一个很重要的东东,大伙要严重注意。在组建 UI 时,你必须调用 CreateTargetForCurrentView 方法创建一个 CompositionTarget 实例,它直接与当前的应用程序视图关联的,你必须创建一个该实例,然后再把 UI 的根元素赋值给 CompositionTarget 的 Root 属性。Root 属性引用的UI元素就作为整个视图的根。
而且,这个 target 在整个视图的生命周期内,你只能创建一次,如果设置了根元素后,你还调用 CreateTargetForCurrentView 方法的话,你会收到一条 DCOMPOSITION_ERROR_WINDOW_ALREADY_COMPOSED 错误,该错误提醒你,该可视化树已经组装过了,你不能再调用了。
CreateColorBrush 方法创建一个单色填充的画刷,这里我用的是蓝色。
在 Run 方法中开启消息循环。
public void Run() { mWindow.Dispatcher.ProcessEvents(CoreProcessEventsOption.ProcessUntilQuit); }
但是,以上代码漏了一句,所以一旦运行,你只能看到初始屏幕。如下图。
因为你还没有激活当前窗口,故你在 ProcessEvents 之前,要加上这一句。
public void Run() { mWindow.Activate(); mWindow.Dispatcher.ProcessEvents(CoreProcessEventsOption.ProcessUntilQuit); }
因为我用 mWindow 字段引用了当前窗口的实例(就是刚刚上面的 SetWindow 方法中),所以我直接调用 Activate 方法。如果你没有用变量存储当前窗口的实例,你还可以这样调用。
CoreWindow.GetForCurrentThread().Activate();
最后,在视图销毁时,打扫一下卫生。
public void Uninitialize() { rootVisual.Dispose(); mCompositor.Dispose(); mWindow = null; }
视图的逻辑弄完了,再实现一下 IFrameworkViewSource 接口。
class DemoViewSource : IFrameworkViewSource { public IFrameworkView CreateView() { return new DemoView(); } }
别忘了入口点。
class Program { static void Main() { CoreApplication.Run(new DemoViewSource()); } }
好,大功告成!按下【F5】键,你满怀信心地看着程序启动。结果傻眼了,FK!怎么一片空白的?
咋回事呢?是啊,咋回事呢。再检查一下代码,逻辑都对啊。其实,你之所以看不到东西,是因为可视化元素的大小默认是 0,所以,你要回到 SetWindow 方法,给可视化元素设一个 Size。
public void SetWindow(CoreWindow window) { mWindow = window; // 创建根元素 rootVisual = mCompositor.CreateSpriteVisual(); rootVisual.Size = new System.Numerics.Vector2(300f, 320f); …… }
如果必要,你还可以设置视图对象的偏移量,默认是 0、0、0,即位于窗口的左上角,注意这个坐标是三个值的,即XYZ三个轴,X和Y轴你都懂的,原点在左上角,X轴正方向往右,Y轴正方向往下。至于Z轴嘛,从屏幕里面向外,说白了就是正方向指着你。
可以设置一下偏移量。
public void SetWindow(CoreWindow window) { mWindow = window; // 创建根元素 rootVisual = mCompositor.CreateSpriteVisual(); rootVisual.Size = new System.Numerics.Vector2(300f, 320f); rootVisual.Offset = new System.Numerics.Vector3(0f, 20f, 5f); …… }
好了,一切的疑问都解开了。再运行一下,Good,效果出来了。
其实,你还可以让它带透明效果的,方法和 XAML 中一样。
public void SetWindow(CoreWindow window) { …… rootVisual = mCompositor.CreateSpriteVisual(); rootVisual.Size = new System.Numerics.Vector2(300f, 320f); rootVisual.Offset = new System.Numerics.Vector3(0f, 20f, 5f); rootVisual.Opacity = 0.5f; …… }
再看看效果。
有趣吧,你还可以对它进行旋转的。
rootVisual.RotationAngleInDegrees = 60f;
RotationAngle 属性设置的旋转角度是弧度角,要用角度进行设置,就用 RotationAngleInDegrees 属性。
你不过瘾的话,还可以调整整个三维坐标的方向。
rootVisual.Orientation = new System.Numerics.Quaternion(-15f, 5f, 2f, 1f);
然后就变成这个样子了。
怎么样,有意思吧。本篇就扯到这里,大伙伴只要知道UI元素的组装方法就可以了,至于说那些三维变量如何设置,这个可以网上查资料,或者自己摸索,三维方面的东西还是挺复杂,其实老周也不是很了解,写出来大家交流交流而已。