【Win 10 应用开发】UI Composition 札记(二):基本构件

在上一篇中,老周用一个示例,演示了框架视图的创建过程,在本篇中,老周将给大伙伴们说一下 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元素的组装方法就可以了,至于说那些三维变量如何设置,这个可以网上查资料,或者自己摸索,三维方面的东西还是挺复杂,其实老周也不是很了解,写出来大家交流交流而已。

原文地址:https://www.cnblogs.com/tcjiaan/p/7784024.html