超级白板
  • iOS
  • Android : Java
  • Web
  • 产品简介
  • 下载
  • 快速开始
  • 体验 App
  • 基础功能
  • 进阶功能
  • 最佳实践
  • 常见错误码
  • 服务端 API v2
  • 客户端 API
  • 常见问题

创建超级白板

更新时间:2022-05-26 17:16

ZEGO 已统一环境概念,不再有正式/测试之分,2021-11-16 及之前在 ZEGO 控制台 创建项目的用户,请参考 测试环境废弃说明 进行 SDK 升级和调整相关代码。

概念解释

  • ZegoExpress-Video SDK:ZEGO 音视频互动 SDK,能够提供超级白板所需的实时信令传输的能力。超级白板 SDK 必须搭配此 SDK 使用。
  • 超级白板 SDK、ZegoSuperBoard SDK:均指提供 ZEGO 超级白板服务(ZegoSuperBoard) 的 SDK。
  • ZegoSuperBoardView:在代码实现过程中,开发者用于展示的白板视图。
  • ZegoSuperBoardSubView:ZegoSuperBoardView 的子集,开发者实际创建的 View。ZegoSuperBoardView 会自动呈现最新创建或通过 switchSuperBoardSubView 指定的 ZegoSuperBoardSubView。
  • 纯白板:指定宽、高和页数创建的白板,用户在指定的白板画布上进行实时绘制。
  • 文件白板:基于文件创建的白板,白板宽高和页数由文件决定,实现在文件上绘制图元的业务需求。

/Pics/WhiteboardView/sync.gif

前提条件

  • 由于 ZegoSuperBoard SDK 需要与配套的 ZegoExpress-Video SDK 搭配使用,如已集成过 ZegoExpress-Video SDK,需要删除旧包并参考本页面重新集成,避免 SDK 版本不匹配造成初始化失败。
  • 已在 ZEGO 控制台 创建项目,并申请有效的 AppID,详情请参考 控制台 - 项目管理 中的“项目信息”。

准备环境

在开始集成 ZegoSuperBoard SDK 前,请确保开发环境满足以下要求:

  • Android Studio 4.0 或以上版本。
  • Android 版本不低于 5.0 且支持音视频的 Android 设备。
  • Android 设备已经连接到 Internet。

集成 SDK

1 (可选)新建项目

此步骤以如何创建新项目为例,如果是集成到已有项目,可忽略此步骤。
  1. 打开 Android Studio,选择菜单 “File > New > New Project”。

  2. 选择项目类型 “Empty Activity”。

  3. 选择项目存储路径。

  4. 单击 “Finish” 完成新工程创建。

2 导入 SDK

ZegoSuperBoard SDK 基于 AndroidX 构建,因此被导入的项目也需要基于 AndroidX 构建。如果开发者的项目不满足该要求,请参考 迁移到 AndroidX 进行迁移。

  1. 请参考 下载 SDK 包,下载最新版本的 SDK,下载完成后进行解压。

  2. 分别将 zegoexpress.jar及对应 arm64-v8a/armeabi-v7a 文件夹、zegowhiteboardview.aar、zegodocsview.aar、zegosuperboard.aar 文件拷贝到开发者的项目目录 “app/libs/” 文件夹下。

  3. 添加 SDK 引用,进入到 “app” 目录,打开 “build.gradle” 文件。

  • 在 “defaultConfig” 节点添加 “ndk” 节点,指定支持的平台类型。
    ndk {
        abiFilters "armeabi-v7a", 'arm64-v8a'
    }
  • 在 “android” 节点添加 “sourceSets” 节点,指定 “libs” 所在目录。
    sourceSets {
        main {
            jniLibs.srcDirs = ['libs']
        }
    }
  1. 在 “app/build.gradle” 文件中的 dependencies 节点下添加以下代码:

    implementation fileTree(include: ['*.jar', '*.aar'], dir: 'libs')
  2. SDK 依赖以下模块,在 “app/build.gradle” 文件中的 dependencies 节点下添加以下代码:

    // 如果需要在纯java项目中集成,需要添加此依赖项
    implementation "org.jetbrains.kotlin:kotlin-stdlib-jdk8:1.4.30" // 文档编写时最新版本为1.4.30

示例代码中 “libs” 目录仅为举例,开发者可根据实际路径填写。

3 设置权限

添加权限声明,打开 "app/AndroidManifest.xml" 文件,添加如下内容:

    <!-- SDK 必须使用的权限 -->
    <uses-permission android:name="android.permission.ACCESS_WIFI_STATE" />
    <uses-permission android:name="android.permission.INTERNET" />
    <uses-permission android:name="android.permission.ACCESS_NETWORK_STATE" />
    <uses-permission android:name="android.permission.WRITE_EXTERNAL_STORAGE" />
    <uses-permission android:name="android.permission.READ_EXTERNAL_STORAGE" />

权限说明如下:

权限 必要性 权限说明 需要理由
INTERNET 必要权限 访问网络权限 SDK 基本功能都需在联网的情况下才可以使用。
ACCESS_WIFI_STATE 必要权限 获取当前 WIFI 状态权限 SDK 会根据网络状态的改变执行不同的操作。如当网络重连的时候,SDK 内部会将网络断开时的状态都恢复,用户不需做额外的操作。
ACCESS_NETWORK_STATE 必要权限 获取当前网络状态权限 SDK 会根据网络状态的改变执行不同的操作。如当网络重连的时候,SDK 内部会将网络断开时的状态都恢复,用户不需做额外的操作。
WRITE_EXTERNAL_STORAGE 必要权限 内置 SD 卡写权限 SDK 会将日志和相关配置文件保存在内置 SD 卡内。
READ_EXTERNAL_STORAGE 必要权限 内置 SD 卡读权限 SDK 会将日志和相关配置文件保存在内置 SD 卡内。

4 防止代码混淆

在 proguard-rules.pro 文件中,为 SDK 添加 -keep 类的配置,这样可以防止混淆 SDK 公共类名称。

-keep class im.zego.zegowhiteboard.**{*;}
-keep class im.zego.zegodocs.**{*;}
-keep class im.zego.superboard.**{*;}

实现超级白板

1 初始化 SDK

初始化 ZegoExpress-Video SDK

定义 SDK 引擎对象,调用 createEngine 接口,将申请到的 AppID 传入参数 “appID”,创建引擎单例对象。

如果需要注册回调,可将实现了 IZegoEventHandler 的对象传入参数 “eventHandler”。如果不需要注册回调,可将 “null” 传入参数 “eventHandler”,创建引擎后仍需要注册回调时可通过调用 setEventHandler 接口设置回调。

// 创建引擎,通用场景接入,并注册 self 为 eventHandler 回调
// 不需要注册回调的话,eventHandler 参数可以传 null,后续可调用 "setEventHandler:" 方法设置回调
ZegoEngineProfile profile = new ZegoEngineProfile();
profile.appID = ;  // 请通过官网注册获取,格式为:1234567890L
profile.scenario = ZegoScenario.GENERAL;  // 通用场景接入
profile.application = getApplication();
engine = ZegoExpressEngine.createEngine(profile, null);

初始化 ZegoSuperBoard SDK

使用 ZegoSuperBoardManagerinit 方法初始化 ZegoSuperBoard SDK。

如果回调 onInit 中的 errorCode 为 ZegoSuperBoardError.ZegoSuperBoardSuccess,代表初始化成功,可进行更多操作。errorCode 可参考 常见错误码

// 配置superBoard初始化所需要的appID,token和UserID
ZegoSuperBoardInitConfig config = new ZegoSuperBoardInitConfig();
config.appID = appID; //赋值 appID
config.token = token; //赋值 token
config.userID = userID; //赋值 userID

// 调用SuperBoardManager初始化SuperBoard sdk
// this 为Android的Application上下文,因此此段代码建议放在Application中实现
ZegoSuperBoardManager.getInstance().init(this, config, new IZegoSuperBoardInitCallback() {
    @Override
    public void onInit(int errorCode) {
        Log.d(TAG, "init ZegoSuperBoardManager result: errorCode = [" + errorCode + "]");
        if (errorCode == ZegoSuperBoardError.ZegoSuperBoardSuccess) {
            /** 初始化成功 */
        } else {
            /** 初始化失败 */
        }
    }
});

需要确保 ZegoExpress-Video SDKZegoSuperBoard SDK 均初始化成功,即调用了各自的 init() 方法并在回调中返回的errorCode等于ZegoSuperBoardError.ZegoSuperBoardSuccess,才能够执行后续的接口调用。

2 监听事件回调

根据实际应用需要,在初始化 SuperBoard 后监听想要关注的事件回调,比如错误提醒、远端新增白板文件、远端删除白板文件、远端切换白板文件等。

SuperBoard 自动实现了多端同步能力,远端通知回调内只需刷新本地UI逻辑即可。

// 常用的 SuperBoard 相关回调
// SuperBoard 自动实现了多端同步能力,远端通知回调内只需刷新本地UI逻辑即可。
ZegoSuperBoardManager.getInstance().setManagerListener(new IZegoSuperBoardManagerListener() {
    @Override
    public void onError(int errorCode) {
        //SDK 抛出的错误码。可以根据错误码提示用户一些异常情况
    }

    @Override
    public void onRemoteSuperBoardSubViewAdded(@NonNull ZegoSuperBoardSubViewModel subViewModel) {
        //远端新增白板。可以根据model里的信息更新展示的UI内容,例如白板名称等
    }

    @Override
    public void onRemoteSuperBoardSubViewRemoved(@NonNull ZegoSuperBoardSubViewModel subViewModel) {
        //远端删除白板通知
        //收到通知后可以根据当前 ZegoSuperBoardManager.getInstance().getSuperBoardView().getCurrentSuperBoardSubView() 的信息更新UI,如更新当前显示白板名称。
    }

    @Override
    public void onRemoteSuperBoardSubViewSwitched(@NonNull String uniqueID) {
        //远端切换白板的通知
        //收到通知后可以根据当前 ZegoSuperBoardManager.getInstance().getSuperBoardView().getCurrentSuperBoardSubView() 的信息更新UI,如更新当前显示白板名称。
    }

});

3 登录房间

调用 ZegoExpressEngineloginRoom 接口登录房间。登录时,请传入本文 前提条件 所获取到的 Token 进行鉴权。

  1. 需保证 “roomID” 信息的全局唯一。
  2. “userID” 与 “userName” 不能为 “null” 否则会导致登录房间失败。
  3. ZegoUser 的构造方法 public ZegoUser(String userID) 会将 “userName” 设为与传的参数 “userID” 一样。
  4. 每个 “userID” 必须唯一,建议设置成一个有意义的值,开发者可将 “userID” 与自己业务账号系统进行关联。
  5. 临时 Token 仅供调试,正式上线时,请从开发者的业务服务器生成 token,详情可参考 使用 Token 鉴权。如果 Token 错误,请参考 错误码 文档中的 1002067 和 1003072 排查问题。

错误码详情请参考 登录房间错误码

//登录房间
void loginRoom() {
    // ZegoUser 的构造方法 public ZegoUser(String userID) 会将 “userName” 设为与传的参数 “userID” 一样。“userID” 与 “userName” 不能为 “null” 否则会导致登录房间失败。
    ZegoUser user = new ZegoUser("user2");

    ZegoRoomConfig roomConfig = new ZegoRoomConfig();
    //token 由用户自己的服务端生成,为了更快跑通流程,可以通过即构控制台 https://console.zego.im/ 获取临时的音视频 token
    roomConfig.token = ;
    // 只有传入 “isUserStatusNotify” 参数取值为 “true” 的 ZegoRoomConfig,才能收到 onRoomUserUpdate 回调。
    roomConfig.isUserStatusNotify = true;

    // roomID 由您本地生成,需保证 “roomID” 全局唯一。不同用户要登陆同一个房间才能进行通话
    String roomID = "room1";

    // 登录房间
    engine.loginRoom(roomID, user, roomConfig, (int error, JSONObject extendedData)->{
        // 登录房间结果,如果仅关注登录结果,关注此回调即可
        if (error == 0) {
            // 登录成功
            Toast.makeText(this, "登录成功", Toast.LENGTH_LONG).show();
        } else {
            // 登录失败,请参考 errorCode 说明 https://doc-zh.zego.im/article/4378
            Toast.makeText(this, "登录失败,请参考 errorCode 说明 https://doc-zh.zego.im/article/4378", Toast.LENGTH_LONG).show();
        }
    });
}

Token 过期更新

监听 Token 过期通知,如果 Token 过期,需要主动更新 Token。

engine.setEventHandler(new IZegoEventHandler() {
    @Override
    public void onRoomTokenWillExpire(String roomID, int remainTimeInSecond) {
        //token即将过期,去服务端获取新的token
        String token = getTokenFromServer();
        engine.renewToken(roomID, token);
        //这里需要同步更新文件token
        ZegoSuperBoardManager.getInstance().renewToken(token);
    }
}

要判断登录房间是否成功,需要监听onRoomStateUpdate回调。如果 ZegoRoomState 等于 ZegoRoomState.CONNECTED,表示登录房间成功,才能够执行后续的接口调用。

// 创建白板前需要保证登录成功,即房间回调状态为 ZegoRoomState.CONNECTED
engine.setEventHandler(new IZegoEventHandler() {
    /** 以下为常用的房间相关回调 */

    /** 房间状态更新回调 */
    @Override
    public void onRoomStateUpdate(String roomID, ZegoRoomState state, int errorCode, JSONObject extendedData) {
        /** 根据需要实现事件回调 */
        if (state == ZegoRoomState.CONNECTED) {
            // 登录房间成功,需要在登录成功之后才可以创建白板
            // 可以将创建白板的代码放在这里,确保登录房间后创建白板,也可以通过其他方法保证登录成功后再创建白板
        }
    }
});

4 添加白板视图

在登录房间之后,将 ZegoSuperBoardView 直接添加到您的业务场景视图中。示例代码如下:

ZegoSuperBoardView superBoardView = ZegoSuperBoardManager.getInstance().getSuperBoardView();
if (superBoardView != null) {
    // 添加 ZegoSuperBoardView 到指定视图中(以 Demo 中的 boardContainer 视图容器为例)。
    boardContainer.addView(superBoardView, new FrameLayout.LayoutParams(
            FrameLayout.LayoutParams.MATCH_PARENT,
            FrameLayout.LayoutParams.MATCH_PARENT,
            Gravity.CENTER));
}

5 创建白板

超级白板支持创建纯白板和文件白板。

  • 纯白板:指定宽、高和页数创建的白板,用户在指定的白板画布上进行实时绘制。
  • 文件白板:基于文件创建的白板,白板宽高和页数由文件决定,实现在文件上绘制图元的业务需求。

创建白板前需要保证登录成功,建议可在登录成功的回调中调用创建纯白板或文件白板的接口。

// 创建白板前需要保证登录成功,即房间回调状态为 ZegoRoomState.CONNECTED
engine.setEventHandler(new IZegoEventHandler() {
    /** 以下为常用的房间相关回调 */

    /** 房间状态更新回调 */
    @Override
    public void onRoomStateUpdate(String roomID, ZegoRoomState state, int errorCode, JSONObject extendedData) {
        /** 根据需要实现事件回调 */
        if (state == ZegoRoomState.CONNECTED) {
            // 登录房间成功,需要在登录成功之后才可以创建白板
            // 可以将创建纯白板或文件白板的代码放在这里,确保登录房间后创建白板,也可以通过其他方法保证登录成功后再创建白板
        }
    }
});
  • 创建纯白板
// 创建白板需要构造 ZegoCreateWhiteboardConfig 配置类,具体字段解释如下
ZegoCreateWhiteboardConfig config = new ZegoCreateWhiteboardConfig();
// 白板名称
config.name = "一个测试白板";
// 白板页数
config.pageCount = 5;
// 一页白板的宽度
config.perPageWidth = 960;
// 一页白板的高度
config.perPageHeight = 540;

ZegoSuperBoardManager.getInstance().createWhiteboardView(config, new IZegoSuperBoardCreateCallback() {
    @Override
    public void onViewCreated(int errorCode, @Nullable ZegoSuperBoardSubViewModel subViewModel) {
        Log.d(TAG, "createWhiteboardView() called with: errorCode = [" + errorCode + "], subViewModel = [" + subViewModel + "]");
        if (errorCode == ZegoSuperBoardError.ZegoSuperBoardSuccess) {
            /** 创建成功 */
        } else {
            /** 创建失败 */
        }
    }
});
  • 创建文件白板

创建文件白板前需要先获取文件的 fileID,可参考 共享文件管理 进行上传。

// 创建文件需要构造 ZegoCreateFileConfig 配置类
ZegoCreateFileConfig config = new ZegoCreateFileConfig();
// 文件的fileID,在上传文件成功后可以拿到
config.fileID = fileID;
ZegoSuperBoardManager.getInstance().createFileView(config, new IZegoSuperBoardCreateCallback() {
    @Override
    public void onViewCreated(int errorCode, @Nullable ZegoSuperBoardSubViewModel subViewModel) {
        Log.d(TAG, "createFileView() called with: errorCode = [" + errorCode + "], subViewModel = [" + subViewModel + "]");
        if (errorCode == ZegoSuperBoardError.ZegoSuperBoardSuccess) {
            /** 创建成功 */
        } else {
            /** 创建失败 */
        }
    }
});
  • 一个房间内最多可创建 50 个白板,房间内已存在 50 个白板时再创建白板会失败。
  • 请通过 querySuperBoardSubViewList 获取房间内当前的白板数量。

6 验证白板创建

使用多台设备运行上述项目,登录同一房间ID。用手指在任一设备的 ZegoSuperBoardView 的范围内按下移动,即可看到涂鸦效果展示在各个设备 ZegoSuperBoardView 上。

7 销毁白板

//销毁后SDK内部会自动切换到另外一个白板。展示的白板为销毁白板的上一个

//subViewModel 是白板共享列表 ZegoSuperBoardManager.getInstance().getSuperBoardSubViewModelList() 中的一个model
ZegoSuperBoardManager.getInstance().destroySuperBoardSubView(uniqueId, new IZegoSuperBoardDestroyCallback() {
    @Override
    public void onViewDestroyed(int errorCode) {
        Log.d(TAG, "onViewDestroyed() called with: errorCode = [" + errorCode + "]");
        if (errorCode == ZegoSuperBoardError.ZegoSuperBoardSuccess) {
            /** 销毁成功 */
        } else {
            /** 销毁失败 */
        }
    }
});