LiveCoreView 是一个跨平台的视频直播核心组件,提供开播、观看、连麦和主播 PK 等关键能力。该组件通过挂件机制,支持在视频区域实时显示用户名、等级、PK 进度条等自定义信息。本文档指导开发者通过实现接口,在 Android 平台上快速定制专属的视频挂件 UI 。连线视频挂件 | PK 视频挂件 |
![]() | ![]() |
LiveCoreView 通过 VideoViewAdapter 代理支持自定义视图渲染。当业务场景发生变化(例如有人上麦、开始 PK )时,LiveCoreView 会调用代理方法询问应该显示的视图。开发者实现对应的接口方法并返回自定义的 View 实例即可。方法 | 描述 | 对应业务场景 |
createCoGuestView | 创建观众连麦的挂件视图。 | 观众连麦、邀请上麦 |
createCoHostView | 创建跨房连麦(主播连线)的挂件视图。 | 主播连线 |
createBattleView | 创建 PK 场景中单个用户的挂件视图(例如头像、分数)。 | 主播 PK |
createBattleContainerView | 创建 PK 场景的整体容器视图(例如背景、分数 PK 条)。 | 主播 PK |

import android.content.Contextimport android.view.Viewimport android.widget.FrameLayoutimport android.widget.ImageViewimport android.widget.TextViewclass CustomInfoView(context: Context, name: String, isMuted: Boolean) : FrameLayout(context) {private val nameTextView = TextView(context)private val muteIcon = ImageView(context)init {nameTextView.text = namemuteIcon.visibility = if (isMuted) View.VISIBLE else View.GONEaddView(nameTextView)addView(muteIcon)// 此处省略具体布局参数设置代码}}
import android.content.Contextimport android.widget.FrameLayoutimport android.widget.ImageViewimport android.widget.TextViewclass EmptySeatView(context: Context) : FrameLayout(context) {private val addIcon = ImageView(context)private val addLabel = TextView(context)init {addLabel.text = "邀请连线"// 此处省略图片资源加载和布局参数设置代码}}
import android.content.Contextimport android.widget.FrameLayoutimport android.widget.ImageViewclass CustomAvatarView(context: Context, avatarURL: String) : FrameLayout(context) {private val avatarImageView = ImageView(context)init {// 实际项目中可使用 Glide 等图片加载库加载 avatarURLaddView(avatarImageView)// 此处省略具体布局参数设置代码}}
VideoViewAdapter 接口的方法 createCoGuestView (观众连线)和 createCoHostView (主播连线),返回刚才创建的自定义视图。VideoViewAdapter 中的 createCoGuestView 方法,返回观众连线视频挂件。import android.content.Contextimport android.view.Viewimport io.trtc.tuikit.atomicxcore.api.device.DeviceStatusimport io.trtc.tuikit.atomicxcore.api.live.SeatInfoimport io.trtc.tuikit.atomicxcore.api.view.VideoViewAdapterimport io.trtc.tuikit.atomicxcore.api.view.ViewLayerclass VideoWidgetAdapter(private val context: Context) : VideoViewAdapter {override fun createCoGuestView(seatInfo: SeatInfo, viewLayer: ViewLayer): View? {val isUserOnSeat = seatInfo.userInfo.userID.isNotEmpty()return when (viewLayer) {ViewLayer.FOREGROUND -> {if (isUserOnSeat) {// 非空麦位,返回自定义的前景视图CustomInfoView(context, seatInfo.userInfo.userName, seatInfo.userInfo.microphoneStatus == DeviceStatus.OFF)} else {// 空麦位,返回自定义空麦位视图EmptySeatView(context)}}ViewLayer.BACKGROUND -> {if (isUserOnSeat) {// 返回自定义的背景视图(未开摄像头时显示)CustomAvatarView(context, seatInfo.userInfo.avatarURL)} else {null}}}}}
VideoViewAdapter 中的 createCoHostView 方法,返回主播连线的视频挂件。import android.content.Contextimport android.view.Viewimport io.trtc.tuikit.atomicxcore.api.device.DeviceStatusimport io.trtc.tuikit.atomicxcore.api.live.SeatInfoimport io.trtc.tuikit.atomicxcore.api.view.VideoViewAdapterimport io.trtc.tuikit.atomicxcore.api.view.ViewLayerclass VideoWidgetAdapter(private val context: Context) : VideoViewAdapter {override fun createCoHostView(seatInfo: SeatInfo, viewLayer: ViewLayer): View? {val isUserOnSeat = seatInfo.userInfo.userID.isNotEmpty()return when (viewLayer) {ViewLayer.FOREGROUND -> {if (isUserOnSeat) {// 返回自定义的前景视图,可以返回与观众连线不同的样式CustomInfoView(context, seatInfo.userInfo.userName, seatInfo.userInfo.microphoneStatus == DeviceStatus.OFF)} else {// 返回自定义空麦位视图,可以返回与观众连线不同的样式EmptySeatView(context)}}ViewLayer.BACKGROUND -> {if (isUserOnSeat) {// 返回自定义的背景视图(未开摄像头时显示),可以返回与观众连线不同的样式CustomAvatarView(context, seatInfo.userInfo.avatarURL)} else {null}}}}}
参数 | 类型 | 说明 |
seatInfo | SeatInfo | 麦位信息对象,包含麦上用户的详细信息。 |
seatInfo.userInfo.userName | String | 麦上用户的昵称。 |
seatInfo.userInfo.avatarURL | String | 麦上用户的头像 URL。 |
seatInfo.userInfo.microphoneStatus | DeviceStatus | 麦上用户的麦克风状态。 |
seatInfo.userInfo.cameraStatus | DeviceStatus | 麦上用户的摄像头状态。 |
viewLayer | ViewLayer | 视图层级枚举。 .foreground 表示前景挂件视图,始终显示在视频画面的最上层。.background 表示背景挂件视图,位于前景视图下层,仅在对应用户没有视频流(例如未开摄像头)的情况下显示,通常用于展示用户的默认头像或占位图。 |
PK 是直播中互动性最强的环节。在 PK 过程中,画面通常根据参与人数被分割为几部分。开发者需要在画面上方或中间区域添加 PK 专用的 UI ,分数 PK 条、每个麦位上的分数显示、倒计时动画或“VS”特效图标,以营造紧张的竞技氛围。
PK 场景通常需要两类视图:import android.content.Contextimport android.widget.FrameLayoutimport android.widget.ImageView// 示例:单人分数条class MyBattleScoreView(context: Context) : FrameLayout(context) {// 内部实现分数显示逻辑}// 示例:全局 VS 面板class MyBattleContainer(context: Context) : FrameLayout(context) {private val battleTimeView = ImageView(context)// 内部实现倒计时和 VS 动画逻辑}
VideoWidgetAdapter 中,实现剩余的 PK 视图方法。class VideoWidgetAdapter(private val context: Context) : VideoViewAdapter {// 省略 createCoGuestView / createCoHostView 实现...// 1. 创建【PK 单人信息】挂件 (显示在每个主播视频上方)override fun createBattleView(seatInfo: SeatInfo): View? {return MyBattleScoreView(context)}// 2. 创建【PK 全局容器】挂件 (显示在整个视频区域上方)override fun createBattleContainerView(): View? {return MyBattleContainer(context)}}
VideoWidgetAdapter 注入到直播核心流程中。LiveCoreView 并通过 setVideoViewAdapter 设置适配器。import android.os.Bundleimport androidx.appcompat.app.AppCompatActivityimport android.view.ViewGroupimport com.trtc.uikit.livekit.features.anchorview.AnchorViewimport io.trtc.tuikit.atomicxcore.api.live.LiveInfoimport io.trtc.tuikit.atomicxcore.api.view.LiveCoreViewclass AnchorActivity : AppCompatActivity() {private lateinit var anchorView: AnchorViewprivate lateinit var widgetAdapter: VideoWidgetAdapteroverride fun onCreate(savedInstanceState: Bundle?) {super.onCreate(savedInstanceState)// 初始化适配器对象widgetAdapter = VideoWidgetAdapter(this)// 假设此处已获取 liveInfoval liveInfo = LiveInfo()// 1. 仅传入 Context 初始化 LiveCoreViewval videoView = LiveCoreView(this)// 2. 调用 setVideoViewAdapter 传入自定义适配器videoView.setVideoViewAdapter(widgetAdapter)// 3. 将核心视频组件传入外层容器anchorView = AnchorView(this, liveInfo, videoView)setContentView(anchorView, ViewGroup.LayoutParams(ViewGroup.LayoutParams.MATCH_PARENT,ViewGroup.LayoutParams.MATCH_PARENT))}}
AudienceContainerViewDelegate 来回调核心视图的创建时机。import android.os.Bundleimport androidx.appcompat.app.AppCompatActivityimport io.trtc.tuikit.atomicxcore.api.live.LiveInfoimport io.trtc.tuikit.atomicxcore.api.view.LiveCoreViewclass AudienceActivity : AppCompatActivity(), AudienceContainerViewDelegate {private lateinit var audienceView: AudienceContainerViewprivate lateinit var widgetAdapter: VideoWidgetAdapteroverride fun onCreate(savedInstanceState: Bundle?) {super.onCreate(savedInstanceState)widgetAdapter = VideoWidgetAdapter(this)val roomId = "your_room_id"audienceView = AudienceContainerView(this, roomId)audienceView.setDelegate(this)setContentView(audienceView)}override fun onCreateCoreView(liveInfo: LiveInfo): LiveCoreView? {val view = LiveCoreView(this)// 注入适配器view.setVideoViewAdapter(widgetAdapter)return view}}
PK 等复杂场景时,您会发现 SeatInfo 只提供了基础麦位信息。如果您需要获取实时倒计时、PK 分数等业务数据,就需要在您的自定义视图中对接 AtomicXCore 中的核心数据。Android 的组件装载链路中,LiveCoreView 的适配器赋值必须在它被传入 AnchorView 之前完成。如果 AnchorView 已经完成了初始化,它内部会直接加载 SDK 的默认挂件视图。new LiveCoreView > 再调用 setVideoViewAdapter 注入自定义 UI > 最后实例化 AnchorView 并传入配置好的 CoreView。VideoViewAdapter 是一种完全接管模式。一旦您设置了自定义的代理,SDK 默认的 AnchorView/AudienceView 内部实现的代理逻辑就会完全失效。PK 视图),您可以:LiveKit 源码中默认的实现类,在您的代理中返回这些默认类的实例。Android 经典的事件分发冲突问题。由于前景视图(.FOREGROUND)叠加在视频渲染层之上:CustomView 设置了 isClickable = true 或注册了 setOnClickListener。ViewGroup(例如 FrameLayout),请检查其父容器是否错误地拦截了触摸事件(例如被外层控件触发了 onInterceptTouchEvent 且返回了 true)。文档反馈