在 Kanzi Android framework (droidfw) 中观察状态变化和处理事件。

观察状态更改

您可以实现 KanziViewListener 来监听 KanziView 中的状态变化。使用此方法注入自定义初始化和反初始化逻辑。

例如:

  • 使用 KanziViewListener.onAttachedToWindow 回调来访问域,注册 Kanzi Engine Java 插件或手动设置视图的节点树。

    view.addListener(new KanziViewListener() {
       ...
    
       @Override
       public void onAttachedToWindow(View view, Domain domain)
       {
             // Register a plugin.
             domain.registerPlugin(new MyPlugin());
    
             // Manually setup the node tree.
             // Instantiate a node.
             ObjectRef<TextBlock2D> text = TextBlock2D.create(domain, "text");
             // Set as root of the view.
             ((KanziView) view).setRoot(text.get());
             // Set properties on a node.
             text.get().setProperty(TextBox2D.TextProperty, "Hello Kanzi!");
    
    view.addListener(object : KanziViewListener {
       ...
    
       override fun onAttachedToWindow(view: View, domain: Domain) {
             // Register a plugin.
             domain.registerPlugin(MyPlugin())
    
             // Manually setup the node tree.
             // Instantiate a node.
             val text = TextBlock2D.create(domain, "text")
             // Set as root of the view.
             (view as KanziView).root = text.get()
             // Set properties on a node.
             text.get().setProperty(TextBox2D.TextProperty, "Hello Kanzi!")
    
  • 使用 KanziViewListener.onStartupPrefabLoaded 回调来访问和修改您配置的启动预设实例化的节点树。

    view.addListener(new KanziViewListener() {
       ...
    
       @Override
       public void onStartupPrefabLoaded(View view, Domain domain)
       {
             // Look up a child node starting from the root.
             TextBox2D text = ((KanziView)view).getRoot().lookupNode("#Text");
             // Set properties on a node.
             text.setProperty(TextBox2D.TextProperty, "Hello world!");
    
    view.addListener(object : KanziViewListener {
       ...
    
       override fun onStartupPrefabLoaded(view: View, domain: Domain) {
             // Look up a child node starting from the root.
             val text = (view as KanziView).root.lookupNode<TextBox2D>("#Text")
             // Set properties on a node.
             text.setProperty(TextBox2D.TextProperty, "Hello world!")
    

要了解有关 Kanzi Engine Java 插件的更多信息,请参阅 创建 Kanzi Engine Java 插件。

处理输入

默认情况下,KanziSurfaceView 和 KanziTextureView 处理输入事件。如果您希望 Kanzi Engine 处理使用 KanziViewAdapter 的任意视图的事件,则必须手动转发此类输入事件。为此,请使用这些接口:

默认情况下,所有 Kanzi 视图将 focusableInTouchMode 设置为 True,除非通过编程或布局属性另行设置。这使得它们在被点击时获得并保持焦点。请参阅 Android touch mode。

处理来自 Android Automotive 旋转控制器的输入

默认情况下,KanziSurfaceView 和 KanziTextureView 处理旋转控制器输入。

旋转操作

Kanzi 如何处理这些操作

../../../_images/rotary-input-nudge.png
  • 微调向上

  • 微调向下

  • 微调左移

  • 右微调

Kanzi Android framework (droidfw) 将这些操作处理为相应的箭头键事件。

Kanzi 不支持并忽略来自对角微调的输入。

../../../_images/rotary-input-center.png

居中按钮

Kanzi Android framework (droidfw) 尝试为聚焦的 Kanzi 视图启用直接操作模式。如果 Kanzi Android framework (droidfw) 无法启用直接操作模式,它会将按键事件转发给 Kanzi Engine。Kanzi Engine 将旋转中心按钮的按键代码 KeyEvent.KEYCODE_DPAD_CENTER 转换为 Enter 逻辑按键。请参阅 Direct manipulation mode。

../../../_images/rotary-input-back.png

上一步按钮 (Back button)

Kanzi Android framework (droidfw) 将键事件转发到 Kanzi Engine。如果 Kanzi Engine 不消费该事件,Kanzi Android framework (droidfw) 将禁用 Kanzi 视图的直接操作模式。

../../../_images/rotary-input-rotate.png
  • 逆时针旋转

  • 顺时针旋转

Android 发送一个通用运动事件,其中包含旋转方向和按压次数。

Kanzi 视图将所有通用运动事件转发到 KanziViewAdapter.handleGenericMotionEvent。

KanziViewAdapter 确定事件是否来自旋转控制器,并检查 Kanzi 视图是否处于直接操作模式。如果 Kanzi 视图处于直接操作模式,Kanzi 视图将旋转信息转发给 Kanzi Engine。Kanzi Engine 翻译:

  • 顺时针旋转进入 Tab 键事件。

  • 逆时针旋转进入 BackTab 键事件。

要禁用默认的旋转控制器输入处理,使用以下方法之一:

  • 在布局文件中,为 Kanzi 视图元素,将 rotaryHandlingEnabled 属性设置为 false:

    <com.rightware.kanzi.KanziSurfaceView
        android:id="@+id/rotaryview"
        app:name="Rotary"
        app:kzbPathList="rotary.kzb"
        app:startupPrefabUrl="kzb://rotary/StartupPrefab"
        app:rotaryHandlingEnabled="false"/>
    
  • 在活动代码中,将 false 传递给 KanziViewAdapter.setRotaryHandlingEnabled 函数调用:

    protected void onCreate(Bundle icicle) {
        super.onCreate(icicle);
        setContentView(R.layout.activity_main);
        mView = findViewById(R.id.rotaryview);
        mView.getAdapter().setRotaryHandlingEnabled(false);
    }
    

处理方向更改

当屏幕方向发生变化时,Android 应用程序默认情况下会销毁并重新创建活动。这允许应用程序为纵向和横向屏幕使用备用布局,但这也意味着您会丢失所有内部 Kanzi 状态。

您可以通过在 AndroidManifest.xml 文件中添加 android:configChanges="orientation" 属性来覆盖此设置。使用此选项,活动在方向更改时不再被销毁,而是在活动上调用 onConfigurationChanged()。手动将此事件转发到 KanziView.handleOrientationChange。此选项避免重新创建 Kanzi 控件,因此保留了所有内部状态。请参阅 Android 配置更改。

处理 Android 生命周期

虽然 KanziSurfaceView 和 KanziTextureView 默认情况下处理窗口和表面事件,但使用 KanziViewAdapter 的任意视图必须通过 KanziViewAdapter.handleAttachedToWindow、KanziViewAdapter.handleSurfaceCreated、KanziViewAdapter.handleSurfaceChanged、KanziViewAdapter.handleSurfaceDestroyed 和 KanziViewAdapter.handleDetachedFromWindow 接口手动转发它希望 Kanzi 处理的窗口和表面事件。

处理视图的用户可见性

Kanzi Android framework (droidfw) 会自动持续跟踪每个已连接的视图对用户是否可见,并停止隐藏视图的 rendering 和输入处理。

要检查视图是否对用户可见,Kanzi Android framework (droidfw) 考虑:

  • 视图的可见性状态

  • 视图的所有祖先的可见性状态

  • 视图的 alpha 值

  • 通过 View.getGlobalVisibleRect() 计算的视图的有效可见部分。

使用 KanziViewAdapter 的任意视图必须手动转发可见性事件,以便通过 KanziViewAdapter.handleVisibilityChange 接口处理 Kanzi。

空闲状态

Kanzi Android framework (droidfw) 会在没有工作时自动暂停不必要的操作,并在有工作时唤醒。请参阅 应用程序空闲状态。

为了在暂停期间检测来自 Kanzi 主循环外部的更改,Kanzi Android framework (droidfw) 可以:

  • 订阅 Android MessageQueue.IdleHandler 事件。

  • 在 Android Choreographer 上运行轻量级任务。

默认情况下,Kanzi Android framework (droidfw) 订阅 Android 空闲事件,并在 Android 应用程序进入空闲状态时检查外部更改。在此模式下,Kanzi 在暂停期间的 CPU 使用率真正降为零。

您可以通过调用 KanziRuntime.enableSuspendedChangeTrackingTask 将 Kanzi 切换为使用后台任务。在此模式下,Kanzi 以通过 KanziRuntime.setSuspendedChangeCheckRate 设置的频率在 Android Choreographer 上运行轻量级任务。通过更改此频率,您可以控制 Kanzi 检查外部更改的频率。默认值为 0 时,Kanzi 以显示器 vsync 频率跟踪外部更改。您可以进一步降低此频率及相关的 CPU 消耗,但代价是在有工作时唤醒会延迟。此频率独立于 rendering 和动画的频率,后两者由帧率限制控制。