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

文章详细描述了一个Android应用中的媒体播放功能,涉及MediaPlayer的使用、SurfaceView的集成以及处理刘海屏的适配。同时展示了如何控制播放、暂停和停止,以及实现视图大小的动态切换。
摘要由CSDN通过智能技术生成

} 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 {

/**

  • 获取屏幕宽度

  • @param ctx

  • @return

*/

public static int getScreenWidth(Context ctx) {

WindowManager wm = (WindowManager) ctx.getSystemService(Context.WINDOW_SERVICE);

DisplayMetrics dm = new DisplayMetrics();

wm.getDefaultDisplay().getMetrics(dm);

return dm.widthPixels;

}

/**

  • 获取屏幕高度

  • @param ctx

  • @return

*/

public static int getScreenHeight(Context ctx) {

WindowManager wm = (WindowManager) ctx.getSystemService(Context.WINDOW_SERVICE);

DisplayMetrics dm = new DisplayMetrics();

wm.getDefaultDisplay().getMetrics(dm);

return dm.heightPixels;

}

/**

  • dp转px

  • @param context 上下文

  • @param dpValue dp值

  • @return

*/

public static int dip2px(Context context, float dpValue) {

final float scale = context.getResources().getDisplayMetrics().density;

return (int) (dpValue * scale + 0.5f);

}

/**

  • px 转 dp

  • @param context 上下文

  • @param pxValue px值

  • @return

*/

public static int px2dip(Context context, float pxValue) {

final float scale = context.getResources().getDisplayMetrics().density;

return (int) (pxValue / scale + 0.5f);

}

/**

  • 判断是否是刘海屏

  • @return

*/

public static boolean hasNotchScreen(Activity activity) {

if (getInt(“ro.miui.notch”, activity) == 1 || hasNotchAtHuawei(activity) || hasNotchAtOPPO(activity)

|| hasNotchAtVivo(activity) || isAndroidP(activity) != null) { //TODO 各种品牌

return true;

}

return false;

}

/**

  • Android P 刘海屏判断

  • @param activity

  • @return

*/

public static DisplayCutout isAndroidP(Activity activity) {

View decorView = activity.getWindow().getDecorView();

if (decorView != null && android.os.Build.VERSION.SDK_INT >= 28) {

WindowInsets windowInsets = decorView.getRootWindowInsets();

if (windowInsets != null)

return windowInsets.getDisplayCutout();

}

return null;

}

/**

  • 小米刘海屏判断.

  • @return 0 if it is not notch ; return 1 means notch

  • @throws IllegalArgumentException if the key exceeds 32 characters

*/

public static int getInt(String key, Activity activity) {

int result = 0;

try {

ClassLoader classLoader = activity.getClassLoader();

@SuppressWarnings(“rawtypes”)

Class SystemProperties = classLoader.loadClass(“android.os.SystemProperties”);

//参数类型

@SuppressWarnings(“rawtypes”)

Class[] paramTypes = new Class[2];

paramTypes[0] = String.class;

paramTypes[1] = int.class;

Method getInt = SystemProperties.getMethod(“getInt”, paramTypes);

//参数

Object[] params = new Object[2];

params[0] = new String(key);

params[1] = new Integer(0);

result = (Integer) getInt.invoke(SystemProperties, params);

} catch (ClassNotFoundException e) {

e.printStackTrace();

} catch (NoSuchMethodException e) {

e.printStackTrace();

} catch (IllegalAccessException e) {

e.printStackTrace();

} catch (IllegalArgumentException e) {

e.printStackTrace();

} catch (InvocationTargetException e) {

e.printStackTrace();

}

return result;

}

/**

  • 华为刘海屏判断

  • @return

*/

public static boolean hasNotchAtHuawei(Context context) {

boolean ret = false;

try {

ClassLoader classLoader = context.getClassLoader();

Class HwNotchSizeUtil = classLoader.loadClass(“com.huawei.android.util.HwNotchSizeUtil”);

Method get = HwNotchSizeUtil.getMethod(“hasNotchInScreen”);

ret = (boolean) get.invoke(HwNotchSizeUtil);

} catch (ClassNotFoundException e) {

Log.e(“Huawei”, “hasNotchAtHuawei ClassNotFoundException”);

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

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

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

img

img

img

img

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

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

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

总结

本文讲解了我对Android开发现状的一些看法,也许有些人会觉得我的观点不对,但我认为没有绝对的对与错,一切交给时间去证明吧!愿与各位坚守的同胞们互相学习,共同进步!

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

升技能,往往是自己摸索成长或者是报班学习,但对于培训机构动则近万的学费,着实压力不小。自己不成体系的自学效果低效又漫长,而且极易碰到天花板技术停滞不前!**

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

[外链图片转存中…(img-RLgtjFcp-1712914931672)]

[外链图片转存中…(img-78GIRQR6-1712914931672)]

[外链图片转存中…(img-hOveXO82-1712914931673)]

[外链图片转存中…(img-GWhaoy8F-1712914931673)]

[外链图片转存中…(img-f69GoiK3-1712914931673)]

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

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

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

总结

本文讲解了我对Android开发现状的一些看法,也许有些人会觉得我的观点不对,但我认为没有绝对的对与错,一切交给时间去证明吧!愿与各位坚守的同胞们互相学习,共同进步!

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

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值