语聊组件

组件概述

语音聊天室的核心控件(SeaGridView) 是专为直播场景设计的核心控件,提供了一系列强大的 API 功能,帮助开发者快速实现语音聊天室功能。通过该控件,您可以轻松开启或关闭语音聊天室,并高效管理直播间内的麦位操作,包括申请上麦、邀请上麦、移动麦位以及踢人下麦等。借助 SeaGridView,您能够为应用快速集成语音聊天室模块,显著提升用户体验,同时降低开发成本。

开发环境要求

Android
iOS
Flutter
Android 5.0(SDK API Level 21)及以上版本。
Gradle 7.0 及以上的版本。
Android 5.0 及以上的手机设备。
环境配置或编译运行期间,如有问题,请参见 常见问题
Xcode 15 及以上。
iOS 13.0 及以上。
CocoaPods 环境安装,点击查看
如果您的接入和使用中遇到问题,请参见 常见问题
平台
版本
Flutter
Flutter 3.27.4 及更高版本。
Dart 3.6.2 或更高的版本。
Android
Android Studio 3.5及以上版本。
Android 5.0 及以上版本的 Android 设备。
iOS
Xcode 15.0及以上版本。
请确保您的项目已设置有效的开发者签名。

步骤1:开通服务

请参见 开通服务(TUILiveKit),领取体验版或者开通付费版。

步骤2:集成与配置

Android
iOS
Flutter
1. 在 app 目录下找到build.gradle.kts(或build.gradle)文件,并在其中增加如下代码,加入对 SeatGridView 组件的依赖:
build.gradle.kts
build.gradle
api("io.trtc.uikit:live-stream-core:latest.release")
api 'io.trtc.uikit:live-stream-core:latest.release'
2. 由于我们在 SDK 内部使用了Java 的反射特性,需要将 SDK 中的部分类加入不混淆名单,因此需要您在proguard-rules.pro文件中添加如下代码:
-keep class com.tencent.** { *; }
-keep class com.trtc.uikit.livekit.voiceroomcore.** { *; }
-keep class com.google.gson.** { *;}
3. 在 app 目录下找到AndroidManifest.xml 文件,在 application 节点中添加 tools:replace="android:allowBackup" 和android:allowBackup="false",覆盖组件内的设置,使用自己的设置。
// app/src/main/AndroidManifest.xml
<application
...
// 添加如下配置覆盖 依赖的 sdk 中的配置
android:allowBackup="false"
tools:replace="android:allowBackup">
使用 CocoaPods 导入组件,如果您遇到问题,请先参见 环境准备。导入组件具体骤如下:
1. 请在您的 Podfile 文件中添加 pod 'LiveStreamCore' 依赖。
Swift
target 'xxxx' do
...
...
pod 'LiveStreamCore'
end
如果您没有Podfile 文件,首先终端cdxxxx.xcodeproj目录,然后通过以下命令创建:
pod init
2. 在终端中,首先cdPodfile目录下,然后执行以下命令,安装组件。
pod install
如果无法安装 SeatGridView 最新版本,可以先删除Podfile.lockPods。然后执行以下命令更新本地的 CocoaPods 仓库列表。
pod repo update
之后执行以下命令,更新组件库的 Pod 版本。
pod update
3. 可以先编译运行一下,如果遇到问题,请参见 常见问题。问题如果依然无法解决,可以先去跑一下我们的 Example工 程。您在接入和使用过程中遇到的任何问题,欢迎给我们 反馈
在工程的根目录下,通过命令行执行以下命令安装组件 live_stream_core 插件。
flutter pub add live_stream_core
您在接入和使用过程中遇到的任何问题,欢迎给我们 反馈

步骤3:登录

Android
iOS
Flutter
在您的项目中添加如下代码,它的作用是通过调用 TUICore 中的相关接口完成 TUI 组件的登录。这一步骤至关重要,只有在成功登录之后,您才能正常使用 SeatGridView 提供的各项功能。
Kotlin
Java
TUIRoomEngine.login(applicationContext,
1400000001, // 请替换为步骤一取到的 SDKAppID
"denny", // 请替换为您的 UserID
"xxxxxxxxxxx", // 您可以在控制台中计算一个 UserSig 并填在这个位置
object : TUIRoomDefine.ActionCallback() {
override fun onSuccess() {
Log.i(TAG, "login success")
}

override fun onError(errorCode: Int, errorMessage: String) {
Log.e(TAG, "login failed, errorCode: $errorCode msg:$errorMessage")
}
})
TUIRoomEngine.login(context,
1400000001, // 请替换为步骤一取到的 SDKAppID
"denny", // 请替换为您的 UserID
"xxxxxxxxxxx", // 您可以在控制台中计算一个 UserSig 并填在这个位置
new TUIRoomDefine.ActionCallback() {
@Override
public void onSuccess() {
Log.i(TAG, "login success");
}

@Override
public void onError(TUICommonDefine.Error error, String message) {
Log.e(TAG, "login failed, errorCode: " + errorCode + " msg:" + errorMessage);
}
});
在您的项目中添加如下代码,它的作用是通过调用 RTCRoomEngine 中的登录相关接口完成 TUI 组件的登录。这一步骤至关重要,只有在成功登录之后,您才能正常使用 SeatGridView 提供的各项功能。
swift
//
// AppDelegate.swift
//

import RTCRoomEngine

func application(_ application: UIApplication, didFinishLaunchingWithOptions launchOptions: [UIApplication.LaunchOptionsKey: Any]?) -> Bool {
TUIRoomEngine.login(sdkAppId: 1400000001, // 请替换为步骤一取到的 SDKAppID
userId: "denny", // 请替换为您的 UserID
userSig: "xxxxxxxxxxx") { // 您可以在控制台中计算一个 UserSig 并填在这个位置
print("login success")
} onError: { code, message in
print("login failed, code: \(code), error: \(message ?? "nil")")
}
return true
}
在您的项目中添加如下代码,它的作用是通过调用 RTCRoomEngine 中的登录相关接口完成 TUI 组件的登录。这一步骤至关重要,只有在成功登录之后,您才能正常使用 LiveStreamCore 提供的各项功能。
final result = await TUIRoomEngine.login(
'Replace with your activated SDKAppID',
'Replace with your userId',
'Replace with your userSig');
参数说明
这里详细介绍一下 login 函数中所需要用到的几个关键参数:
参数
类型
说明
SDKAppID
int
步骤1 中的第3步中获取。
UserID
String
当前用户的 ID,字符串类型,只允许包含英文字母(a-z 和 A-Z)、数字(0-9)、连词符和下划线。
userSig
String
使用 步骤1 的第3步中获取的 SecretKey 对 SDKAppID、UserID 等信息进行加密,就可以得到 UserSig,它是一个鉴权用的票据,用于腾讯云识别当前用户是否能够使用 TRTC 的服务。您可以通过控制台中的 辅助工具 生成一个临时可用的 UserSig。更多信息请参见 如何计算及使用 UserSig
说明:
开发环境:如果您正在本地开发调试阶段,可以采用本地 GenerateTestUserSig.genTestSig函数生成 userSig。该方法中 SDKSecretKey 很容易被反编译逆向破解,一旦您的密钥泄露,攻击者就可以盗用您的腾讯云流量。
生产环境:如果您的项目要发布上线,请采用 服务端生成 UserSig 的方式。

步骤4:使用核心控件实现语音聊天室功能

房主开启语音聊天室并上麦

效果预览

房主开启语音聊天室&上麦




创建核心控件

Android
iOS
Flutter
您可以在您 Activity 中通过 java 代码或者 xml 方式加载我们的核心控件,其中代码方式示例如下(XML 方式也类似):
kotlin
java
val seatGridView = SeatGridView(this)
SeatGridView seatGridView = new SeatGridView(this);
swift
import LiveStreamCore

let seatGridView = SeatGridView()
您需要先创建一个控制器 SeatGridController ,然后再将其赋值给语音聊天室核心部件 SeatGridWidget
SeatGridController 负责提供API, SeatGridWidget 用于展示麦位UI。您可将 SeatGridWidget 添加在您需要展示麦位 UI 的任何地方。
final controller = SeatGridController();
SeatGridWidget(controller: controller);

主播开启语音聊天室并上麦

开启一个语音聊天室,并将本地麦克风采集的数据推流到直播间。
Android
iOS
Flutter
kotlin
java
val roomInfo = TUIRoomDefine.RoomInfo()
roomInfo.roomId = Config.roomId
roomInfo.seatMode = TUIRoomDefine.SeatMode.APPLY_TO_TAKE
seatGridView.startVoiceRoom(roomInfo, object : TUIRoomDefine.GetRoomInfoCallback {
override fun onSuccess(roomInfo: TUIRoomDefine.RoomInfo) {
val seatIndex = 1
val timeout = 60
seatGridView.takeSeat(seatIndex, timeout, null)
seatGridView.startMicrophone(null)
}

override fun onError(error: TUICommonDefine.Error, message: String) {
}
})
TUIRoomDefine.RoomInfo roomInfo = new TUIRoomDefine.RoomInfo();
roomInfo.roomId = "roomId_123456";
roomInfo.seatMode = TUIRoomDefine.SeatMode.APPLY_TO_TAKE;
seatGridView.startVoiceRoom(roomInfo, new TUIRoomDefine.GetRoomInfoCallback() {
@Override
public void onSuccess(TUIRoomDefine.RoomInfo roomInfo) {
int seatIndex = 1;
int timeout = 60;
seatGridView.takeSeat(seatIndex, timeout, null);
seatGridView.startMicrophone(null);
}

@Override
public void onError(TUICommonDefine.Error error, String message) {
}
});
swift
import LiveStreamCore
import RTCRoomEngine

let roomInfo = TUIRoomInfo()
roomInfo.roomId = "123456"
roomInfo.seatMode = .applyToTake

seatGridView.startVoiceRoom(roomInfo: roomInfo) { roomInfo in
} onError: { code, message in
}

seatGridView.startMicrophone() {
} onError: { code,message in
}
import 'package:live_stream_core/live_stream_core.dart';
import 'package:rtc_room_engine/rtc_room_engine.dart';

final roomInfo = TUIRoomInfo(roomId: 'replace with your roomId');
roomInfo.name = 'replace with your roomName';
roomInfo.seatMode = TUISeatMode.applyToTake;
roomInfo.isSeatEnabled = true;
roomInfo.roomType = TUIRoomType.livingRoom;
final startVoiceRoomResult = await controller.startVoiceRoom(roomInfo);

final startMicrophoneResult = await controller.startMicrophone();

观众加入语音聊天室

效果预览

观众加入语音聊天室



Android
iOS
Flutter
kotlin
java
seatGridView.joinVoiceRoom("roomId_123456", null)
seatGridView.joinVoiceRoom("roomId_123456", null);
swift
import LiveStreamCore
import RTCRoomEngine

seatGridView.joinVoiceRoom(roomId: "roomId_123456") { roomInfo in
} onError: { code, message in
}
import 'package:live_stream_core/live_stream_core.dart';

final result = await controller.joinVoiceRoom('replace with your roomId');

观众申请上麦

效果预览

申请上麦前
上麦成功


观众申请上麦功能主要是通过 SeatGridView(Android & iOS) / SeatGridController(Flutter) 实现,您可以调用如下 API 函数,实现观众申请上麦功能。以观众 B 申请上麦为例实现如下。

观众发送申请上麦请求

观众 B 给主播 A 发送申请上麦请求,主播 A 会在 onSeatRequestReceived 回调中收到观众 B 的上麦请求。
Android
iOS
Flutter
Kotlin
Java
val seatIndex = 1;
val timeout = 60;
seatGridView.takeSeat(seatIndex, timeout, object : VoiceRoomDefine.RequestCallback {
override fun onAccepted(userInfo: TUIRoomDefine.UserInfo) {
Log.i(TAG, "申请上麦被同意")
}

override fun onRejected(userInfo: TUIRoomDefine.UserInfo) {
Log.i(TAG, "申请上麦被拒绝")
}

override fun onCancelled(userInfo: TUIRoomDefine.UserInfo) {
Log.i(TAG, "申请上麦被取消")
}

override fun onTimeout(userInfo: TUIRoomDefine.UserInfo) {
Log.i(TAG, "申请上麦超时")
}

override fun onError(userInfo: TUIRoomDefine.UserInfo, error: TUICommonDefine.Error, message: String) {
Log.i(TAG, "申请上麦错误")
}
})
int seatIndex = 1;
int timeout = 60;
seatGridView.takeSeat(seatIndex, timeout, new VoiceRoomDefine.RequestCallback() {
@Override
public void onAccepted(TUIRoomDefine.UserInfo userInfo) {
Log.i(TAG, "申请上麦被同意");
}

@Override
public void onRejected(TUIRoomDefine.UserInfo userInfo) {
Log.i(TAG, "申请上麦被拒绝");
}

@Override
public void onCancelled(TUIRoomDefine.UserInfo userInfo) {
Log.i(TAG, "申请上麦被取消");
}

@Override
public void onTimeout(TUIRoomDefine.UserInfo userInfo) {
Log.i(TAG, "申请上麦超时");
}

@Override
public void onError(TUIRoomDefine.UserInfo userInfo, TUICommonDefine.Error error, String message) {
Log.i(TAG, "申请上麦错误");
}
});
swift
import RTCRoomEngine
import LiveStreamCore

let seatIndex = 1
let timeout = 60
seatGridView.takeSeat(index: index, timeout: timeout) { userInfo in
print("申请上麦被同意")
} onRejected: { userInfo in
print("申请上麦被拒绝")
} onCancelled: { userInfo in
print("申请上麦被取消")
} onTimeout: { userInfo in
print("申请上麦超时")
} onError: { userInfo, code, message in
print("申请上麦错误")
}
Dart
import 'package:live_stream_core/live_stream_core.dart';
import 'package:rtc_room_engine/rtc_room_engine.dart';

const int seatIndex = 1;
const int timeout = 60;
final controller = SeatGridController();
final result = await controller.takeSeat(seatIndex, timeout);
if (result.code == TUIError.success) {
switch (result.type) {
case RequestResultType.onAccepted:
debugPrint('申请上麦被同意');
break;
case RequestResultType.onRejected:
debugPrint('申请上麦被拒绝');
break;
case RequestResultType.onCancelled:
debugPrint('申请上麦被取消');
break;
case RequestResultType.onTimeout:
debugPrint('申请上麦超时');
break;
default:
break;
}
} else {
debugPrint('申请上麦错误');
}
观众申请上麦功能 主要是通过 SeatGridView(Android & iOS) / SeatGridController(Flutter) 实现,您可以调用如下 API 函数,实现观众申请上麦功能。以观众 B 申请上麦为例实现如下。
注意:
只有当房间模式是 APPLY_TO_TAKE (申请上麦),房主才会收到上麦请求,FREE_TO_TAKE (自由上麦) 模式下,takeSeat 会直接上麦成功。

主播响应上麦请求

主播 A 收到观众的上麦请求后,可以调用 responseRemoteRequest 响应观众 B 是否同意上麦, 观众 B 会收到主播 A 同意和拒绝(onAccepted/onRejected)的回调。
Android
iOS
Flutter
Kotlin
Java
// 主播同意观众上麦
seatGridView.responseRemoteRequest(userId, true, null);

// 主播拒绝观众上麦
seatGridView.responseRemoteRequest(userId, false, null);
// 主播同意观众上麦
seatGridView.responseRemoteRequest(userId, true, null);

// 主播拒绝观众上麦
seatGridView.responseRemoteRequest(userId, false, null);
swift
import RTCRoomEngine
import LiveStreamCore

// 主播同意观众上麦
seatGridView.responseRemoteRequest(userId, true) {
} onError: { code, message in
}

// 主播拒绝观众上麦
seatGridView.responseRemoteRequest(userId, false) {
} onError: { code, message in
}
Dart
import 'package:live_stream_core/live_stream_core.dart';
import 'package:rtc_room_engine/rtc_room_engine.dart';

final controller = SeatGridController();

const String userId = 'Replace with the userId of userB';

// 主播同意观众上麦
final result = await controller.responseRemoteRequest(userId, true);

// 主播拒绝观众上麦
final result = await controller.responseRemoteRequest(userId, false);

麦位信息发生变化的回调

Android
iOS
Flutter
如果您已经设置过 自定义麦位视图, 可以监听 updateSeatView 回调来刷新自己的自定义 UI。
Kotlin
Java
override fun updateSeatView(seatGridView: SeatGridView, seatInfo: TUIRoomDefine.SeatInfo, seatView: View) {
Log.i(TAG, "麦位信息发生变化");
}
@Override
public void void updateSeatView(SeatGridView seatGridView, TUIRoomDefine.SeatInfo seatInfo, View seatView) {
Log.i(TAG, "麦位信息发生变化");
}
如果您已经设置过 自定义麦位视图, 可以监听updateSeatView回调来刷新自己的自定义 UI。
swift
import RTCRoomEngine
import LiveStreamCore

func seatGridView(_ view: SeatGridView, updateSeatView seatInfo: TUISeatInfo, seatView: UIView) {
print("麦位信息发生变化")
}
当有麦位信息发生变化时,SeatWidgetBuilderseatInfoNotifier 参数对应数值会发生改变,您可将您的自定义麦位部件作为 ValueListenableBuilder 的子部件,以便即时更新您的自定义麦位UI。
Dart
// seatWidetBuilder 定义
typedef SeatWidgetBuilder = Widget Function(
BuildContext context,
ValueNotifier<TUISeatInfo> seatInfoNotifier,
ValueNotifier<int> volumeNotifier);
// 使用示例
import 'package:live_stream_core/live_stream_core.dart';
import 'package:rtc_room_engine/rtc_room_engine.dart';

SeatGridWidget(
controller: controller,
seatWidgetBuilder: (
BuildContext context,
ValueNotifier<TUISeatInfo> seatInfoNotifier,
ValueNotifier<int> volumeNotifier) {
return ValueListenableBuilder(
valueListenable: seatInfoNotifier,
builder: (context, seatInfo, _) {
// 返回您的自定义麦位部件
return Container();
}
);
}
)

麦位用户移动麦位

效果预览
移动麦位前
移动麦位后


Android
iOS
Flutter
Kotlin
Java
val index = 3

seatGridView.moveToSeat(index, object : TUIRoomDefine.ActionCallback {
override fun onSuccess() {
// 移动麦位成功
}

override fun onError(error: TUICommonDefine.Error, message: String) {
// 移动麦位失败
}
})
int index = 3;

seatGridView.moveToSeat(index, new TUIRoomDefine.ActionCallback() {
@Override
public void onSuccess() {
// 移动麦位成功
}

@Override
public void onError(TUICommonDefine.Error error, String message) {
// 移动麦位失败
}
});
Swift
import RTCRoomEngine
import LiveStreamCore

let index = 3
self.seatGridView.moveToSeat(index: destinationIndex) {
// 移动麦位成功
} onError: { code, message in
// 移动麦位失败
}
Dart
import 'package:live_stream_core/live_stream_core.dart';

final destinationIndex = 3;
final result = await controller.moveToSeat(destinationIndex);

麦上用户主动下麦

用户上麦成功后,主动下麦

用户 B 上麦成功后,用户 B 主动下麦可以调用 leaveSeat。
Android
iOS
Flutter
Kotlin
Java
seatGridView.leaveSeat()
seatGridView.leaveSeat();
swift
import RTCRoomEngine
import LiveStreamCore

seatGridView.leaveSeat() {
} onError: { code, message in
}
Dart
import 'package:live_stream_core/live_stream_core.dart';

final controller = SeatGridController();
final result = await controller.leaveSeat();

主播邀请观众上麦

主播发送邀请上麦请求

主播 A 给观众 C 发送邀请上麦请求,观众 C 会在 onSeatRequestReceived 回调中收到主播 A 的邀请上麦请求。
Android
iOS
Flutter
Kotlin
Java
val seatIndex = 1;
val userId = "userIdC";
val timeout = 60;
seatGridView.takeUserOnSeatByAdmin(seatIndex, timeout, userId, object : VoiceRoomDefine.RequestCallback {
override fun onAccepted(userInfo: TUIRoomDefine.UserInfo) {
Log.i(TAG, "邀请上麦被同意")
}

override fun onRejected(userInfo: TUIRoomDefine.UserInfo) {
Log.i(TAG, "邀请上麦被拒绝")
}

override fun onCancelled(userInfo: TUIRoomDefine.UserInfo) {
Log.i(TAG, "邀请上麦被取消")
}

override fun onTimeout(userInfo: TUIRoomDefine.UserInfo) {
Log.i(TAG, "邀请上麦超时")
}

override fun onError(userInfo: TUIRoomDefine.UserInfo, error: TUICommonDefine.Error, message: String) {
Log.i(TAG, "邀请上麦错误")
}
})
val seatIndex = 1;
val userId = "userIdC";
val timeout = 60;
seatGridView.takeUserOnSeatByAdmin(seatIndex, userId, timeout, new VoiceRoomDefine.RequestCallback() {
@Override
public void onAccepted(TUIRoomDefine.UserInfo userInfo) {
Log.i(TAG, "邀请上麦被同意");
}

@Override
public void onRejected(TUIRoomDefine.UserInfo userInfo) {
Log.i(TAG, "邀请上麦被拒绝");
}

@Override
public void onCancelled(TUIRoomDefine.UserInfo userInfo) {
Log.i(TAG, "邀请上麦被取消");
}

@Override
public void onTimeout(TUIRoomDefine.UserInfo userInfo) {
Log.i(TAG, "邀请上麦超时");
}

@Override
public void onError(TUIRoomDefine.UserInfo userInfo, TUICommonDefine.Error error, String message) {
Log.i(TAG, "邀请上麦错误");
}
});
swift
import RTCRoomEngine
import LiveStreamCore

let seatIndex = 1
let timeout = 60
let userId = "userIdC"

seatGridView.takeUserOnSeatByAdmin(index: seatIndex, timeout: timeout, userId: userId) { userInfo in
print("邀请上麦被同意")
} onRejected: { userInfo in
print("邀请上麦被拒绝")
} onCancelled: { userInfo in
print("邀请上麦被取消")
} onTimeout: { userInfo in
print("邀请上麦超时")
} onError: { userInfo, code, message in
print("邀请上麦错误")
}
Dart
import 'package:live_stream_core/live_stream_core.dart';
import 'package:rtc_room_engine/rtc_room_engine.dart';

const int seatIndex = 1;
const String userId = 'userIdC'
const int timeout = 60;
final controller = SeatGridController();
final result = await controller.takeUserOnSeatByAdmin(seatIndex, userId, timeout);
if (result.code == TUIError.success) {
switch (result.type) {
case RequestResultType.onAccepted:
debugPrint('邀请上麦被同意');
break;
case RequestResultType.onRejected:
debugPrint('邀请上麦被拒绝');
break;
case RequestResultType.onCancelled:
debugPrint('邀请上麦被取消');
break;
case RequestResultType.onTimeout:
debugPrint('邀请上麦超时');
break;
default:
break;
}
} else {
debugPrint('邀请上麦错误');
}

观众端收到邀请上麦请求

观众 C 会在 onSeatRequestReceived 回调方法中收到 主播A 的邀请上麦请求。
Android
iOS
Flutter
Kotlin
Java
override fun onSeatRequestReceived(type: VoiceRoomDefine.RequestType, userInfo: TUIRoomDefine.UserInfo) {
if (type == VoiceRoomDefine.RequestType.INVITE_TO_TAKE_SEAT) {
Log.i(TAG, "收到主播邀请上麦请求:${userInfo.userId}")
}
}
@Override
public void onSeatRequestReceived(VoiceRoomDefine.RequestType type, TUIRoomDefine.UserInfo userInfo) {
if (type == VoiceRoomDefine.RequestType.INVITE_TO_TAKE_SEAT) {
Log.i(TAG, "收到主播邀请上麦请求:" + userInfo.userId);
}
}
swift
import RTCRoomEngine
import LiveStreamCore

func onSeatRequestReceived(type: SGRequestType, userInfo: TUIUserInfo) {
if type == .inviteToTakeSeat {
print("收到主播邀请上麦请求:\(userInfo.userId)")
}
}
Dart
import 'package:live_stream_core/live_stream_core.dart';
import 'package:rtc_room_engine/rtc_room_engine.dart';

// 创建监听Observer实例
final controller = SeatGridController();
final exampleObserver = ExampleObserver();

// 添加观察者
controller.addObserver(exampleObserver);

class ExampleObserver extends SeatGridWidgetObserver {
ExampleObserver() {
super.onSeatRequestReceived = (type, userInfo) {
if (type == RequestType.inviteToTakeSeat) {
debugPrint('收到主播邀请上麦请求:${userInfo.userId}');
}
};
}
}

观众响应邀请上麦请求

观众 C 收到观众的上麦请求后,可以调用 responseRemoteRequest 响应主播 A 是否同意上麦, 主播 A 会收到观众 C 同意和拒绝(onAccepted/onRejected)的回调。
Android
iOS
Flutter
Kotlin
Java
// 观众同意主播邀请
seatGridView.responseRemoteRequest("", true, null);

// 观众拒绝主播邀请
seatGridView.responseRemoteRequest("", false, null);
// 观众同意主播邀请
seatGridView.responseRemoteRequest("", true, null);

// 观众拒绝主播邀请
seatGridView.responseRemoteRequest("", false, null);
swift
import RTCRoomEngine
import LiveStreamCore

// 观众同意主播邀请
seatGridView.responseRemoteRequest("userId of anchor", true) {
} onError: { code, message in
}

// 观众拒绝主播邀请
seatGridView.responseRemoteRequest("userId of anchor", false, null) {
} onError: { code, message in
}
Dart
import 'package:live_stream_core/live_stream_core.dart';

final controller = SeatGridController();

const String userId = 'userId of anchor';

// 观众同意主播邀请
final result = await controller.responseRemoteRequest(userId, true);

// 观众拒绝主播邀请
final result = await controller.responseRemoteRequest(userId, false);
说明:
如果自定义麦位视图,主播和观众都可以通过监听 麦位信息发生变化的回调 刷新麦位 UI。

房主踢人下麦

观众上麦成功后,主播踢观众下麦

观众 B 上麦成功后,主播 A 踢观众 B 下麦。
Android
iOS
Flutter
Kotlin
Java
val userId = "userIdB"
seatGridView.kickUserOffSeatByAdmin(userId, null)
String userId = "userIdB";
seatGridView.kickUserOffSeatByAdmin(userId, null);
swift
import RTCRoomEngine
import LiveStreamCore

let userId = "userIdB"
seatGridView.kickUserOffSeatByAdmin(userId) {
} onError: { code, message in
}
Dart
import 'package:live_stream_core/live_stream_core.dart';

const String userId = "userIdB";
final controller = SeatGridController();
final result = await controller.kickUserOffSeatByAdmin();

观众收到被主播踢下麦的回调

主播 A 踢观众 B 下麦后,观众 B 会收到 onKickedOffSeat 回调。
Android
iOS
Flutter
Kotlin
Java
override fun onKickedOffSeat(inviterUser: UserInfo) {
Log.i(TAG, "主播踢下麦")
}
@Override
public void onKickedOffSeat(TUIRoomDefine.UserInfo userInfo) {
Log.i(TAG, "主播踢下麦");
}
swift
import RTCRoomEngine
import LiveStreamCore

func onKickedOffSeat(userInfo: TUIUserInfo) {
print("主播踢下麦")
}
Dart
import 'package:live_stream_core/live_stream_core.dart';
import 'package:rtc_room_engine/rtc_room_engine.dart';

// 创建监听Observer实例
final controller = SeatGridController();
final exampleObserver = ExampleObserver();

// 添加观察者
controller.addObserver(exampleObserver);

class ExampleObserver extends SeatGridWidgetObserver {
ExampleObserver() {
super.onKickedOffSeat = (userInfo) {
debugPrint('主播踢下麦');
};
}
}
说明:
如果自定义麦位视图,主播和观众都可以通过监听 麦位信息发生变化的回调 刷新麦位 UI。

房主锁定麦位

效果预览
锁定麦位前
锁定麦位后



位置锁定

主播可将某个麦位进行锁定,此麦位就会被封禁,此麦位就无法进行上下麦操作。
Android
iOS
Flutter
Kotlin
Java
val index = 1
val isLockSeat = true
val params = TUIRoomDefine.SeatLockParams().apply {
lockSeat = isLockSeat
}
seatGridView.lockSeat(index, params, null)
int index = 1;
bool isLockSeat = true;
TUIRoomDefine.SeatLockParams params = new TUIRoomDefine.SeatLockParams();
params.lockSeat = isLockSeat;
seatGridView.lockSeat(index, params, null);
swift
import RTCRoomEngine
import LiveStreamCore

let index = 1
let isLockSeat = true

let params = TUISeatLockParams()
params.lockSeat = isLockSeat

seatGridView.lockSeat(index: index, lockMode: params) {
} onError: { code, message in
}
Dart
import 'package:live_stream_core/live_stream_core.dart';
import 'package:rtc_room_engine/rtc_room_engine.dart';

const int index = 1;
const bool isLockSeat = true
final params = TUISeatLockParams();
params.lockSeat = isLockSeat;
final controller = SeatGridController();
final result = await controller.lockSeat(index, params);

设置麦位列表布局

自定义麦位列表布局效果预览
宫格布局
元素布局
纵向布局
自定义布局

















您可以通过以下方式快速设置您的麦位列表布局。
Android
iOS
Flutter
kotlin
java
// 设置宫格布局
seatGirdView.setLayoutMode(VoiceRoomDefine.LayoutMode.GRID, null)

// 设置元素布局
seatGirdView.setLayoutMode(VoiceRoomDefine.LayoutMode.FOCUS, null)

// 设置纵向布局
seatGirdView.setLayoutMode(VoiceRoomDefine.LayoutMode.VERTICAL, null)

// 设置自定义布局
val layoutConfig = VoiceRoomDefine.SeatViewLayoutConfig().apply {
rowConfigs = ArrayList()
rowSpacing = dp2px(10); //每行的间距
}
// 第一行配置
val rowConfig1 = VoiceRoomDefine.SeatViewLayoutRowConfig().apply {
count = 3 //第一行显示的数量
seatSize = VoiceRoomDefine.Size(dp2px(50), dp2px(50)) //第一行显示的每个麦位视图大小
seatSpacing = dp2px(10) //第一行每个麦位的水平间距
alignment = VoiceRoomDefine.SeatViewLayoutRowAlignment.CENTER //第一行麦位的对齐方式
}
layoutConfig.rowConfigs.add(rowConfig1)
// 第二行配置
val rowConfig2 = VoiceRoomDefine.SeatViewLayoutRowConfig().apply {
count = 3 //第二行显示的数量
seatSize = VoiceRoomDefine.Size(dp2px(50), dp2px(50)) //第二行显示的每个麦位大小
seatSpacing = dp2px(10) //第二行每个麦位的水平间距
alignment = VoiceRoomDefine.SeatViewLayoutRowAlignment.SPACE_AROUND //第二行麦位的对齐方式
}
layoutConfig.rowConfigs.add(rowConfig2)
seatGirdView.setLayoutMode(VoiceRoomDefine.LayoutMode.FREE, layoutConfig)
// 设置宫格布局
seatGirdView.setLayoutMode(VoiceRoomDefine.LayoutMode.GRID, null)

// 设置元素布局
seatGirdView.setLayoutMode(VoiceRoomDefine.LayoutMode.FOCUS, null)

// 设置纵向布局
seatGirdView.setLayoutMode(VoiceRoomDefine.LayoutMode.VERTICAL, null)

// 设置自由布局
VoiceRoomDefine.SeatViewLayoutConfig layoutConfig = new VoiceRoomDefine.SeatViewLayoutConfig();
layoutConfig.rowConfigs = new ArrayList<>();
layoutConfig.rowSpacing = dp2px(10); //每行的间距
//第一行配置
VoiceRoomDefine.SeatViewLayoutRowConfig rowConfig1 = new VoiceRoomDefine.SeatViewLayoutRowConfig();
rowConfig1.count = 3; //第一行显示的数量
rowConfig1.seatSize = new VoiceRoomDefine.Size(dp2px(50),dp2px(50)); //第一行显示的每个麦位视图大小
rowConfig1.seatSpacing = dp2px(10); //第一行每个麦位的水平间距
rowConfig1.alignment = VoiceRoomDefine.SeatViewLayoutRowAlignment.CENTER; //第一行麦位的对齐方式
layoutConfig.rowConfigs.add(rowConfig1);
//第二行配置
VoiceRoomDefine.SeatViewLayoutRowConfig rowConfig2 = new VoiceRoomDefine.SeatViewLayoutRowConfig();
rowConfig2.count = 3; //第二行显示的数量
rowConfig2.seatSize = new VoiceRoomDefine.Size(dp2px(50),dp2px(50)); //第二行显示的每个麦位视图大小
rowConfig1.seatSpacing = dp2px(10); //第二行每个麦位的水平间距
rowConfig2.alignment = VoiceRoomDefine.SeatViewLayoutRowAlignment.SPACE_AROUND; //第二行麦位的对齐方式
layoutConfig.rowConfigs.add(rowConfig2);
seatGirdView.setLayoutMode(VoiceRoomDefine.LayoutMode.FREE, layoutConfig);
说明:
自定义布局的参数设置可查看 SeatViewLayoutRowConfig 中参数说明,其中对齐方式 alignment 可参见 SeatViewLayoutRowAlignment 中描述对齐方式效果可参见下图(自定义布局对齐方式示意图)。
swift
import LiveStreamCore

// 设置宫格布局
seatGridView.setLayoutMode(layoutMode: .grid)

// 设置元素布局
seatGridView.setLayoutMode(layoutMode: .focus)

// 设置纵向布局
seatGridView.setLayoutMode(layoutMode: .vertical)

// 设置自定义布局
// 第一行配置
let rowConfig1 = SGSeatViewLayoutRowConfig(count: 3, //第一行显示的数量
seatSpacing: 10, //第一行每个麦位的水平间距
seatSize: CGSize(width: 50, height: 50), //第一行显示的每个麦位视图大小
alignment: .center) //第一行麦位的对齐方式
// 第二行配置
let rowConfig2 = SGSeatViewLayoutRowConfig(count: 3, //第二行显示的数量
seatSpacing: 10, //第二行每个麦位的水平间距
seatSize: CGSize(width: 50, height: 50), //第二行显示的每个麦位视图大小
alignment: .spaceAround) //第二行麦位的对齐方式
let layoutConfig = SGSeatViewLayoutConfig(rowConfigs: [rowConfig1, rowConfig2],
rowSpacing: 10)

seatGridView.setLayoutMode(layoutMode: .free, layoutConfig: layoutConfig)
说明:
自定义布局的参数设置可查看 SGSeatViewLayoutRowConfig 中参数说明,其中对齐方式 alignment 可参见 SGSeatViewLayoutRowAlignment 中描述对齐方式效果可参见下图(自定义布局对齐方式示意图)。
// 接口定义
void setLayoutMode(LayoutMode layoutMode, SeatWidgetLayoutConfig? layoutConfig);

// 接口使用
import 'package:live_stream_core/live_stream_core.dart';

// 设置宫格布局
controller.setLayoutMode(LayoutMode.grid, null);

// 设置元素布局
controller.setLayoutMode(LayoutMode.focus, null);

// 设置垂直布局
controller.setLayoutMode(LayoutMode.vertical, null);

// 设置自定义布局
final rowConfig = SeatWidgetLayoutRowConfig(
count: 2,
seatSpacing: 20.0,
seatSize: const Size(80, 80),
alignment: SeatWidgetLayoutRowAlignment.spaceBetween);
final layoutConfig = SeatWidgetLayoutConfig(
rowConfigs: [rowConfig, rowConfig]);
controller.setLayoutMode(LayoutMode.free, layoutConfig);
说明:
自定义布局的参数设置可查看 SeatWidgetLayoutRowConfig 中参数说明,其中对齐方式 alignment 可参见 SeatWidgetLayoutRowAlignment 中描述对齐方式效果可参见下图(自定义布局对齐方式示意图)。
自定义布局对齐方式示意图




自定义麦位视图

Android
iOS
Flutter
如果您认为我们默认的UI不满足您的需求,您想自定义自己的麦位UI,您可以通过以下方式快速设置您的麦位布局, 完全自定义自己的麦位视图UI。
kotlin
java
val adapter = object : VoiceRoomDefine.SeatViewAdapter {
override fun createSeatView(seatGridView: SeatGridView, seatInfo: TUIRoomDefine.SeatInfo): View {
return TestSeatInfoView(context, seatGridView, seatInfo)
}

override fun updateSeatView(seatGridView: SeatGridView,seatInfo: TUIRoomDefine.SeatInfo, seatView: View) {
(seatView as TestSeatInfoView).updateSeatView(seatGridView, seatInfo)
}

override fun updateUserVolume(seatGridView: SeatGridView, volume: Int, customSeatView: View) {
(customSeatView as TestSeatInfoView).updateUserVolume(seatGridView, volume)
}
}

seatGirdView.setSeatViewAdapter(adapter)

class TestSeatInfoView constructor(context: Context, seatGirdView: SeatGridView, seatInfo: TUIRoomDefine.SeatInfo) : FrameLayout(context) {
init {
initView() //初始化view
}

fun updateSeatView(seatGirdView: SeatGridView, seatInfo: TUIRoomDefine.SeatInfo) {
updateView(seatInfo) //更新自定义麦位视图UI
}

fun updateUserVolume(seatGirdView: SeatGridView, volume: Int) {
updateUserVolume(volume) //更新音量变化UI
}
}
VoiceRoomDefine.SeatViewAdapter adapter = new VoiceRoomDefine.SeatViewAdapter() {
@Override
public View createSeatView(SeatGridView seatGridView, TUIRoomDefine.SeatInfo seatInfo) {
return new TestSeatInfoView(getApplicationContext(), seatGridView, seatInfo);
}

@Override
public void updateSeatView(SeatGridView seatGridView, TUIRoomDefine.SeatInfo seatInfo,
View customSeatView) {
((TestSeatInfoView) customSeatView).updateSeatView(seatGridView, seatInfo);
}

@Override
public void updateUserVolume(SeatGridView seatGridView, int volume, View customSeatView) {
((TestSeatInfoView) customSeatView).updateUserVolume(seatGridView, volume);
}
};
seatGirdView.setSeatViewAdapter(adapter);

public class TestSeatInfoView extends FrameLayout {

public TestSeatInfoView(@NonNull Context context, SeatGridView seatGirdView, TUIRoomDefine.SeatInfo seatInfo) {
super(context);
initView();
}

public void updateSeatView(SeatGridView seatGirdView, TUIRoomDefine.SeatInfo seatInfo) {
updateView(seatInfo);
}

public void updateUserVolume(SeatGridView seatGirdView, int volume) {
updateUserVolume(volume);
}
}
如果您认为我们默认的UI不满足您的需求,您想自定义自己的麦位UI,您可以通过以下方式快速设置您的麦位布局, 完全自定义自己的麦位视图UI。
swift
import LiveStreamCore

class TestSeatViewDelegate: SGSeatViewDelegate {
func seatGridView(_ view: SeatGridView, createSeatView seatInfo: TUISeatInfo) -> UIView? {
return TestSeatInfoView(seatGridView: view, seatInfo: seatInfo)
}
func seatGridView(_ view: SeatGridView, updateSeatView seatInfo: TUISeatInfo, seatView: UIView) {
if let seatView = seatView as? TestSeatInfoView {
seatView.updateSeatView(seatGridView: view, seatInfo: seatInfo)
}
}
func seatGridView(_ view: SeatGridView, updateUserVolume volume: Int, seatView: UIView) {
if let seatView = seatView as? TestSeatInfoView {
seatView.updateUserVolume(seatGridView: view, volume: volume)
}
}
}

seatGridView.setSeatViewDelegate(TestSeatViewDelegate())

class TestSeatInfoView: UIView {
init(seatGridView: SeatGridView, seatInfo: TUISeatInfo) {
super.init(frame: .zero)
initView() //初始化view
}

func updateSeatView(seatGridView: SeatGridView, seatInfo: TUISeatInfo) {
updateView(seatInfo) //更新自定义麦位视图UI
}

func updateUserVolume(seatGridView: SeatGridView, volume: Int) {
updateUserVolume(volume) //更新音量变化UI
}
}
如果您认为我们默认的 UI 不满足您的需求,您想自定义自己的麦位UI,您可通过 SeatGridWidget 的参数 seatWidetBuilder 实现指定麦位的 UI 样式。
// seatWidetBuilder 定义
typedef SeatWidgetBuilder = Widget Function(
BuildContext context,
ValueNotifier<TUISeatInfo> seatInfoNotifier,
ValueNotifier<int> volumeNotifier);
// 使用示例
import 'package:live_stream_core/live_stream_core.dart';
import 'package:rtc_room_engine/rtc_room_engine.dart';

SeatGridWidget(
controller: controller,
onSeatWidgetTap: (TUISeatInfo seatInfo) {
// debugPrint('click seatWidget index:${seatInfo.index}');
},
seatWidgetBuilder: (
BuildContext context,
ValueNotifier<TUISeatInfo> seatInfoNotifier,
ValueNotifier<int> volumeNotifier) {
// 返回您的自定义麦位部件
return Container();
}
)
默认麦位视图
自定义麦位视图示例








功能扩展

Android
iOS
Flutter
若您需要扩展其他管理功能(如麦克风管理),可参见 SeatGridView 文档。
若您需要扩展其他管理功能(如麦克风管理),可参见 SeatGridView 文档。
若您需要扩展其他管理功能(如麦克风管理),可参见 SeatGridWidget 文档。