语聊组件
组件概述
语音聊天室的核心控件(SeaGridView) 是专为直播场景设计的核心控件,提供了一系列强大的 API 功能,帮助开发者快速实现语音聊天室功能。通过该控件,您可以轻松开启或关闭语音聊天室,并高效管理直播间内的麦位操作,包括申请上麦、邀请上麦、移动麦位以及踢人下麦等。借助 SeaGridView,您能够为应用快速集成语音聊天室模块,显著提升用户体验,同时降低开发成本。
开发环境要求
Android 5.0(SDK API Level 21)及以上版本。
Gradle 7.0 及以上的版本。
Android 5.0 及以上的手机设备。
环境配置或编译运行期间,如有问题,请参见 常见问题。
平台 | 版本 |
Flutter | Flutter 3.27.4 及更高版本。 Dart 3.6.2 或更高的版本。 |
Android | Android Studio 3.5及以上版本。 Android 5.0 及以上版本的 Android 设备。 |
iOS | Xcode 15.0及以上版本。 请确保您的项目已设置有效的开发者签名。 |
步骤1:开通服务
步骤2:集成与配置
1. 在 app 目录下找到
build.gradle.kts(或build.gradle)
文件,并在其中增加如下代码,加入对 SeatGridView 组件的依赖: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">
1. 请在您的
Podfile
文件中添加 pod 'LiveStreamCore'
依赖。target 'xxxx' do......pod 'LiveStreamCore'end
如果您没有
Podfile
文件,首先终端cd
到xxxx.xcodeproj
目录,然后通过以下命令创建:pod init
2. 在终端中,首先
cd
到Podfile
目录下,然后执行以下命令,安装组件。pod install
如果无法安装 SeatGridView 最新版本,可以先删除Podfile.lock和Pods。然后执行以下命令更新本地的 CocoaPods 仓库列表。
pod repo update
之后执行以下命令,更新组件库的 Pod 版本。
pod update
flutter pub
add
live_stream_core
步骤3:登录
在您的项目中添加如下代码,它的作用是通过调用 TUICore 中的相关接口完成 TUI 组件的登录。这一步骤至关重要,只有在成功登录之后,您才能正常使用 SeatGridView 提供的各项功能。
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() {@Overridepublic void onSuccess() {Log.i(TAG, "login success");}@Overridepublic void onError(TUICommonDefine.Error error, String message) {Log.e(TAG, "login failed, errorCode: " + errorCode + " msg:" + errorMessage);}});
在您的项目中添加如下代码,它的作用是通过调用 RTCRoomEngine 中的登录相关接口完成 TUI 组件的登录。这一步骤至关重要,只有在成功登录之后,您才能正常使用 SeatGridView 提供的各项功能。
//// AppDelegate.swift//import RTCRoomEnginefunc application(_ application: UIApplication, didFinishLaunchingWithOptions launchOptions: [UIApplication.LaunchOptionsKey: Any]?) -> Bool {TUIRoomEngine.login(sdkAppId: 1400000001, // 请替换为步骤一取到的 SDKAppIDuserId: "denny", // 请替换为您的 UserIDuserSig: "xxxxxxxxxxx") { // 您可以在控制台中计算一个 UserSig 并填在这个位置print("login success")} onError: { code, message inprint("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 | |
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:使用核心控件实现语音聊天室功能
房主开启语音聊天室并上麦
效果预览
房主开启语音聊天室&上麦 |
![]() |
创建核心控件
您可以在您 Activity 中通过 java 代码或者 xml 方式加载我们的核心控件,其中代码方式示例如下(XML 方式也类似):
val seatGridView = SeatGridView(this)
SeatGridView seatGridView = new SeatGridView(this);
import LiveStreamCorelet seatGridView = SeatGridView()
您需要先创建一个控制器
SeatGridController
,然后再将其赋值给语音聊天室核心部件 SeatGridWidget
。SeatGridController
负责提供API, SeatGridWidget
用于展示麦位UI。您可将 SeatGridWidget
添加在您需要展示麦位 UI 的任何地方。final controller = SeatGridController();SeatGridWidget(controller: controller);
主播开启语音聊天室并上麦
开启一个语音聊天室,并将本地麦克风采集的数据推流到直播间。
val roomInfo = TUIRoomDefine.RoomInfo()roomInfo.roomId = Config.roomIdroomInfo.seatMode = TUIRoomDefine.SeatMode.APPLY_TO_TAKEseatGridView.startVoiceRoom(roomInfo, object : TUIRoomDefine.GetRoomInfoCallback {override fun onSuccess(roomInfo: TUIRoomDefine.RoomInfo) {val seatIndex = 1val timeout = 60seatGridView.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() {@Overridepublic void onSuccess(TUIRoomDefine.RoomInfo roomInfo) {int seatIndex = 1;int timeout = 60;seatGridView.takeSeat(seatIndex, timeout, null);seatGridView.startMicrophone(null);}@Overridepublic void onError(TUICommonDefine.Error error, String message) {}});
import LiveStreamCoreimport RTCRoomEnginelet roomInfo = TUIRoomInfo()roomInfo.roomId = "123456"roomInfo.seatMode = .applyToTakeseatGridView.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();
观众加入语音聊天室
效果预览
观众加入语音聊天室 |
![]() |
seatGridView.joinVoiceRoom("roomId_123456", null)
seatGridView.joinVoiceRoom("roomId_123456", null);
import LiveStreamCoreimport RTCRoomEngineseatGridView.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 的上麦请求。
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() {@Overridepublic void onAccepted(TUIRoomDefine.UserInfo userInfo) {Log.i(TAG, "申请上麦被同意");}@Overridepublic void onRejected(TUIRoomDefine.UserInfo userInfo) {Log.i(TAG, "申请上麦被拒绝");}@Overridepublic void onCancelled(TUIRoomDefine.UserInfo userInfo) {Log.i(TAG, "申请上麦被取消");}@Overridepublic void onTimeout(TUIRoomDefine.UserInfo userInfo) {Log.i(TAG, "申请上麦超时");}@Overridepublic void onError(TUIRoomDefine.UserInfo userInfo, TUICommonDefine.Error error, String message) {Log.i(TAG, "申请上麦错误");}});
import RTCRoomEngineimport LiveStreamCorelet seatIndex = 1let timeout = 60seatGridView.takeSeat(index: index, timeout: timeout) { userInfo inprint("申请上麦被同意")} onRejected: { userInfo inprint("申请上麦被拒绝")} onCancelled: { userInfo inprint("申请上麦被取消")} onTimeout: { userInfo inprint("申请上麦超时")} onError: { userInfo, code, message inprint("申请上麦错误")}
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)的回调。
// 主播同意观众上麦seatGridView.responseRemoteRequest(userId, true, null);// 主播拒绝观众上麦seatGridView.responseRemoteRequest(userId, false, null);
// 主播同意观众上麦seatGridView.responseRemoteRequest(userId, true, null);// 主播拒绝观众上麦seatGridView.responseRemoteRequest(userId, false, null);
import RTCRoomEngineimport LiveStreamCore// 主播同意观众上麦seatGridView.responseRemoteRequest(userId, true) {} onError: { code, message in}// 主播拒绝观众上麦seatGridView.responseRemoteRequest(userId, false) {} onError: { code, message in}
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);
麦位信息发生变化的回调
override fun updateSeatView(seatGridView: SeatGridView, seatInfo: TUIRoomDefine.SeatInfo, seatView: View) {Log.i(TAG, "麦位信息发生变化");}
@Overridepublic void void updateSeatView(SeatGridView seatGridView, TUIRoomDefine.SeatInfo seatInfo, View seatView) {Log.i(TAG, "麦位信息发生变化");}
import RTCRoomEngineimport LiveStreamCorefunc seatGridView(_ view: SeatGridView, updateSeatView seatInfo: TUISeatInfo, seatView: UIView) {print("麦位信息发生变化")}
当有麦位信息发生变化时,
SeatWidgetBuilder
的 seatInfoNotifier
参数对应数值会发生改变,您可将您的自定义麦位部件作为 ValueListenableBuilder
的子部件,以便即时更新您的自定义麦位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,seatWidgetBuilder: (BuildContext context,ValueNotifier<TUISeatInfo> seatInfoNotifier,ValueNotifier<int> volumeNotifier) {return ValueListenableBuilder(valueListenable: seatInfoNotifier,builder: (context, seatInfo, _) {// 返回您的自定义麦位部件return Container();});})
麦位用户移动麦位
效果预览
移动麦位前 | 移动麦位后 |
![]() | ![]() |
val index = 3seatGridView.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() {@Overridepublic void onSuccess() {// 移动麦位成功}@Overridepublic void onError(TUICommonDefine.Error error, String message) {// 移动麦位失败}});
import RTCRoomEngineimport LiveStreamCorelet index = 3self.seatGridView.moveToSeat(index: destinationIndex) {// 移动麦位成功} onError: { code, message in// 移动麦位失败}
import 'package:live_stream_core/live_stream_core.dart';final destinationIndex = 3;final result = await controller.moveToSeat(destinationIndex);
麦上用户主动下麦
用户上麦成功后,主动下麦
用户 B 上麦成功后,用户 B 主动下麦可以调用 leaveSeat。
seatGridView.leaveSeat()
seatGridView.leaveSeat();
import RTCRoomEngineimport LiveStreamCoreseatGridView.leaveSeat() {} onError: { code, message in}
import 'package:live_stream_core/live_stream_core.dart';final controller = SeatGridController();final result = await controller.leaveSeat();
主播邀请观众上麦
主播发送邀请上麦请求
主播 A 给观众 C 发送邀请上麦请求,观众 C 会在 onSeatRequestReceived 回调中收到主播 A 的邀请上麦请求。
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() {@Overridepublic void onAccepted(TUIRoomDefine.UserInfo userInfo) {Log.i(TAG, "邀请上麦被同意");}@Overridepublic void onRejected(TUIRoomDefine.UserInfo userInfo) {Log.i(TAG, "邀请上麦被拒绝");}@Overridepublic void onCancelled(TUIRoomDefine.UserInfo userInfo) {Log.i(TAG, "邀请上麦被取消");}@Overridepublic void onTimeout(TUIRoomDefine.UserInfo userInfo) {Log.i(TAG, "邀请上麦超时");}@Overridepublic void onError(TUIRoomDefine.UserInfo userInfo, TUICommonDefine.Error error, String message) {Log.i(TAG, "邀请上麦错误");}});
import RTCRoomEngineimport LiveStreamCorelet seatIndex = 1let timeout = 60let userId = "userIdC"seatGridView.takeUserOnSeatByAdmin(index: seatIndex, timeout: timeout, userId: userId) { userInfo inprint("邀请上麦被同意")} onRejected: { userInfo inprint("邀请上麦被拒绝")} onCancelled: { userInfo inprint("邀请上麦被取消")} onTimeout: { userInfo inprint("邀请上麦超时")} onError: { userInfo, code, message inprint("邀请上麦错误")}
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 的邀请上麦请求。
override fun onSeatRequestReceived(type: VoiceRoomDefine.RequestType, userInfo: TUIRoomDefine.UserInfo) {if (type == VoiceRoomDefine.RequestType.INVITE_TO_TAKE_SEAT) {Log.i(TAG, "收到主播邀请上麦请求:${userInfo.userId}")}}
@Overridepublic void onSeatRequestReceived(VoiceRoomDefine.RequestType type, TUIRoomDefine.UserInfo userInfo) {if (type == VoiceRoomDefine.RequestType.INVITE_TO_TAKE_SEAT) {Log.i(TAG, "收到主播邀请上麦请求:" + userInfo.userId);}}
import RTCRoomEngineimport LiveStreamCorefunc onSeatRequestReceived(type: SGRequestType, userInfo: TUIUserInfo) {if type == .inviteToTakeSeat {print("收到主播邀请上麦请求:\(userInfo.userId)")}}
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)的回调。
// 观众同意主播邀请seatGridView.responseRemoteRequest("", true, null);// 观众拒绝主播邀请seatGridView.responseRemoteRequest("", false, null);
// 观众同意主播邀请seatGridView.responseRemoteRequest("", true, null);// 观众拒绝主播邀请seatGridView.responseRemoteRequest("", false, null);
import RTCRoomEngineimport LiveStreamCore// 观众同意主播邀请seatGridView.responseRemoteRequest("userId of anchor", true) {} onError: { code, message in}// 观众拒绝主播邀请seatGridView.responseRemoteRequest("userId of anchor", false, null) {} onError: { code, message in}
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);
说明:
房主踢人下麦
观众上麦成功后,主播踢观众下麦
观众 B 上麦成功后,主播 A 踢观众 B 下麦。
val userId = "userIdB"seatGridView.kickUserOffSeatByAdmin(userId, null)
String userId = "userIdB";seatGridView.kickUserOffSeatByAdmin(userId, null);
import RTCRoomEngineimport LiveStreamCorelet userId = "userIdB"seatGridView.kickUserOffSeatByAdmin(userId) {} onError: { code, message in}
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 回调。
override fun onKickedOffSeat(inviterUser: UserInfo) {Log.i(TAG, "主播踢下麦")}
@Overridepublic void onKickedOffSeat(TUIRoomDefine.UserInfo userInfo) {Log.i(TAG, "主播踢下麦");}
import RTCRoomEngineimport LiveStreamCorefunc onKickedOffSeat(userInfo: TUIUserInfo) {print("主播踢下麦")}
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('主播踢下麦');};}}
说明:
房主锁定麦位
效果预览
锁定麦位前 | 锁定麦位后 |
![]() | ![]() |
位置锁定
主播可将某个麦位进行锁定,此麦位就会被封禁,此麦位就无法进行上下麦操作。
val index = 1val isLockSeat = trueval 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);
import RTCRoomEngineimport LiveStreamCorelet index = 1let isLockSeat = truelet params = TUISeatLockParams()params.lockSeat = isLockSeatseatGridView.lockSeat(index: index, lockMode: params) {} onError: { code, message in}
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 = truefinal params = TUISeatLockParams();params.lockSeat = isLockSeat;final controller = SeatGridController();final result = await controller.lockSeat(index, params);
设置麦位列表布局
自定义麦位列表布局效果预览
宫格布局 | 元素布局 | 纵向布局 | 自定义布局 |
![]() | ![]() |
![]() |
![]() |
您可以通过以下方式快速设置您的麦位列表布局。
// 设置宫格布局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 中描述对齐方式效果可参见下图(自定义布局对齐方式示意图)。
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 中描述对齐方式效果可参见下图(自定义布局对齐方式示意图)。
自定义布局对齐方式示意图 |
![]() |
自定义麦位视图
如果您认为我们默认的UI不满足您的需求,您想自定义自己的麦位UI,您可以通过以下方式快速设置您的麦位布局, 完全自定义自己的麦位视图UI。
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() {@Overridepublic View createSeatView(SeatGridView seatGridView, TUIRoomDefine.SeatInfo seatInfo) {return new TestSeatInfoView(getApplicationContext(), seatGridView, seatInfo);}@Overridepublic void updateSeatView(SeatGridView seatGridView, TUIRoomDefine.SeatInfo seatInfo,View customSeatView) {((TestSeatInfoView) customSeatView).updateSeatView(seatGridView, seatInfo);}@Overridepublic 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。
import LiveStreamCoreclass 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();})
默认麦位视图 | 自定义麦位视图示例 |
![]() |
![]() |
功能扩展