Android技術分享| 【Android 自定義View】多人視訊通話控制元件
【Android 自定義View】多人視訊通話控制元件
*以上圖片截自wechat等待中介面
等待中介面
上圖是wechat多人視訊通話時未接通的介面狀態,可見每個人的 View 中大致需包含了以下元素。
- 頭像
- 暱稱
- Loading View
- 視訊 View
- 音訊狀態 icon
所以,我們先寫好每個人的佈局。如下
```xml
<!--視訊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>
```
GroupUserVideoLayout
接著定義自定義View類,GroupUserVideoLayout 新增一些基本的方法。
```kotlin /* * 多人視訊通話中每個使用者的佈局 / 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 ,這個是在外部直接使用的,裡面應當有查詢、新增使用者,移除使用者,根據人數更新佈局位置等功能。
```kotlin 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)
}
} ```
以上就實現了類似wechat視訊通話介面的自定義View,具體使用效果會在下一期的文章所介紹的demo中體現~敬請期待!
「其他文章」
- Android技術分享| ViewPager2離屏載入,實現抖音上下視訊滑動
- Android技術分享| Activity 過渡動畫 — 讓切換更加炫酷
- Linux下玩轉nginx系列(七)---nginx如何實現限流功能
- 技術分享| 如何部署安裝分散式序列號生成器系統
- web技術分享| 【地圖】實現自定義的軌跡回放
- 解決方案| 快對講綜合排程系統
- 實時訊息RTM| 多活架構中的資料一致性問題
- Android技術分享| Context淺析
- Android技術分享| Context淺析
- 螢幕共享的實現與應用
- 技術分析| 即時通訊和實時通訊的區別
- IOS技術分享| ARCallPlus 開源專案(二)
- Android技術分享| Android 中部分記憶體洩漏示例及解決方案
- Android技術分享| 安卓3行程式碼,實現整套音視訊通話功能
- 行業分析| 快對講Poc方案的優勢
- Android技術分享|【自定義View】實現Material Design的Loading效果
- IOS技術分享| ARCallPlus 開源專案(一)
- web技術分享| WebRTC控制攝像機平移、傾斜和縮放
- Android技術分享| anyLive 開源專案
- Android技術分享| 【Android 自定義View】多人視訊通話控制元件