flutter tv开发之按键消息分发机制(下)

上一篇flutter tv开发之按键消息分发机制(上)说到,flutter基本控件自身是不支持按键操作的,我们需要找到一个支持按键交互的控件来装饰我们的基本组件,那就是RawKeyboardListener

这里写图片描述

绘制的控件只有被这个控件包裹,控件才支持按键事件。实例化该对象时,有3个参数是必须要传的:

  • focusNode

    控制该控件是否有焦点,要使控件获取焦点,可以这样写:

    FocusScope.of(context).requestFocus(focusNode);

  • onKey

    控件按键事件回调,该回调接口带一个参数,类型为RawKeyEvent,RawKeyEvent是一个抽象类,有一个抽象工厂方法,该方法接收android、ios和fuchsia等系统平台发送的按键数据,根据按键类型是KeyDown还是KeyUp,返回对应的继承该类的RawKeyDownEvent和RawKeyUpEvent子类对象。

    因为onKey方法返回的参数是泛型,需要强转,以Android平台为例,具体做法如下:

    onKey: (RawKeyEvent event) {
     if (event is RawKeyDownEvent && event.data is RawKeyEventDataAndroid) {
        RawKeyDownEvent rawKeyDownEvent = event;
        RawKeyEventDataAndroid rawKeyEventDataAndroid = rawKeyDownEvent.data;
        switch (rawKeyEventDataAndroid.keyCode) {
                          ……
          }
      }                    
    },
    
  • child

    目标控件,也即RawKeyBoardListener的孩子树。

对于TV来说,交互依赖于焦点显示,而焦点显示方式按照实现机制分为真焦点和假焦点。

所谓真焦点就是:焦点的变化体现在目标控件的状态变化上,比如控件背景色、大小随着焦点得失而动态改变。

对应的假焦点就是:焦点的变化不影响目标控件本身的属性,有一个专门负责显示焦点改变的控件,一般称为焦点框,随着焦点位置改变而执行平移动画。焦点框的偏移距离为获得焦点的控件坐标减去失去焦点的控件坐标,同时焦点框形状始终适应目标控件的大小,获得焦点的控件看起来像是被焦点框包裹着。

以tv launcher菜单UI为例,我们来具体分析如何使用flutter实现假焦点交互。

这里写图片描述

首先我们需要绘制一个发光的矩形框,发光这一点只需要找UI设计师提供一张特效图即可,矩形框要怎么绘制呢?我们先看看flutter源码里有没有这样的实现,仔细找一找,我们看到了RelativeRectTween

这里写图片描述

这个类有两个参数begin和end,分别对应动画开始和动画结束时框的状态,参数类型为RelativeRect,RelativeRect这个类负责绘制一个矩形边框,构造函数如下:

这里写图片描述

left、top、right、bottom分别表示边框距离屏幕左、上、右、下四个边界的间距。

我们实例化一个RelativeRectTween对象后,要给它绑定一个AnimationController,这个动画控制器用来控制动画的执行。要开始动画,调用AnimationController对象的forward()方法,同时要保证动画连续执行,需要这样写:

这里写图片描述

接下来是布局的处理,在Android中,我们知道,焦点框是绘制在焦点View顶层的,一般采用RelativeLayout或者FrameLayout布局。

在Flutter中,层叠布局实现的控件是StackStack控件的每一个子控件都是定位或不定位,定位的子控件是被Positioned控件包裹的。Stack控件本身包含所有不定位的子控件,其根据alignment定位(默认为左上角)。然后根据定位的子控件的toprightbottomleft属性将它们放置在Stack控件上。

这里写图片描述

具体焦点框和视图布局排列如下:

这里写图片描述

以上是假焦点交互的实现流程,下面我们再来看看真焦点交互如何实现,以按钮颜色随焦点变化而变化的效果为例:

  • 第一步,如上面分析,让按钮被RawKeyboardListener控件包裹,该控件负责处理按键事件和焦点控制,这里不赘述。

  • 第二步,定义按钮的颜色,我们创建一个颜色对象,负责按钮颜色显示:

  Color color0 = Colors.grey; //这里给一个灰色默认值

这里写图片描述

  • 第三步,监听焦点的变化

这里写图片描述

具体焦点处理就在changeColor0()这个方法里,我们根据绑定focusNode0对象的控件是否有焦点,来改变color0对象的值:

  _changeColor0() {
    print("focusNode0.hasFocus = ${focusNode0.hasFocus}");
    setState(() {
      if (focusNode0.hasFocus) {
        color0 = Colors.red;
      } else {
        color0 = Colors.grey;
      }
    });
  }

可以看到这个方法里,对color0的赋值操作放在setState()里面,这个setState()的作用是通知框架此对象的内部状态已更改,从而引起视图的重绘,如果只是仅仅改变控件的属性值,而不通知系统,是不会引起界面重绘的。

这里写图片描述

Flutter开发TV首先就要解决交互问题,相信通过博客的抛砖引玉,你一定能得到一些启发。

文中demo源码地址:https://github.com/coderJohnZhang/flutter_tv

猜你喜欢

转载自blog.csdn.net/johnwcheung/article/details/79462565