Android技术分享| Android 自定义View多人视频通话控件

Posted anyRTC

tags:

篇首语:本文由小常识网(cha138.com)小编为大家整理,主要介绍了Android技术分享| Android 自定义View多人视频通话控件相关的知识,希望对你有一定的参考价值。

android 自定义View】多人视频通话控件

*以上图片截自微信等待中界面

等待中界面

上图是微信多人视频通话时未接通的界面状态,可见每个人的 View 中大致需包含了以下元素。

  1. 头像
  2. 昵称
  3. Loading View
  4. 视频 View
  5. 音频状态 icon

所以,我们先写好每个人的布局。如下

<merge xmlns:android="http://schemas.android.com/apk/res/android"
    xmlns:tools="http://schemas.android.com/tools"
    xmlns:app="http://schemas.android.com/apk/res-auto"
    android:layout_
    android:layout_
    android:background="#2C3033"
    tools:parentTag="android.widget.RelativeLayout">

    <!--视频View-->
    <TextureView
        android:id="@+id/video_view"
        android:layout_
        android:layout_>
    </TextureView>

    <!--头像-->
    <ImageView
        android:id="@+id/iv_avatar"
        android:src="@drawable/avatar"
        android:layout_
        android:layout_>
    </ImageView>

    <!--名字-->
    <TextView
        android:id="@+id/tv_user_name"
        android:layout_
        android:layout_
        android:layout_alignParentBottom="true"
        android:layout_margin="20dp"
        tools:text="UserName"
        android:background="@android:color/transparent"
        android:textColor="@android:color/white"
        android:textSize="14sp" />

    <!--音频状态Icon-->
    <ImageView
        android:id="@+id/iv_audio_enable"
        android:layout_
        android:layout_
        android:layout_alignParentRight="true"
        android:layout_alignParentBottom="true"
        android:layout_margin="20dp"
        android:src="@drawable/mic_enable">
    </ImageView>

    <!--Loading-->
    <ImageView
        android:id="@+id/iv_loading"
        android:layout_
        android:layout_
        android:layout_centerInParent="true"
        android:src="@drawable/loading">
    </ImageView>

</merge>
GroupUserVideoLayout

接着定义自定义View类,GroupUserVideoLayout 添加一些基本的方法。

/**
 * 多人视频通话中每个用户的布局
 */
class GroupUserVideoLayout @JvmOverloads constructor(mContext:Context): RelativeLayout(mContext) 

    private var videoView:TextureView
    private var ivAvatar:ImageView
    private var ivAudio:ImageView
    private var tvName:TextView
    private var ivLoading:ImageView

    init 
        val root = LayoutInflater.from(mContext).inflate(R.layout.layout_gv_layout,this)
        videoView = root.findViewById(R.id.video_view)
        ivAvatar = root.findViewById(R.id.iv_avatar)
        ivAudio = root.findViewById(R.id.iv_audio_enable)
        tvName = root.findViewById(R.id.tv_user_name)
        ivLoading = root.findViewById(R.id.iv_loading)
    

    //设置昵称
    fun setUserName(userName:String)
        tvName.text = userName
    
    //设置头像
    fun setAvatar(avatarUrl:String)
        ivAvatar.loadUrl(avatarUrl)
    
    //设置音频图标状态
    fun enableAudio(enable:Boolean)
        ivAudio.visibility = if (enable) VISIBLE else GONE
    
    //设置LoadingView状态
    fun setLoadingState(open:Boolean)
        ivLoading.visibility = if (open) VISIBLE else GONE
    

接听后

​ 接听后,对应的设置每个人的头像昵称,去掉 Loading,显示视频。接下来就是要定义多个人进出时,布局的变化了。

- 2个人的时候,左右对齐均分显示
- 3个人的时候品字型显示
- 4个人的时候上下2个均分显示
- 5个人以上则九宫格显示
GroupVideoLayoutManager

定义 GroupVideoLayoutManager ,这个是在外部直接使用的,里面应当有查找、添加用户,移除用户,根据人数更新布局位置等功能。

class GroupVideoLayoutManager constructor(mContext: Context): RelativeLayout(mContext) 

    //自己的ID
    var selfId:String=""
    private val userLayoutList = mutableListOf<LayoutEntity>()
    private var userCount = 0
    private val MAX_USER = 8

    private val oneUserParamList by lazy  LayoutUtils.get1UserParam(mContext,width,height) 
    private val twoUserParamList by lazy  LayoutUtils.get2UserParam(mContext,width,height) 
    private val threeUserParamList by lazy  LayoutUtils.get3UserParam(mContext,width,height) 
    private val fourUserParamList by lazy  LayoutUtils.get4UserParam(mContext,width,height) 
    private val nineUserParamList by lazy  LayoutUtils.get9UserParam(mContext,width,height) 

    /**
     * 根据uid 查找对应的View
     */
    fun findUser(uid:String):GroupUserVideoLayout?
        userLayoutList.find  it.userId==uid ?.let  layoutEntity->
            layoutEntity.layout?.let 
                return it
            ?:let
                return null
            
        ?:let
            return null
        
    

    /**
     * 根据uid 添加对应的View
     */
    fun addUser(uid:String):GroupUserVideoLayout?
        if (userCount>MAX_USER)
            return null
        
        val layout = GroupUserVideoLayout(context)
        userLayoutList.add(LayoutEntity(layout,uid))
        userCount++
        post 
            updateLayout()
        
        return layout
    

    /**
     * 根据uid 移除对应View
     */
    fun removeUser(uid:String)
        userLayoutList.find  it.userId==uid ?.let 
            userLayoutList.remove(it)
            userCount--
        
    

    //更新布局位置
    private fun updateLayout()
        if (userLayoutList.isNullOrEmpty())
            return
        
        val paramsList:ArrayList<LayoutParams>
        when(userCount)
            1->
                paramsList = oneUserParamList
                userLayoutList[0].layout?.layoutParams = paramsList[0]
                return
            
            2->
                paramsList = twoUserParamList
            
            3->
                paramsList = threeUserParamList
            
            4->
                paramsList = fourUserParamList
            
            else->
                paramsList = nineUserParamList
            
        
        var layoutIndex = if (selfId.isEmpty()) 0 else 1
        userLayoutList.forEach 
            if (it.userId == selfId)
                it.layout?.layoutParams = paramsList[0]
            else if (layoutIndex<paramsList.size)
                it.layout?.layoutParams = paramsList[layoutIndex++]
            
        

    

    private inner class LayoutEntity 
        var layout: GroupUserVideoLayout? = null
        var userId = ""

        constructor(layout: GroupUserVideoLayout?, userId: String) 
            this.layout = layout
            this.userId = userId
        
    

    override fun onMeasure(widthMeasureSpec: Int, heightMeasureSpec: Int) 
        val widthSize = MeasureSpec.getSize(widthMeasureSpec)
        val heightSize = MeasureSpec.getSize(heightMeasureSpec)
        if (widthSize == 0 && heightSize == 0) 
            super.onMeasure(widthMeasureSpec, heightMeasureSpec)
            val minSize = Math.min(measuredWidth, measuredHeight)
            setMeasuredDimension(minSize, minSize)
            return
        
        val size: Int
        size = if (widthSize == 0 || heightSize == 0) 
            Math.max(widthSize, heightSize)
         else 
            Math.min(widthSize, heightSize)
        
        val newMeasureSpec = MeasureSpec.makeMeasureSpec(size, MeasureSpec.EXACTLY)
        super.onMeasure(newMeasureSpec, newMeasureSpec)
    

​ 以上就实现了类似微信视频通话界面的自定义View,具体使用效果会在下一期的文章所介绍的demo中体现~敬请期待!

以上是关于Android技术分享| Android 自定义View多人视频通话控件的主要内容,如果未能解决你的问题,请参考以下文章

分享基于Android系统的XMPP即时通讯技术项目实战(仿微信开发架构,自定义控件)

Android技术分享| 自定义LayoutManager

Android技术分享| 自习室自定义View代替通知动画(完)

Android技术分享| Bugly 应用升级自定义UI

Android技术分享| 自定义View实现使用更方便的SeekBar

Android技术分享| 自习室自定义View代替通知动画