仿微信视频通话大小视图切换(SurfaceView实现)(1)

private MediaPlayer mediaPlayer;

private MediaPlayer mediaPlayer2;

@Override

protected void onCreate(Bundle savedInstanceState) {

super.onCreate(savedInstanceState);

//如果判断有刘海屏不让填充到状态栏

if (DisplayUtil.hasNotchScreen(this)) {

getWindow().addFlags(

WindowManager.LayoutParams.FLAG_KEEP_SCREEN_ON

| WindowManager.LayoutParams.FLAG_LAYOUT_IN_SCREEN);

} else {

getWindow().addFlags(

WindowManager.LayoutParams.FLAG_KEEP_SCREEN_ON

| WindowManager.LayoutParams.FLAG_LAYOUT_IN_SCREEN

| WindowManager.LayoutParams.FLAG_LAYOUT_NO_LIMITS);

}

setContentView(R.layout.activity_main);

llCallContainer = findViewById(R.id.ll_call_container);

rlRemote = findViewById(R.id.rl_remote);

surfaceviewRemote = findViewById(R.id.surfaceview_remote);

rlLocal = findViewById(R.id.rl_local);

surfaceviewLocal = findViewById(R.id.surfaceview_local);

surfaceviewRemote.setOnClickListener(this);

surfaceviewLocal.setOnClickListener(this);

//初始化视频源

mediaPlayer = new MediaPlayer();

mediaPlayer2 = new MediaPlayer();

SurfaceHolder remoteHolder = surfaceviewRemote.getHolder();

remoteHolder.setFormat(PixelFormat.TRANSPARENT);

remoteHolder.addCallback(new SurfaceHolder.Callback() {

@Override

public void surfaceCreated(SurfaceHolder holder) {

play2();

LogUtils.e(“Remote + surfaceCreated”);

}

@Override

public void surfaceChanged(SurfaceHolder holder, int format,

int width, int height) {

LogUtils.e( “Remote + surfaceChanged”);

}

@Override

public void surfaceDestroyed(SurfaceHolder holder) {

pause2();

LogUtils.e( “Remote + surfaceDestroyed”);

}

});

surfaceviewLocal.setZOrderOnTop(true);

surfaceviewLocal.setZOrderMediaOverlay(true);

SurfaceHolder localHolder = surfaceviewLocal.getHolder();

localHolder.setFormat(PixelFormat.TRANSPARENT);

localHolder.addCallback(new SurfaceHolder.Callback() {

@Override

public void surfaceCreated(SurfaceHolder holder) {

play();

LogUtils.e( “Local + surfaceCreated”);

}

@Override

public void surfaceChanged(SurfaceHolder holder, int format,

int width, int height) {

LogUtils.e(“Local + surfaceChanged”);

}

@Override

public void surfaceDestroyed(SurfaceHolder holder) {

pause();

LogUtils.e( “Local + surfaceDestroyed”);

}

});

defaultLocalMargin = DisplayUtil.dip2px(this, defaultLocalMargin);

defaultLocalwidth = DisplayUtil.dip2px(this, defaultLocalwidth);

defaultLocalHeight = (int) (DisplayUtil.getScreenHeight(this) / (float) DisplayUtil.getScreenWidth(this) * defaultLocalwidth);

zoomOpera(rlRemote, surfaceviewRemote, rlLocal, surfaceviewLocal);

uiHandler = new Handler();

postDelayedCloseBtn();

}

@Override

protected void onDestroy() {

if (mediaPlayer != null) {

mediaPlayer.stop();

mediaPlayer.release();

mediaPlayer = null;

}

if (mediaPlayer2 != null) {

mediaPlayer2.stop();

mediaPlayer2.release();

mediaPlayer2 = null;

}

if (uiHandler != null) {

uiHandler.removeCallbacksAndMessages(null);

}

super.onDestroy();

}

/**

  • 播放源

*/

private void play() {

try {

//添加播放视频的路径与配置MediaPlayer

AssetFileDescriptor fileDescriptor = getResources().openRawResourceFd(R.raw.video4);

mediaPlayer.reset();

//给mMediaPlayer添加预览的SurfaceHolder,将播放器和SurfaceView关联起来

mediaPlayer.setDisplay(surfaceviewLocal.getHolder());

mediaPlayer.setDataSource(fileDescriptor.getFileDescriptor(),

fileDescriptor.getStartOffset(),

fileDescriptor.getLength());

//设置视频输出到SurfaceView

// mediaPlayer.setDisplay(surfaceviewLocal.getHolder());

mediaPlayer.prepare();

mediaPlayer.setOnPreparedListener(new MediaPlayer.OnPreparedListener() {

@Override

public void onPrepared(MediaPlayer mp) {

mediaPlayer.start();

}

});

} catch (IOException e) {

e.printStackTrace();

}

}

/**

  • 播放源2

*/

private void play2() {

try {

//添加播放视频的路径与配置MediaPlayer

AssetFileDescriptor fileDescriptor = getResources().openRawResourceFd(R.raw.video5);

mediaPlayer2.reset();

//给mMediaPlayer添加预览的SurfaceHolder,将播放器和SurfaceView关联起来

mediaPlayer2.setDisplay(surfaceviewRemote.getHolder());

mediaPlayer2.setDataSource(fileDescriptor.getFileDescriptor(),

fileDescriptor.getStartOffset(),

fileDescriptor.getLength());

//设置视频输出到SurfaceView

// mediaPlayer2.setDisplay(surfaceviewRemote.getHolder());

mediaPlayer2.prepare();

mediaPlayer2.setOnPreparedListener(new MediaPlayer.OnPreparedListener() {

@Override

public void onPrepared(MediaPlayer mp) {

mediaPlayer2.start();

}

});

} catch (IOException e) {

e.printStackTrace();

}

}

/**

  • 暂停1

*/

private void pause() {

if (mediaPlayer.isPlaying()) {

mediaPlayer.pause();

} else {

mediaPlayer.start();

}

}

/**

  • 暂停1

*/

private void pause2() {

if (mediaPlayer2.isPlaying()) {

mediaPlayer2.pause();

} else {

mediaPlayer2.start();

}

}

/**

  • 停止

*/

private void stop() {

if (mediaPlayer != null && mediaPlayer.isPlaying()) {

mediaPlayer.stop();

mediaPlayer.release();

}

}

/**

  • 停止

*/

private void stop2() {

if (mediaPlayer2 != null && mediaPlayer.isPlaying()) {

mediaPlayer2.stop();

mediaPlayer2.release();

}

}

@Override

public void onClick(View view) {

switch (view.getId()) {

case R.id.surfaceview_local:

LogUtils.d(" onClick surfaceview_local: " + mSate);

if (mSate) {

zoomOpera(rlLocal, surfaceviewLocal, rlRemote, surfaceviewRemote);

mSate = false;

} else {

changeStatus();

}

break;

case R.id.surfaceview_remote:

LogUtils.d(" onClick surfaceview_remote: " + mSate);

if (!mSate) {

zoomOpera(rlRemote, surfaceviewRemote, rlLocal, surfaceviewLocal);

mSate = true;

} else {

changeStatus();

}

break;

default:

break;

}

}

/**

  • 改变转态烂的显示隐藏

*/

protected void changeStatus() {

if (llCallContainer.getVisibility() == View.GONE) {

llCallContainer.setVisibility(View.VISIBLE);

showStatusBar();

postDelayedCloseBtn();

} else {

llCallContainer.setVisibility(View.GONE);

hideStatusBar();

}

}

/**

  • 大小视图切换 (小视图在前面、大视图在后面)

  • @param sourcView 之前相对布局大小

  • @param beforeview 之前surfaceview

  • @param detView 之后相对布局大小

  • @param afterview 之后的surfaceview

*/

private void zoomOpera(View sourcView, SurfaceView beforeview,

View detView, SurfaceView afterview) {

RelativeLayout paretview = (RelativeLayout) sourcView.getParent();

paretview.removeView(detView);

paretview.removeView(sourcView);

//设置远程大视图RelativeLayout 的属性

RelativeLayout.LayoutParams params1 = new RelativeLayout.LayoutParams(

RelativeLayout.LayoutParams.MATCH_PARENT,

RelativeLayout.LayoutParams.MATCH_PARENT);

params1.addRule(RelativeLayout.CENTER_IN_PARENT, RelativeLayout.TRUE);

beforeview.setZOrderMediaOverlay(true);

beforeview.getHolder().setFormat(PixelFormat.TRANSPARENT);

sourcView.setLayoutParams(params1);

//设置本地小视图RelativeLayout 的属性

params1 = new RelativeLayout.LayoutParams(defaultLocalwidth, defaultLocalHeight);

params1.addRule(RelativeLayout.ALIGN_PARENT_RIGHT, RelativeLayout.TRUE);

params1.setMargins(0, defaultLocalMargin, defaultLocalMargin, 0);

//在调用setZOrderOnTop(true)之后调用了setZOrderMediaOverlay(true) 遮挡问题

afterview.setZOrderOnTop(true);

afterview.setZOrderMediaOverlay(true);

afterview.getHolder().setFormat(PixelFormat.TRANSPARENT);

detView.setLayoutParams(params1);

paretview.addView(sourcView);

paretview.addView(detView);

}

/**

  • 开启取消延时动画

*/

private void postDelayedCloseBtn() {

uiHandler.removeCallbacksAndMessages(null);

uiHandler.postDelayed(new Runnable() {

@Override

public void run() {

llCallContainer.setVisibility(View.GONE);

hideStatusBar();

}

}, 5000);

}

/**

  • 隐藏标题栏

*/

private void hideStatusBar() {

WindowManager.LayoutParams attrs = getWindow().getAttributes();

attrs.flags |= WindowManager.LayoutParams.FLAG_FULLSCREEN;

getWindow().setAttributes(attrs);

}

/**

  • 显示标题栏

*/

private void showStatusBar() {

WindowManager.LayoutParams attrs = getWindow().getAttributes();

attrs.flags &= ~WindowManager.LayoutParams.FLAG_FULLSCREEN;

getWindow().setAttributes(attrs);

}

}

1.2、activity_main.xml


<?xml version="1.0" encoding="utf-8"?>

<FrameLayout

xmlns:android=“http://schemas.android.com/apk/res/android”

android:layout_width=“match_parent”

android:layout_height=“match_parent”>

<RelativeLayout

android:layout_width=“match_parent”

android:layout_height=“match_parent”>

<RelativeLayout

android:id=“@+id/rl_remote”

android:layout_width=“match_parent”

android:layout_height=“wrap_content”>

<SurfaceView

android:id=“@+id/surfaceview_remote”

android:layout_width=“match_parent”

android:layout_height=“match_parent”/>

<RelativeLayout

android:id=“@+id/rl_local”

android:layout_width=“wrap_content”

android:layout_height=“wrap_content”

android:layout_alignParentRight=“true”>

<SurfaceView

android:id=“@+id/surfaceview_local”

android:layout_width=“wrap_content”

android:layout_height=“wrap_content”/>

<LinearLayout

android:id=“@+id/ll_call_container”

android:layout_width=“match_parent”

android:layout_height=“wrap_content”

android:layout_gravity=“bottom”

android:layout_marginBottom=“25dp”

android:gravity=“center_horizontal”

android:orientation=“horizontal”>

<TextView

android:id=“@+id/tv_call_quiet”

android:layout_width=“wrap_content”

android:layout_height=“wrap_content”

android:layout_weight=“1”

android:drawablePadding=“10dp”

android:drawableTop=“@mipmap/chat_video_change_voice_img”

android:gravity=“center_horizontal”

android:text=“切到语音聊天”

android:textColor=“#ffffff”

android:textSize=“12sp”/>

<TextView

android:id=“@+id/tv_handup_call”

android:layout_width=“wrap_content”

android:layout_height=“wrap_content”

android:layout_weight=“1”

android:drawablePadding=“10dp”

android:drawableTop=“@mipmap/chat_video_guaduan_img_normal”

android:gravity=“center_horizontal”

android:text=“挂断”

android:textColor=“#ffffff”

android:textSize=“12sp”/>

<TextView

android:id=“@+id/tv_change_camera”

android:layout_width=“wrap_content”

android:layout_height=“wrap_content”

android:layout_weight=“1”

android:drawablePadding=“10dp”

android:drawableTop=“@mipmap/chat_video_change_camera_img”

android:gravity=“center_horizontal”

android:text=“转换摄像头”

android:textColor=“#ffffff”

android:textSize=“12sp”/>

1.3、DisplayUtil.java


public class DisplayUtil {
自我介绍一下,小编13年上海交大毕业,曾经在小公司待过,也去过华为、OPPO等大厂,18年进入阿里一直到现在。

深知大多数初中级Android工程师,想要提升技能,往往是自己摸索成长或者是报班学习,但对于培训机构动则近万的学费,着实压力不小。自己不成体系的自学效果低效又漫长,而且极易碰到天花板技术停滞不前!

因此收集整理了一份《2024年Android移动开发全套学习资料》,初衷也很简单,就是希望能够帮助到想自学提升又不知道该从何学起的朋友,同时减轻大家的负担。

img

img

img

img

既有适合小白学习的零基础资料,也有适合3年以上经验的小伙伴深入学习提升的进阶课程,基本涵盖了95%以上Android开发知识点,真正体系化!

由于文件比较大,这里只是将部分目录截图出来,每个节点里面都包含大厂面经、学习笔记、源码讲义、实战项目、讲解视频,并且会持续更新!

如果你觉得这些内容对你有帮助,可以扫码获取!!(备注:Android)

尾声

在我的博客上很多朋友都在给我留言,需要一些系统的面试高频题目。之前说过我的复习范围无非是个人技术博客还有整理的笔记,考虑到笔记是手写版不利于保存,所以打算重新整理并放到网上,时间原因这里先列出面试问题,题解详见:


展示学习笔记

《互联网大厂面试真题解析、进阶开发核心学习笔记、全套讲解视频、实战项目源码讲义》点击传送门即可获取!

。**

[外链图片转存中…(img-08uPk240-1712914898279)]

[外链图片转存中…(img-04ZQcj0u-1712914898280)]

[外链图片转存中…(img-7BXGaMGs-1712914898280)]

[外链图片转存中…(img-qKxU4xeo-1712914898280)]

[外链图片转存中…(img-DnNWMPsl-1712914898281)]

既有适合小白学习的零基础资料,也有适合3年以上经验的小伙伴深入学习提升的进阶课程,基本涵盖了95%以上Android开发知识点,真正体系化!

由于文件比较大,这里只是将部分目录截图出来,每个节点里面都包含大厂面经、学习笔记、源码讲义、实战项目、讲解视频,并且会持续更新!

如果你觉得这些内容对你有帮助,可以扫码获取!!(备注:Android)

尾声

在我的博客上很多朋友都在给我留言,需要一些系统的面试高频题目。之前说过我的复习范围无非是个人技术博客还有整理的笔记,考虑到笔记是手写版不利于保存,所以打算重新整理并放到网上,时间原因这里先列出面试问题,题解详见:

[外链图片转存中…(img-dMxAt4lM-1712914898281)]
展示学习笔记
[外链图片转存中…(img-IAv9g8T9-1712914898281)]
[外链图片转存中…(img-JxfTfjSE-1712914898281)]

《互联网大厂面试真题解析、进阶开发核心学习笔记、全套讲解视频、实战项目源码讲义》点击传送门即可获取!

  • 4
    点赞
  • 4
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值