ITPub博客

首页 > 应用开发 > IT综合 > Android技术分享| 【Android 自定义View】多人视频通话控件

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

原创 IT综合 作者:anyRTC 时间:2022-03-01 10:33:55 0 删除 编辑

【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_width= "match_parent"
    android:layout_height= "match_parent"
    android:background= "#2C3033"
    tools:parentTag= "android.widget.RelativeLayout" >
   
    <!--视频View-->
    < TextureView
        android:id= "@+id/video_view"
        android:layout_height= "match_parent"
        android:layout_width= "match_parent" >
    </ TextureView >

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

    <!--名字-->
    < TextView
        android:id= "@+id/tv_user_name"
        android:layout_width= "wrap_content"
        android:layout_height= "wrap_content"
        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_height= "20dp"
        android:layout_width= "20dp"
        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_height= "30dp"
        android:layout_width= "30dp"
        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中体现~敬请期待!

来自 “ ITPUB博客 ” ,链接:https://blog.itpub.net/70013909/viewspace-2861639/ ,如需转载,请注明出处,否则将追究法律责任。

请登录后发表评论 登录
全部评论

注册时间:2022-02-15

  • 博文量
    125
  • 访问量
    68146