废话
今天我们来编写音视频布局
在上一篇中,我们写了音视频的Activity,还有它的布局文件,但是它里面的两个子View还没有讲怎么写,现在我们来慢慢分析
音视频的布局
activity_media.xml
<?xml version="1.0" encoding="utf-8"?>
<RelativeLayout xmlns:android="http://schemas.android.com/apk/res/android"
android:layout_width="match_parent"
android:layout_height="match_parent">
<com.yk.mchat.app.widget.VideoLayout
android:id="@+id/media_video_layout"
android:layout_width="match_parent"
android:layout_height="match_parent" />
<com.yk.mchat.app.widget.VoiceLayout
android:id="@+id/media_voice_layout"
android:layout_width="match_parent"
android:layout_height="match_parent"/>
</RelativeLayout>
这个是上一篇文章写的布局,它里面有两个子View
- VideoLayout:视频布局
- VoiceLayout:音频布局
我们先来看音频布局
VoiceLayout.java
package com.yk.mchat.app.widget;
import android.annotation.SuppressLint;
import android.content.Context;
import android.support.design.widget.FloatingActionButton;
import android.util.AttributeSet;
import android.view.LayoutInflater;
import android.view.View;
import android.widget.LinearLayout;
import android.widget.RelativeLayout;
import android.widget.TextView;
import com.bumptech.glide.Glide;
import com.hyphenate.chat.EMClient;
import com.hyphenate.exceptions.EMNoActiveCallException;
import com.yk.mchat.R;
import com.yk.mchat.model.contacts.User;
import de.hdodenhof.circleimageview.CircleImageView;
public class VoiceLayout extends RelativeLayout {
public static final int TYPE_CALL = 0;
public static final int TYPE_ANSWER = 1;
private View mView;
private Context mContext;
private CircleImageView mAvatar;
private TextView mNickname;
private LinearLayout mDelayLayout;
private FloatingActionButton mRejectFab;
private FloatingActionButton mAnswerFab;
private FloatingActionButton mEndFab;
private int mType;
public VoiceLayout(Context context) {
this(context, null);
}
public VoiceLayout(Context context, AttributeSet attrs) {
super(context, attrs);
mView = LayoutInflater.from(context).inflate(R.layout.layout_voice, this);
fbv();
init();
event();
}
private void fbv() {
mAvatar = mView.findViewById(R.id.layout_voice_avatar);
mNickname = mView.findViewById(R.id.layout_voice_nickname);
mDelayLayout = mView.findViewById(R.id.layout_voice_delay_layout);
mRejectFab = mView.findViewById(R.id.layout_voice_reject_fab);
mAnswerFab = mView.findViewById(R.id.layout_voice_answer_fab);
mEndFab = mView.findViewById(R.id.layout_voice_end_fab);
}
private void init() {
}
private void event() {
mRejectFab.setOnClickListener(new OnClickListener() {
@Override
public void onClick(View view) {
rejectCall();
}
});
mAnswerFab.setOnClickListener(new OnClickListener() {
@Override
public void onClick(View view) {
answerCall();
}
});
mEndFab.setOnClickListener(new OnClickListener() {
@Override
public void onClick(View view) {
endCall();
}
});
}
private void rejectCall() {
try {
EMClient.getInstance().callManager().rejectCall();
} catch (EMNoActiveCallException e) {
e.printStackTrace();
}
}
private void answerCall() {
try {
EMClient.getInstance().callManager().answerCall();
} catch (Exception e) {
e.printStackTrace();
}
}
private void endCall() {
try {
EMClient.getInstance().callManager().endCall();
} catch (EMNoActiveCallException e) {
e.printStackTrace();
}
}
public void setUserInfo(User user) {
Glide.with(mContext).load(user.getAvatar().getFileUrl()).into(mAvatar);
mNickname.setText(user.getNickname());
}
@SuppressLint("RestrictedApi")
public void setType(int type) {
mType = type;
switch (mType) {
case TYPE_CALL:
mDelayLayout.setVisibility(GONE);
mEndFab.setVisibility(VISIBLE);
break;
case TYPE_ANSWER:
mDelayLayout.setVisibility(VISIBLE);
mEndFab.setVisibility(GONE);
break;
default:
break;
}
}
/**
* 电话接通
*/
@SuppressLint("RestrictedApi")
public void connect() {
mDelayLayout.setVisibility(GONE);
mEndFab.setVisibility(VISIBLE);
}
}
layout_voice
<?xml version="1.0" encoding="utf-8"?>
<RelativeLayout xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:app="http://schemas.android.com/apk/res-auto"
android:layout_width="match_parent"
android:layout_height="match_parent">
<de.hdodenhof.circleimageview.CircleImageView
android:id="@+id/layout_voice_avatar"
android:layout_width="72dp"
android:layout_height="72dp"
android:layout_centerHorizontal="true"
android:layout_marginTop="72dp"
android:scaleType="centerCrop"
android:src="@drawable/ic_person_black_48dp"
app:civ_border_width="2dp"
app:civ_border_color="@color/colorBlack"/>
<TextView
android:id="@+id/layout_voice_nickname"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:layout_below="@id/layout_voice_avatar"
android:gravity="center"
android:text="Nickname" />
<LinearLayout
android:id="@+id/layout_voice_delay_layout"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:orientation="horizontal"
android:layout_alignParentBottom="true"
android:layout_centerHorizontal="true"
android:gravity="center"
android:layout_marginBottom="72dp">
<android.support.design.widget.FloatingActionButton
android:id="@+id/layout_voice_reject_fab"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_marginTop="16dp"
android:layout_marginBottom="16dp"
android:layout_marginRight="32dp"
android:src="@drawable/ic_call_end_white_24dp"
app:fabSize="normal"
app:backgroundTint="@color/colorPrimary"
app:rippleColor="@color/colorAccent"/>
<android.support.design.widget.FloatingActionButton
android:id="@+id/layout_voice_answer_fab"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_marginTop="16dp"
android:layout_marginBottom="16dp"
android:layout_marginLeft="32dp"
android:src="@drawable/ic_call_white_24dp"
app:fabSize="normal"
app:backgroundTint="@color/colorPrimary"
app:rippleColor="@color/colorAccent"/>
</LinearLayout>
<android.support.design.widget.FloatingActionButton
android:id="@+id/layout_voice_end_fab"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_centerHorizontal="true"
android:layout_alignParentBottom="true"
android:layout_marginBottom="88dp"
android:src="@drawable/ic_call_end_white_24dp"
app:fabSize="normal"
app:backgroundTint="@color/colorAccent"
app:rippleColor="@color/colorPrimary"/>
</RelativeLayout>
可以看到,音频布局比较简单:
- 自定义布局继承自RelativeLayout
- 布局上方是用户头像和昵称
- 下方是三个按钮:拒接、挂断、接听
接下来我们来看视频界面
VideoLayout.java
package com.yk.mchat.app.widget;
import android.content.Context;
import android.util.AttributeSet;
import android.view.View;
import android.view.ViewGroup;
public class VideoLayout extends ViewGroup {
public VideoLayout(Context context) {
this(context, null);
}
public VideoLayout(Context context, AttributeSet attrs) {
super(context, attrs);
}
@Override
protected void onMeasure(int widthMeasureSpec, int heightMeasureSpec) {
int childCount = getChildCount();
if (childCount == 1) {
measureOne(widthMeasureSpec, heightMeasureSpec);
} else if (childCount == 2) {
measureTwo(widthMeasureSpec, heightMeasureSpec);
}
super.onMeasure(widthMeasureSpec, heightMeasureSpec);
}
private void measureOne(int widthMeasureSpec, int heightMeasureSpec) {
View child = getChildAt(0);
child.measure(widthMeasureSpec, heightMeasureSpec);
}
private void measureTwo(int widthMeasureSpec, int heightMeasureSpec) {
View remoteChild = getChildAt(0);
remoteChild.measure(widthMeasureSpec, heightMeasureSpec);
View localChild = getChildAt(1);
int localChildWidth = MeasureSpec.makeMeasureSpec(MeasureSpec.getSize(widthMeasureSpec) / 3, MeasureSpec.EXACTLY);
int localChildHeight = MeasureSpec.makeMeasureSpec(MeasureSpec.getSize(heightMeasureSpec) / 4, MeasureSpec.EXACTLY);
localChild.measure(localChildWidth,localChildHeight);
}
@Override
protected void onLayout(boolean b, int i, int i1, int i2, int i3) {
int childCount=getChildCount();
if(childCount==1){
layoutOne();
}else if(childCount==2){
layoutTwo();
}
}
private void layoutOne() {
View child=getChildAt(0);
child.layout(0,0,child.getMeasuredWidth(),child.getMeasuredHeight());
}
private void layoutTwo() {
View remoteChild=getChildAt(0);
remoteChild.layout(0,0,remoteChild.getMeasuredWidth(),remoteChild.getMeasuredHeight());
View localChild=getChildAt(1);
localChild.layout(localChild.getMeasuredWidth()*2,localChild.getMeasuredHeight()*3,localChild.getMeasuredWidth()*3,localChild.getMeasuredHeight()*4);
}
}
视频界面只是用于显示双方视频,所有就没有xml文件
可以看到,我们重写了ViewGroup的onMeasure和onLayout方法,这两个方法分别是测量和布局,为什么要重写这两个方法呢,因为当我们在视频通话的时候有两种情况,一是呼叫中,二是接通,呼叫中我们只需要显示,也只可以显示自己的画面,接通时两方的画面都要有,所有我们要做动态的改变画面的显示。
今天就到这里。