*以上图片截自微信等待中界面
上图是微信多人视频通话时未接通的界面状态,可见每个人的 View 中大致需包含了以下元素。
头像
昵称
Loading View
视频 View
音频状态 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 >
接着定义自定义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 ,这个是在外部直接使用的,里面应当有查找、添加用户,移除用户,根据人数更新布局位置等功能。
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/ ,如需转载,请注明出处,否则将追究法律责任。