TUILiveKit 产品动态
云直播推拉流 SDK 产品动态

requestHostConnection 方法。import androidx.appcompat.app.AppCompatActivityimport io.trtc.tuikit.atomicxcore.api.CompletionHandlerimport io.trtc.tuikit.atomicxcore.api.live.CoHostLayoutTemplateimport io.trtc.tuikit.atomicxcore.api.live.CoHostStore// 主播A的Activityclass AnchorAActivity : AppCompatActivity() {private val liveId = "主播A的房间ID"private val coHostStore = CoHostStore.create(liveId)// 用户点击"连线"按钮,并选择了主播Bfun inviteHostB(targetHostLiveId: String) {val layout = CoHostLayoutTemplate.HOST_DYNAMIC_GRID // 选择一个布局模板val timeout = 30 // 邀请超时时间(秒)coHostStore.requestHostConnection(targetHostLiveID = targetHostLiveId,layoutTemplate = layout,timeout = timeout,extraInfo = null,completion = object : CompletionHandler {override fun onSuccess() {println("连线邀请已发送,等待对方处理...")}override fun onFailure(code: Int, desc: String) {println("邀请发送失败: $desc")}})}}
CoHostListener 的回调方法,您可以接收到主播 B 的处理结果。import android.os.Bundleimport androidx.appcompat.app.AppCompatActivityimport io.trtc.tuikit.atomicxcore.api.live.CoHostListenerimport io.trtc.tuikit.atomicxcore.api.live.CoHostStoreimport io.trtc.tuikit.atomicxcore.api.live.SeatUserInfoclass AnchorAActivity : AppCompatActivity() {private val liveId = "主播A的房间ID"private val coHostStore = CoHostStore.create(liveId)private val coHostListener = object : CoHostListener() {override fun onCoHostRequestAccepted(invitee: SeatUserInfo) {println("主播 ${invitee.userName} 同意了你的连线邀请")}override fun onCoHostRequestRejected(invitee: SeatUserInfo) {println("主播 ${invitee.userName} 拒绝了你的邀请")}override fun onCoHostRequestTimeout(inviter: SeatUserInfo, invitee: SeatUserInfo) {println("邀请超时,对方未回应")}}override fun onCreate(savedInstanceState: Bundle?) {super.onCreate(savedInstanceState)setContentView(R.layout.activity_anchor_a)// 初始化CoHostStorecoHostStore.addCoHostListener(coHostListener)}}
CoHostListener,主播B可以监听到来自主播A的邀请。import androidx.appcompat.app.AppCompatActivityimport io.trtc.tuikit.atomicxcore.api.live.CoHostListenerimport io.trtc.tuikit.atomicxcore.api.live.CoHostStoreimport io.trtc.tuikit.atomicxcore.api.live.SeatUserInfo// 主播B的Activityclass AnchorBActivity : AppCompatActivity() {private val liveId = "主播B的房间ID"private val coHostStore = CoHostStore.create(liveId)private val coHostListener = object : CoHostListener() {override fun onCoHostRequestReceived(inviter: SeatUserInfo, extensionInfo: String) {println("收到主播 ${inviter.userName} 的连线邀请")// 显示邀请对话框,响应联线邀请// showInvitationDialog(inviter)}}}
// AnchorBActivity 的一部分fun acceptInvitation(fromHostLiveId: String) {coHostStore.acceptHostConnection(fromHostLiveID = fromHostLiveId, completion = null)}fun rejectInvitation(fromHostLiveId: String) {coHostStore.rejectHostConnection(fromHostLiveID = fromHostLiveId, completion = null)}
requestBattle 方法。// AnchorAActivity 的一部分class AnchorAActivity : AppCompatActivity() {private val liveId = "主播A的房间ID"private val battleStore = BattleStore.create(liveId)fun startPK(opponentUserId: String) {val config = BattleConfig(duration = 300) // PK持续5分钟battleStore.requestBattle(config = config,userIDList = listOf(opponentUserId),timeout = 30,completion = null)}}
BattleListener 监听 PK 的开始、结束等关键事件。// AnchorAActivity 的一部分class AnchorAActivity : AppCompatActivity() {private val liveId = "主播A的房间ID"private val battleStore = BattleStore.create(liveId)private val battleListener = object : BattleListener() {override fun onBattleStarted(battleInfo: BattleInfo,inviter: SeatUserInfo,invitees: List<SeatUserInfo>) {println("PK 开始")}override fun onBattleEnded(battleInfo: BattleInfo, reason: BattleEndedReason?) {println("PK 结束")}}override fun onCreate(savedInstanceState: Bundle?) {super.onCreate(savedInstanceState)// ... 其他初始化代码 ...battleStore.addBattleListener(battleListener)}}
BattleListener 监听到 PK 邀请。// 在 AnchorBActivity 中添加class AnchorBActivity : AppCompatActivity() {private val liveId = "主播B的房间ID"private val battleStore = BattleStore.create(liveId)private val battleListener = object : BattleListener() {override fun onBattleRequestReceived(battleID: String, inviter: SeatUserInfo, invitee: SeatUserInfo) {println("收到主播 ${inviter.userName} 的PK挑战")// 弹出对话框,让主播B选择"接受"或"拒绝"// showPKChallengeDialog(battleID, inviter)}}override fun onCreate(savedInstanceState: Bundle?) {super.onCreate(savedInstanceState)// ... 其他初始化代码 ...battleStore.addBattleListener(battleListener)}}
// AnchorBActivity 的一部分// 用户点击"接受挑战"fun acceptPK(battleId: String) {battleStore.acceptBattle(battleID = battleId, completion = null)}// 用户点击"拒绝挑战"fun rejectPK(battleId: String) {battleStore.rejectBattle(battleID = battleId, completion = null)}
// AnchorAActivity 的一部分// 主播主动结束 PKfun stopPK(battleId: String) {// 调用 exitBattle 接口退出当前的 PK 对战battleStore.exitBattle(battleID = battleId, completion = object : CompletionHandler {override fun onSuccess() {println("PK 已结束")// 此时 UI 会收到 onBattleEnded 事件,请在事件回调中处理 UI 刷新}override fun onFailure(code: Int, desc: String) {println("结束 PK 失败: $desc")}})}
// AnchorAActivity 的一部分// 主播主动断开连线fun stopConnection() {// 调用 exitHostConnection 接口断开与其他主播的连接coHostStore.exitHostConnection(completion = object : CompletionHandler {override fun onSuccess() {println("连线已断开,恢复单人直播")// 此时 UI 会收到 onCoHostUserLeft 事件}override fun onFailure(code: Int, desc: String) {println("断开连线失败: $desc")}})}
// 在 AnchorAActivity 中补充监听逻辑// 1. 监听 PK 结束事件private val battleListener = object : BattleListener() {// ... 其他回调 ...override fun onBattleEnded(battleInfo: BattleInfo, reason: BattleEndedReason?) {println("收到 PK 结束事件,原因: $reason")// 在此处移除 PK 分数视图、进度条等 UI// runOnUiThread { removeBattleUI() }}}// 2. 监听连线断开事件private val coHostListener = object : CoHostListener() {// ... 其他回调 ...override fun onCoHostUserLeft(userInfo: SeatUserInfo) {println("主播 ${userInfo.userName} 已离开连线")// 在此处移除对方的视频画面视图,恢复单人直播布局// runOnUiThread { resetToSingleStreamLayout() }}}

VideoViewAdapter 接口提供的"插槽"能力,在视频流画面上添加自定义视图,用于显示昵称、头像、PK 进度条等信息,或在主播关闭摄像头时提供占位图,以优化视觉体验。
import android.content.Contextimport android.graphics.Colorimport android.view.Gravityimport android.widget.LinearLayoutimport android.widget.TextView// 自定义的用户信息悬浮视图(前景)class CustomSeatView(context: Context) : LinearLayout(context) {private val nameLabel: TextViewinit {orientation = VERTICALsetBackgroundColor(Color.parseColor("#80000000")) // 半透明黑色背景nameLabel = TextView(context).apply {setTextColor(Color.WHITE)textSize = 14fgravity = Gravity.CENTER}addView(nameLabel)val layoutParams = nameLabel.layoutParams as LayoutParamslayoutParams.setMargins(5, 0, 5, 5)}fun setUserName(userName: String) {nameLabel.text = userName}}
import android.content.Contextimport android.graphics.Colorimport android.view.Gravityimport android.widget.ImageViewimport android.widget.LinearLayout// 自定义的头像占位视图(背景)class CustomAvatarView(context: Context) : LinearLayout(context) {private val avatarImageView: ImageViewinit {orientation = VERTICALgravity = Gravity.CENTERsetBackgroundColor(Color.TRANSPARENT)avatarImageView = ImageView(context).apply {setColorFilter(Color.GRAY)scaleType = ImageView.ScaleType.CENTER_CROP}addView(avatarImageView)val layoutParams = avatarImageView.layoutParams as LayoutParamslayoutParams.width = 120 // 60dplayoutParams.height = 120 // 60dp}}
LiveCoreView 自定义视图 VideoViewAdapter,实现 VideoViewAdapter.createCoHostView 协议,根据 viewLayer 的值返回对应的视图。import com.tencent.cloud.tuikit.engine.room.TUIRoomDefineimport android.view.Viewimport androidx.appcompat.app.AppCompatActivityimport io.trtc.tuikit.atomicxcore.api.view.VideoViewAdapterimport io.trtc.tuikit.atomicxcore.api.view.ViewLayer// 在您的Activity中,实现 VideoViewAdapter 接口class YourActivity : AppCompatActivity(), VideoViewAdapter {// ... 其他代码 ...override fun onCreate(savedInstanceState: Bundle?) {super.onCreate(savedInstanceState)// 1. 设置适配器liveCoreView.setVideoViewAdapter(this)}// 2. 完整实现接口方法,处理两种 viewLayeroverride fun createCoHostView(coHostUser: TUIRoomDefine.SeatFullInfo?, viewLayer: ViewLayer?): View? {val seatInfo = coHostUser ?: return nullval userId = seatInfo.userIdif (userId.isNullOrEmpty()) {return null}return when (viewLayer) {ViewLayer.FOREGROUND -> {val seatView = CustomSeatView(this)seatView.setUserName(seatInfo.userName ?: "")seatView}ViewLayer.BACKGROUND -> {val avatarView = CustomAvatarView(this)// 您可以在这里通过 seatInfo.userAvatar 加载用户真实头像avatarView}null -> null}}}
参数 | 类型 | 说明 |
seatInfo | SeatFullInfo? | 麦位信息对象,包含麦上用户的详细信息 |
seatInfo.userId | String | 麦上用户的 ID |
seatInfo.userName | String | 麦上用户的昵称 |
seatInfo.userAvatar | String | 麦上用户的头像 URL |
seatInfo.userMicrophoneStatus | DeviceStatus | 麦上用户的麦克风状态 |
seatInfo.userCameraStatus | DeviceStatus | 麦上用户的摄像头状态 |
viewLayer | ViewLayer | 视图层级枚举 FOREGROUND 表示前景挂件视图,始终显示在视频画面的最上层BACKGROUND 表示背景挂件视图,位于前景视图下层,仅在对应用户没有视频流(例如未开摄像头)的情况下显示,通常用于展示用户的默认头像或占位图 |

import android.content.Contextimport android.graphics.Colorimport android.view.Gravityimport android.widget.LinearLayoutimport android.widget.TextViewimport com.tencent.cloud.tuikit.engine.extension.TUILiveBattleManager.BattleUserimport io.trtc.tuikit.atomicxcore.api.live.BattleStoreimport kotlinx.coroutines.CoroutineScopeimport kotlinx.coroutines.Dispatchersimport kotlinx.coroutines.launch// 自定义PK 用户视图class CustomBattleUserView(context: Context,private val liveId: String,private val battleUser: BattleUser) : LinearLayout(context) {private lateinit var scoreView: LinearLayoutprivate lateinit var scoreLabel: TextViewprivate val battleStore: BattleStoreinit {orientation = VERTICALgravity = Gravity.BOTTOM or Gravity.ENDsetBackgroundColor(Color.TRANSPARENT)isClickable = falsebattleStore = BattleStore.create(liveId)// UI 布局setupUI()// 订阅分数变化subscribeBattleState()}private fun setupUI() {scoreView = LinearLayout(context).apply {setBackgroundColor(Color.parseColor("#66000000"))orientation = VERTICALgravity = Gravity.CENTER}scoreLabel = TextView(context).apply {setTextColor(Color.WHITE)textSize = 14fgravity = Gravity.CENTER}scoreView.addView(scoreLabel)addView(scoreView)val layoutParams = scoreView.layoutParams as LayoutParamslayoutParams.width = LayoutParams.WRAP_CONTENTlayoutParams.height = 48 // 24dplayoutParams.setMargins(0, 0, 10, 10)}// 订阅 PK 分数变化private fun subscribeBattleState() {CoroutineScope(Dispatchers.Main).launch {battleStore.battleState.battleScore.collect { battleScore ->val score = battleScore[battleUser.userId] ?: 0// 更新 UIscoreLabel.text = score.toString()}}}}
LiveCoreView 自定义视图 VideoViewAdapter,实现 VideoViewAdapter.createBattleView 协议// 让您的Activity实现 VideoViewAdapter 接口class YourActivity : AppCompatActivity(), VideoViewAdapter {override fun onCreate(savedInstanceState: Bundle?) {super.onCreate(savedInstanceState)// 1. 设置适配器liveCoreView.setVideoViewAdapter(this)}override fun createBattleView(battleUser: BattleUser?): View? {battleUser ?: return null// 2. CustomBattleUserView 是您自定义的PK用户信息视图return CustomBattleUserView(this, liveId, battleUser)}}
参数 | 类型 | 说明 |
battleUser | BattleUser? | PK 用户信息对象 |
battleUser.roomId | String | PK 的房间 ID |
battleUser.userId | String | PK 用户 ID |
battleUser.userName | String | PK 用户昵称 |
battleUser.avatarUrl | String | PK 用户头像地址 |
battleUser.score | int | PK 分数 |

LiveCoreView 自定义视图 VideoViewAdapter,实现 VideoViewAdapter.createBattleContainerView 协议。// 让您的Activity实现 VideoViewAdapter 接口并设置适配器class YourActivity : AppCompatActivity(), VideoViewAdapter {override fun onCreate(savedInstanceState: Bundle?) {super.onCreate(savedInstanceState)// 1. 设置适配器liveCoreView.setVideoViewAdapter(this)}override fun createBattleContainerView(): View? {// 2. 自定义PK视图return CustomBattleContainerView(this)}}
礼物类型 | 分数计算规则 | 示例 |
基础礼物 | 礼物价值 × 5 | 10元礼物 → 50分 |
中级礼物 | 礼物价值 × 8 | 50元礼物 → 400分 |
高级礼物 | 礼物价值 × 12 | 100元礼物 → 1200分 |
特效礼物 | 固定高分数 | 520元礼物 → 1314分 |

Store/Component | 功能描述 | API 文档 |
LiveCoreView | 直播视频流展示与交互的核心视图组件:负责视频流渲染和视图挂件处理,支持主播直播、观众连麦、主播连线等场景。 | |
DeviceStore | 音视频设备控制:麦克风(开关 / 音量)、摄像头(开关 / 切换 / 画质)、屏幕共享,设备状态实时监听。 | |
CoHostStore | 主播跨房连线:支持多布局模板(动态网格等),发起 / 接受 / 拒绝连线,连麦主播互动管理。 | |
BattleStore | 主播 PK 对战:发起 PK(配置时长 / 对手),管理 PK 状态(开始 / 结束),同步分数,监听对战结果。 |
VideoViewAdapter 添加的自定义视图的生命周期和事件?文档反馈