Using the Kanzi views and view adapters

Adding a Kanzi view

Kanzi exposes the KanziSurfaceView and KanziTextureView view types derived from the Android SurfaceView and TextureView respectively.

../../../_images/classes.svg
  • KanziSurfaceView renders directly to a surface obtained from hardware composer, which makes its performance better. But it comes with the restriction that an application can render it only behind or on the top of the rest of the views in an Activity.

  • KanziTextureView renders to a texture like a regular view. This allows an application to mix its content freely with Android content and you can move, transform, animate, and even make it translucent.

See Android SurfaceView vs TextureView or Creating a transparent view.

You can declare a Kanzi view in the Android layout and inflate that view anywhere in the same way as a regular Android view.

<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
   xmlns:app="http://schemas.android.com/apk/res-auto">

   <com.rightware.kanzi.KanziSurfaceView
      android:id="@+id/view1"
      app:name="View1"
      app:kzbPathList="project1.kzb"
      app:startupPrefabUrl="kzb://project1/Prefabs/View1" />

Creating multiple views

You can activate multiple Kanzi-based views simultaneously within an app and access them across different Activities or Fragments.

Configuring a Kanzi view

These view types expose various configurations that you can assign directly from a layout.

Attribute

Use

Format

Default value

name

Name of the view.

string

Kanzi View

kzbPathList

Comma-separated list of kzb files. When set, Kanzi automatically loads these files when the view is attached. You can also pass a cfg file that contains a list of kzb files that you want to load.

string

null

startupPrefabUrl

URL of the startup prefab. When set, Kanzi asynchronously loads and instantiates this prefab as a child of the view.

string

null

clearColor

Color used to clear the surface.

color

0xff000000

clearEnabled

Flag to enable the clearing of the surface.

boolean

True

Alternatively, you can assign these configuration options with setters after inflation.

public class Activity1 extends FragmentActivity {

    @Override
    protected void onCreate(Bundle) {
        ...

        // Inflate the content from the layout.
        setContentView(R.layout.activity1);

        // Get the reference to the view.
        KanziView view = findViewById(R.id.view1);

        // Set the name.
        view.setName("View1");

        // Set the kzb paths
        view.setKzbPathList(new String[]{"project1.kzb"});

        // Set the startup prefab.
        view.setStartupPrefabUrl("kzb://project1/Prefabs/View1");
class CarMenu : FragmentActivity() {

    override fun onCreate(icicle: Bundle?) {
        ...

        // Inflate the content from the layout.
        setContentView(R.layout.activity1)

        // Get the reference to the view.
        val view: KanziView = findViewById(R.id.view1)

        // Set the name.
        view.setName("View1")

        // Set the kzb paths
        view.setKzbPathList(arrayOf("project1.kzb"));

        // Set the startup prefab.
        view.setStartupPrefabUrl("kzb://project1/Prefabs/View1")

It is also possible to explicitly load kzb files during the activity onCreate function.

@Override
protected void onCreate(Bundle) {
   ...

   // Acquire a reference to the KanziRuntime.
   mRuntimeRef = KanziRuntime.acquire(this);

   // Load the kzb.
   try {
       mRuntimeRef.get().loadKzb("project1.kzb");
   } catch (FileNotFoundException e) {
       Log.e(e.getMessage());
       return;
   }

   // Inflate the content from the layout.
   setContentView(R.layout.activity1);
override fun onCreate(icicle: Bundle?) {
   ...

   // Acquire a reference to the KanziRuntime.
   mRuntimeRef = KanziRuntime.acquire(this)

   // Load the kzb.
   try {
       mRuntimeRef.get().loadKzb("project1.kzb")
   } catch (FileNotFoundException e) {
       Log.e(e.getMessage())
       return
   }

   // Inflate the content from the layout.
   setContentView(R.layout.activity1)

Creating a transparent view

A KanziView can show transparent content that allows the content behind that view to be visible.

To create a transparent view:

  1. To enable transparency for a KanziView, edit the onCreate method of the view activity:

    • For KanziSurfaceView call the setZOrderOnTop and setFormat methods.

      @Override
      protected void onCreate(Bundle) {
          ...
      
          // Get reference to the view.
          KanziSurfaceView view = findViewById(R.id.view1);
      
          // Add transparency support to the view.
          view.setZOrderOnTop(true);
          view.getHolder().setFormat(PixelFormat.TRANSPARENT);
      
      override fun onCreate(icicle: Bundle?) {
          ...
      
          // Get reference to the view.
          val view = findViewById(R.id.view1);
      
          // Add transparency support to the view.
          view!!.setZOrderOnTop(true);
          view!!.getHolder().setFormat(PixelFormat.TRANSPARENT);
      
    • For KanziTextureView call the setOpaque method.

      @Override
      protected void onCreate(Bundle) {
          ...
      
          // Get reference to the view.
          KanziTextureView view = findViewById(R.id.view1);
      
          // Add transparency support to the view.
          view.setOpaque(false);
      
      override fun onCreate(icicle: Bundle?) {
          ...
      
          // Get reference to the view.
          val view = findViewById(R.id.view1);
      
          // Add transparency support to the view.
          view!!.setOpaque(false);
      
  2. In the Android layout file, update the KanziView clear color to have transparency.

    app:clearColor="@android:color/transparent"
    
  3. Edit the application.cfg to require a surface with alpha bits. See Color format.

    SurfaceBitsAlpha = 8
    
  4. In Kanzi Studio select the node that shows the transparent content and in the Properties set the Background Brush property to < No Brush >.

    ../../../_images/background-no-brush.png

Attaching Kanzi to an existing view

You can instantiate and attach a KanziViewAdapter to an existing Android SurfaceView or TextureView.

@Override
protected void onCreate(Bundle) {
   ...

   // Get reference to the view.
   SurfaceView view = findViewById(R.id.view1);

   // Instantiate an adapter and assign a view.
   mAdapter = new KanziViewAdapter(view, "View1");

   // Assign a startup prefab.
   view.setStartupPrefabUrl("kzb://project1/Prefabs/View1");
override fun onCreate(icicle: Bundle?) {
    ...

    // Get reference to the view.
    val view = findViewById(R.id.view1)

    // Instantiate an adapter and assign a view.
    mAdapter = KanziViewAdapter(view, "View1");

    // Assign a startup prefab.
    view!!.setStartupPrefabUrl("kzb://project1/Prefabs/View1");

Using Kanzi from view-less contexts

You can also instantiate and use a KanziViewAdapter without a view. For example, from a WallpaperService.

class WallpaperEngine1 extends WallpaperService.Engine {
    KanziViewAdapter mAdapter;

    @Override
    public void onCreate(SurfaceHolder) {
        ...
        // Instantiate an adapter.
        mAdapter = new KanziViewAdapter(getApplicationContext(), "WallpaperEngine1");

        // Assign a startup prefab.
        mAdapter.setStartupPrefabUrl("kzb://project1/Prefabs/Wallpaper");
private inner class WallpaperEngine1 : Engine() {
    private var mAdapter: KanziViewAdapter? = null

    override fun onCreate(surfaceHolder: SurfaceHolder) {
        ...
        // Instantiate an adapter.
        mAdapter = KanziViewAdapter(applicationContext, "WallpaperEngine1")

        // Assign a startup prefab.
        mAdapter!!.setStartupPrefabUrl("kzb://project1/Prefabs/Wallpaper")

Sharing resources between views

All views in the same app share a single KanziRuntime along with all the loaded kzb files and resources. KanziRuntime exposes the KanziRuntime.acquire interface to access the current instance or create a new instance, if it has not already been created.

Accessing the Kanzi Java and Kotlin API

When the node tree of your Kanzi view is initialized, Kanzi checks whether the root node of that node tree is a Screen node. If the root node is not a Screen node, Kanzi creates a Screen node and attaches the node tree to that Screen node. Kanzi uses the Screen node to handle input and focus in the Kanzi view.

../../../_images/node-tree.svg

In a Kanzi view, to get the child of the Screen node, use the KanziView.getRoot method. From this Root node, you have access to the node tree with Kanzi Java API.

// Get the Root.
Node root = view.getRoot();

// Look up a node using an alias.
Node button = root.lookupNode("#Button");

// Set a property.
button.setProperty(ButtonConceptMetadata.ToggleStateProperty, 0);

// Add a message handler.
button.addMessageHandler(ButtonConceptMetadata.ToggleStateMessage,
                           new Node.MessageSubscriptionFunction() {
                           @Override
                           public void handle(MessageArguments messageArguments) {
                              int toggleState = messageArguments.getArgument(ButtonConceptMetadata.ToggleStateProperty);
                              Log.i("Button State is " + toggleState);
                           }});
// Get the Root.
val root = view.getRoot();

// Look up a node using an alias.
val button = root.lookupNode("#Button");

// Set a property.
button!!.setProperty(ButtonConceptMetadata.ToggleStateProperty, 0);

// Add a message handler.
button.addMessageHandler(ButtonConceptMetadata.ToggleStateMessage,
    Node.MessageSubscriptionFunction { messageArguments ->
        val toggleState =
            messageArguments.getArgument(ButtonConceptMetadata.ToggleStateProperty)
        Log.i(TAG, "Button State is $toggleState")
    })

Because the Kanzi Android framework (droidfw) runs in the Android UI thread, it is safe to invoke the Kanzi Java API interfaces directly from your application code. If you need to use Kanzi from a different thread, you can post a task to the Android UI thread using View.post(Runnable), TaskDispatcher, or similar functionality.

To post a task to the Android UI thread using the task dispatcher:

// This example assumes that your application code has mechanisms to ensure the
// validity of references to the Domain and root Node. For example, you can do this
// by hooking into the lifecycle of the Kanzi view or to the lifetime of a Kanzi
// object.
TaskDispatcher taskDispatcher = getDomain().getTaskDispatcher();

// Submit a task from a non-UI thread.
taskDispatcher.submit(() -> {
    // Look up the Child Node in the node tree.
    Node2D node = rootNode.lookupNode("./Child Node");
    // Set the Visible property of the node.
    node.setProperty(Node2D.VisibleProperty, true);
});
// This example assumes that your application code has mechanisms to ensure the
// validity of references to the Domain and root Node. For example, you can do this
// by hooking into the lifecycle of the Kanzi view or to the lifetime of a Kanzi
// object.
val taskDispatcher = domain.taskDispatcher

// Submit a task from a non-UI thread.
taskDispatcher.submit {
    // Look up the Child Node in the node tree.
    val node = rootNode.lookupNode<Node2D>("./Child Node")
    // Set the Visible property of the node.
    node.setProperty(Node2D.VisibleProperty, true)
}

To learn more about Kanzi Java API, see Using Java and Kotlin.

Unloading Kanzi Engine

When there are no active views that contain Kanzi content, you can unload Kanzi Engine from the memory.

The KanziRuntime.Reference class performs reference counting on the active KanziRuntime instance. Each attached KanziView automatically keeps a KanziRuntime.Reference to KanziRuntime, which is closed only when the view gets detached.

When all the KanziRuntime.Reference instances have been closed the KanziRuntime is unloaded.

If the KanziRuntime has been unloaded, the next call to KanziRuntime.acquire initializes a new instance of KanziRuntime, requiring the reload of all kzb files and resources.