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

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

这里写图片描述

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

  • focusNodegit

    控制该控件是否有焦点,要使控件获取焦点,能够这样写:github

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

  • onKeybash

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

    由于onKey方法返回的参数是泛型,须要强转,以Android平台为例,具体作法以下:svg

    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