教程:Android 的数据源¶
在本教程中,您将学习如何为 Android 的 Kanzi 应用程序创建数据源。您将学习如何:
使用 Kanzi Java API 创建数据源。
使用 Android API 来支持数据源。
确保与 Kanzi Studio Preview 兼容,在这些情况下 Android API 不可用。
视频显示教程的结果。
本教程假设您熟悉开发 Android 应用程序、Java 编程语言,并且了解使用 Kanzi Studio 的基础知识。熟悉 Kanzi Studio 的最佳入门点是:
在开始本教程之前,确保将您的 Kanzi 开发环境为 Android 设置好。请参阅 使用 Kanzi 进行 Android 应用程序开发的要求。
要在 Android 虚拟设备中运行您在本教程中创建的应用程序,请设置虚拟设备:
API 级别为 29 或更高
内部存储至少为 8 GB。
获取教程¶
要获取本教程,请在 Kanzi Studio 的 Quick Start 窗口中点击 Examples。在 Android data source 教程旁点击 。
Kanzi Studio 会将教程下载到 <KanziWorkspace>/Tutorials 目录。您可以找到:
教程资产位于
<KanziWorkspace>/Tutorials/Android data source/Assets目录中。完成的教程位于
<KanziWorkspace>/Tutorials/Android data source/Completed目录中。
创建单向数据源¶
在本节中,您将创建一个数据源,您可以从 Kanzi 应用程序中读取该数据源。您将创建一个显示 Android 设备电池电量的小部件。
要创建单向数据源:
在 Kanzi Studio 中,创建一个项目。在 New Project 窗口中,设置:
Name 到 Android data source
Template 到 Android application with Java plugin
使用此模板开始构建 Kanzi Java API 数据源。
Android application with Java plugin 模板创建一个包含 Kanzi Engine Java 插件的基于 Kanzi Android 框架的 Kanzi Studio 项目。
当您想为 Android 平台创建应用程序,并计划广泛使用 Android API 和服务时,请使用 Kanzi Android framework (droidfw)。
Kanzi Android framework (droidfw) 是一个专用于开发 Android Kanzi 应用程序的框架。它公开了 Kanzi Java API,使您可以完全使用 Java 或 Kotlin 编写应用程序和插件代码。您不需要编写任何 C++ 或 JNI 代码,但仍然可以使用本机 Kanzi 插件。Kanzi Android framework (droidfw) 提供与 Android UI 的强大集成,包括对多个同时基于 Kanzi 的视图的支持,以及 Kanzi 和 Android UI 元素的灵活组合。Kanzi Android framework (droidfw) 与 Android Choreographer 集成,在 Android UI 线程中运行 Kanzi 主循环任务。这使您可以从应用程序代码中使用 Kanzi Java API,而无需调度机制。
在 Android Studio 中:
打开
Android data source/Application/configs/platforms/android_gradle项目。
在
androiddatasourceplugin>java>com.example.androiddatasourceplugin的JavaDataSource类中:添加一个常量来定义电池电量数据对象的名称。
static String BATTERY_LEVEL = "BatteryLevel";
在
initialize()方法中,替换现有的数据源设置。root.addChild(DataObjectInt.create(domain, "Integer value", 1).get()); root.addChild(DataObjectString.create(domain, "String value", "dummy value").get()); root.addChild(DataObjectReal.create(domain, "Real value", 0.5).get());
通过
root.addChild(DataObjectReal.create(domain, BATTERY_LEVEL, 0.5f).get());
在 Gradle 工具窗口中的 androiddatasource > androiddatasourceplugin > Tasks > kanzi 中,右键单击 exportJarDebug 或 exportJarRelease,然后选择 Run。
如果缺少 exportJarDebug,则需要在 Android Studio 中同步 Gradle 工程。
Android Studio 构建了您在 Kanzi Studio 中使用的 Kanzi Engine 数据源插件 JAR。
在 Kanzi Studio 中,创建内容以显示您在 Android Studio 中定义的数据源的值。
在 Node Tree 中,删除 Viewport 2D 节点。
您可以删除 Viewport 2D 节点,因为在本教程中您不会创建任何 3D 内容。
在 Rootpage 中,创建一个 Flow Layout 2D 节点并将其命名为 Controls。
在 Properties 中,添加并将 Horizontal Alignment 和 Vertical Alignment 属性设置为 Center。
在 Controls 节点中的 Node Tree 中,创建一个 Empty Node 2D 节点并将其命名为 Battery Widget。在 Properties 中,添加并设置:
Layout Width 到 150
Layout Height 到 200
从 Asset Packages > Factory Content 中,将 Progress Indicator 拖到 Node Tree 中的 Battery Widget 节点。
在 Node Tree 中,选择 Progress Indicator 预设占位符。在 Properties 中,添加并设置:
Layout Width 到 150
Layout Height 到 150
Ring Thickness 到 0.3
在 Battery Widget 节点中的 Node Tree 中,创建一个 Text Block 2D 节点。在 Properties 中,添加并设置:
Horizontal Alignment 到 Center
Vertical Alignment 到 Bottom
Text 到 Battery
在 Kanzi Studio 中,创建一个数据源并将其投入使用:
在 Library > Kanzi Engine Plugins 中,右键单击 Android_data_source 插件并选择 Update Kanzi Engine Plugin。
通过这种方式,您可以更新加载包含您在 Android Studio 插件项目中所做更改的插件。
要使插件更改生效,请重启 Preview。
在 Window 主菜单中,选择 Data Sources。
使用 Data Sources 窗口在您的项目中创建、设置和删除数据源,并将数据源中的数据对象连接到项目中的节点和资源。
在 Data Sources 窗口中,单击 Create Data Source 并创建一个具有以下设置的数据源:
Name 到 Android data source
Data Source Type 设置为 JavaDataSource
在 Data Sources 窗口中,您可以看到在 Android Studio 中定义的 BatteryLevel 数据对象。
在 Node Tree 中,选择 RootPage 节点。在 Properties 中,添加 Data Context > Data Context 属性,并将其设置为 Android data source。
通过设置 Data Context 属性,您可以告知应用程序从哪个数据源接收数据。当您为一个节点设置 Data Context 属性时,其所有子节点都会继承 Data Context 属性的值。如果您想为某个子节点使用不同的数据上下文,请在该节点中添加 Data Context 属性,并将其设置为您想要使用的数据上下文。
在 Node Tree 中,选择 Progress Indicator 节点。在 Properties 中,点击 + Add Binding,并在 Binding Editor 中设置:
Property 到 Progress
Expression 到
{DataContext.BatteryLevel} * 100单击 Save。
在 Kanzi Studio 中,选择 File > Export > Export KZB。
更新数据源以从 Android 系统 API 获取电池电量。
使用 Android API 的数据源会导致 Kanzi Studio Preview 在 Windows 上终止,因为 Android API 不可用。本教程中的方法展示了如何在 Android 上使用 Android API,并在您在 Kanzi Studio Preview 中使用插件时,仍然允许数据源返回占位符数据。
在 Android Studio 中,创建一个名为
DataProvider的接口。package com.example.androiddatasourceplugin; interface DataProvider { float getBatteryLevel(); }
要创建一个用于非 Android 平台的接口预设实现,请创建一个名为
DummyDataProvider的类。package com.example.androiddatasourceplugin; class DummyDataProvider implements DataProvider { @Override public float getBatteryLevel() { return 0.5f; } }
要创建应用程序在 Android 设备上运行时使用的接口的 Android 实现,请创建一个名为
AndroidDataProvider的类。package com.example.androiddatasourceplugin; import android.content.Context; import android.os.BatteryManager; class AndroidDataProvider implements DataProvider { private final BatteryManager mBatteryManager; public AndroidDataProvider(Object context) { Context androidContext = (Context) context; mBatteryManager = (BatteryManager) androidContext.getSystemService(Context.BATTERY_SERVICE); } @Override public float getBatteryLevel() { int batLevel = mBatteryManager.getIntProperty(BatteryManager.BATTERY_PROPERTY_CAPACITY); return ((float) batLevel) / 100.0f; } }
更新
JavaDataSource以使用新的提供程序。导入提供您要创建的内容功能的 Kanzi 类。
import com.rightware.kanzi.Platform;
为数据提供者添加一个数据成员:
private DataProvider mDataProvider;
在
initialize()方法中,紧接在super.initialize();之后添加此代码以选择数据提供者:// Construct the proper DataProvider for the current platform. if (Platform.isAndroid()) { mDataProvider = new AndroidDataProvider(getDomain().getPlatformContext()); } else { mDataProvider = new DummyDataProvider(); }
更新电池电量数据对象的定义以从接口中检索值:
root.addChild(DataObjectReal.create(domain, BATTERY_LEVEL, mDataProvider.getBatteryLevel()).get());
在 Android Studio 中,构建并运行 app。
当您在 Android 设备上运行应用程序时,电池小部件会显示该设备的当前电池电量。
创建双向数据源¶
在本节中,您将创建一个小部件,您可以使用它来控制Android设备的Wi-Fi连接状态。
要创建双向数据源:
在 Android Studio 中,更新数据源以包含 Wi-Fi 相关信息:
在
DataProvider接口中,添加:boolean isWifiEnabled(); boolean isNetworkConnected(); void setWifiEnabled(boolean enabled);
在
DummyDataProvider类中,添加:private boolean mWifiEnabled = false; @Override public boolean isWifiEnabled() { return mWifiEnabled; } @Override public boolean isNetworkConnected() { return mWifiEnabled; } @Override public void setWifiEnabled(boolean enabled) { mWifiEnabled = enabled; }
在
AndroidDataProvider类中:导入提供您要创建的内容功能的 Android 类。
import android.net.ConnectivityManager; import android.net.wifi.WifiManager; import android.net.NetworkCapabilities;
添加这些成员:
private final ConnectivityManager mConnectivityManager; private final WifiManager mWifiManager;
更改这些构造函数:
mConnectivityManager = (ConnectivityManager) androidContext.getSystemService(Context.CONNECTIVITY_SERVICE); mWifiManager = (WifiManager) androidContext.getSystemService(Context.WIFI_SERVICE);
添加这些方法:
@Override public void setWifiEnabled(boolean enabled) { mWifiManager.setWifiEnabled(enabled); } @Override public boolean isWifiEnabled() { return mWifiManager.isWifiEnabled(); } @Override public boolean isNetworkConnected() { NetworkCapabilities capabilities = mConnectivityManager.getNetworkCapabilities(mConnectivityManager.getActiveNetwork()); if (capabilities == null) return false; return capabilities.hasTransport(NetworkCapabilities.TRANSPORT_WIFI); }
在
JavaDataSource类中:导入提供您要创建的内容功能的 Kanzi 类。
import com.rightware.kanzi.DataObjectBool;
添加这些成员:
static String NETWORK_CONNECTED = "NetworkConnected"; static String WIFI_ENABLED = "WifiEnabled";
在
initialize()函数中更新数据源:root.addChild( DataObjectBool.create(domain, NETWORK_CONNECTED, mDataProvider.isNetworkConnected()).get()); ObjectRef<DataObjectBool> enabledRef = DataObjectBool.create(domain, WIFI_ENABLED, mDataProvider.isWifiEnabled()); root.addChild(enabledRef.get()); // Register a Notification handler to update Wi-Fi state when Data Source changes. DataObjectBool wifiEnabled = enabledRef.get(); wifiEnabled.addModifiedNotificationHandler(new ModifiedSubscriptionFunction() { @Override public void handle() { mDataProvider.setWifiEnabled(wifiEnabled.getValue()); } });
在 androiddatasourceplugin >
manifests>AndroidManifest.xml和 app >manifests>AndroidManifest.xml文件中,添加这些权限:<uses-permission android:name="android.permission.ACCESS_WIFI_STATE"/> <uses-permission android:name="android.permission.CHANGE_WIFI_STATE"/> <uses-permission android:name="android.permission.ACCESS_NETWORK_STATE" />
在 Gradle 工具窗口中的 androiddatasource > androiddatasourceplugin > Tasks > kanzi 中,右键单击 exportJarDebug 或 exportJarRelease,然后选择 Run。
Android Studio 构建了您在 Kanzi Studio 中使用的 Kanzi Engine 数据源插件 JAR。
在 Kanzi Studio 中,更新插件:
在 Library > Kanzi Engine Plugins 中,右键单击 AndroidDataSource 插件并选择 Update Kanzi Engine Plugin。
通过这种方式,您可以更新和加载包含您在 Android Studio 插件项目中所做更改的插件。
要使插件更改生效,请重启 Preview。
在 Data Sources 窗口中的 Kanzi Studio 中,单击 Refresh 按钮以从插件读取更新的数据源内容。
在 Kanzi Studio 中,为 Wi-Fi 小部件创建用户界面:
在 Node Tree 中,按 Alt 并右键单击 Controls 节点,创建一个 Empty Node 2D 节点,并将其命名为 Wi-Fi Widget。在 Properties 中,添加并设置:
Layout Width 到 150
Layout Height 到 200
Layout.Item > Horizontal Margin 属性 Left 属性字段到 40。
在 Node Tree 中,按 Alt 并右键单击 Wi-Fi Widget 节点,创建一个 Image 节点,并将其命名为 Icon Connected。在 Properties 中,添加并设置:
Image 到 + Import Image… 并选择
<KanziWorkspace>/Tutorials/Android data source/Assets/Wi-Fi-connected.png。Layout Width 到 150
Layout Height 到 150
在 Properties 中,添加绑定并设置:
Property 到 Visible
Expression 到
BOOL({DataContext.NetworkConnected})
单击 Save。
通过此绑定,当 NetworkConnected 数据对象的值为
true时,您可以使 Icon Connected 图像可见。
在 Node Tree 中,复制 Icon Connected 节点并将其重命名为 Icon Disconnected。
在 Node Tree 中,选择 Icon Disconnected 节点。在 Properties 中:
单击绑定,并在 Binding Editor 中将
!添加到绑定表达式的开头。单击 Save。
通过此绑定,当 NetworkConnected 数据对象的值为
false时,您可以使 Icon Disconnected 图像可见。
将 Image 属性设置为 + Import Image… 并选择
<KanziWorkspace>/Tutorials/Android data source/Assets/Wi-Fi-disconnected.png。
从 Asset Packages > Factory Content 中,将 Toggle Button 拖到 Node Tree 上,然后将其放在 Wi-Fi Widget 上。
您可以使用此切换按钮控制 Wi-Fi 连接。
在 Node Tree 中,选择 Toggle Button 节点。在 Properties 中:
创建绑定并设置:
Binding Mode 到 Two way
Property 到 Toggle State
Expression 到
{DataContext.WifiEnabled}
单击 Save。
通过此绑定,您可以在 Toggle Button 节点的切换状态和 WifiEnabled 数据对象之间创建双向连接。
添加 Vertical Alignment 属性并将其设置为 Bottom。
在 Kanzi Studio 中,选择 File > Export > Export KZB。
在 Android Studio 中,构建并运行 app。
当您在 Android 设备上运行应用程序时,Wi-Fi 小部件会显示指示该设备 Wi-Fi 连接状态的图标。
根据外部更改更新数据源 (Update data sources based on external changes)¶
在上一节中,您添加了在应用程序启动时从 Android 设备读取常量数据的功能。在本节中,您将设置数据源以响应 Android 回调,并在 Wi-Fi 或电池电量变化时更新数据源。
要根据外部更改更新数据源:
创建一个名为
DataChangeListener的接口。当数据发生变化时,该接口接受数据更改。
package com.example.androiddatasourceplugin; interface DataChangeListener { void updateBatteryLevel(float batteryLevel); void updateNetworkConnection(boolean isConnected); void updateWifiEnabled(boolean isEnabled); }
修改
AndroidDataProvider类以接收来自 Android 的更新数据。导入提供您要创建的内容功能的 Android 类。
import android.content.BroadcastReceiver; import android.net.Network; import android.content.Intent; import android.content.IntentFilter;
添加一个成员以保存对
DataChangeListener的引用。private final DataChangeListener mDataChangeListener;
修改
AndroidDataProvider构造函数以接收和存储DataChangeListener。public AndroidDataProvider(Object context, DataChangeListener listener) { mDataChangeListener = listener;
添加监听器回调以获取 Wi-Fi 状态和电池电量:
// Battery level listener private final BroadcastReceiver mBatteryReceiver = new BroadcastReceiver() { @Override public void onReceive(Context context, Intent intent) { int level = intent.getIntExtra(BatteryManager.EXTRA_LEVEL, -1); int scale = intent.getIntExtra(BatteryManager.EXTRA_SCALE, -1); float batteryLevel = level / (float) scale; mDataChangeListener.updateBatteryLevel(batteryLevel); } }; // Wi-Fi state listener that indicates whether Wi-Fi is enabled. private final BroadcastReceiver mWifiStateReceiver = new BroadcastReceiver() { @Override public void onReceive(Context context, Intent intent) { int wifiStateExtra = intent.getIntExtra(WifiManager.EXTRA_WIFI_STATE, WifiManager.WIFI_STATE_UNKNOWN); switch (wifiStateExtra) { case WifiManager.WIFI_STATE_ENABLED: mDataChangeListener.updateWifiEnabled(true); break; case WifiManager.WIFI_STATE_DISABLED: mDataChangeListener.updateWifiEnabled(false); break; } } }; // Network state listener that indicates whether Internet connection is available. private final ConnectivityManager.NetworkCallback mNetworkCallback = new ConnectivityManager.NetworkCallback() { @Override public void onAvailable(Network network) { super.onAvailable(network); NetworkCapabilities capabilities = mConnectivityManager.getNetworkCapabilities(network); if (capabilities != null) { mDataChangeListener.updateNetworkConnection(capabilities.hasTransport(NetworkCapabilities.TRANSPORT_WIFI)); } } @Override public void onLost(Network network) { super.onLost(network); mDataChangeListener.updateNetworkConnection(false); } };
要使用 Android 注册回调,请将此代码添加到
AndroidDataProvider构造函数中:// To get battery changes, register the battery receiver. androidContext.registerReceiver(mBatteryReceiver, new IntentFilter(Intent.ACTION_BATTERY_CHANGED)); // To get the Wi-Fi connectivity state changes, register the Wi-Fi state receiver. IntentFilter intentFilter = new IntentFilter(WifiManager.WIFI_STATE_CHANGED_ACTION); androidContext.registerReceiver(mWifiStateReceiver, intentFilter); // Listen to the connectivity state changes. mConnectivityManager.registerDefaultNetworkCallback(mNetworkCallback);
修改
JavaDataSource类以处理数据更新:更改类以实现
DataChangeListener接口。public class JavaDataSource extends DataSource implements DataChangeListener {
添加
DataChangeListener接口的实现。private boolean mUpdateInProgress = false; @Override public void updateBatteryLevel(float batteryLevel) { DataObjectReal batteryObject = (DataObjectReal) mRoot.get().lookupDataContext(BATTERY_LEVEL); if (batteryObject != null) { mUpdateInProgress = true; batteryObject.setValue(batteryLevel); mUpdateInProgress = false; } } @Override public void updateNetworkConnection(boolean isConnected) { DataObjectBool networkConnected = (DataObjectBool) mRoot.get().lookupDataContext(NETWORK_CONNECTED); if (networkConnected != null) { mUpdateInProgress = true; networkConnected.setValue(isConnected); mUpdateInProgress = false; } } @Override public void updateWifiEnabled(boolean isEnabled) { DataObjectBool wifiEnabled = (DataObjectBool) mRoot.get().lookupDataContext(WIFI_ENABLED); if (wifiEnabled != null) { mUpdateInProgress = true; wifiEnabled.setValue(isEnabled); mUpdateInProgress = false; } }
在
initialize()方法中,将this作为参数添加到AndroidDataProvider构造函数中。mDataProvider = new AndroidDataProvider(getDomain().getPlatformContext(), this);
在
initialize()方法中,修改现有的通知处理程序以添加mUpdateInProgress变量,以防止递归更新。wifiEnabled.addModifiedNotificationHandler(new ModifiedSubscriptionFunction() { @Override public void handle() { if (!mUpdateInProgress) { mDataProvider.setWifiEnabled(wifiEnabled.getValue()); } } });
在 Android Studio 中,构建并运行 app。
当您在 Android 设备上运行应用程序时,当网络连接状态发生变化时,Wi-Fi 小部件会更新。要测试这一点,可以通过设备用户界面启用和禁用网络连接,或者使用 Wi-Fi 小部件中的切换按钮。
备注
从 Android 10 (API 29) 开始,普通 Android 应用程序不再允许更改 Wi-Fi 状态。因此,Wi-Fi 小部件中的切换按钮在 Android 10 及更高版本的设备上无法工作。
接下来该做什么?¶
在本教程中,您学习了如何:
使用 Kanzi Java API 创建数据源。
使用 Android API 来支持数据源。
确保与 Kanzi Studio Preview 兼容,在这些情况下 Android API 不可用。
要详细了解如何使用 Kanzi Android framework (droidfw) 和 Java API,请参阅:
另请参阅¶
要了解有关为 Android 开发 Kanzi 应用程序的更多信息,请参阅 为 Android 开发 Kanzi 应用程序。
要了解有关使用 Java Kanzi Engine API 的更多信息,请参阅 使用 Java 和 Kotlin。