Android 大小两个Surface切换

您所在的位置:网站首页 surfaceview不显示 Android 大小两个Surface切换

Android 大小两个Surface切换

2023-12-28 23:43| 来源: 网络整理| 查看: 265

在视频聊天app中一般会有这样的场景,一个大的Surface显示对方的画面,一个略小的Surface显示自己的画面(类似于微信视频聊天),然后点击一下小的那个Surface,会把两个画面对调。今天就来看看,这个是怎么实现的。

先给大家一个直观的认识,来两张截图。 这里写图片描述 这里写图片描述 就是这两个画面,一大一小,大的是自己摄像头的预览画面,小的是对面传过来的画面(现在暂时是这样的。)点击小的画面里面右上角按钮,就可以切换两个换面。具体如何实现,会碰到哪些问题呢? ####方案一:采用切换数据源 这是什么意思呢?就是说,Surface的大小和位置不去改变他,数据绘制的位置,来实现,这个方案且不说实现起来复杂度和可行性,有一个致命的问题,就是在切换Surface的过程中,必然会有重新加载的过程,会导致短暂的黑屏(取决于网络和缓存时间),所以果断放弃了。 ####方案二:改变Surface大小,并改变层级 这个方案理论上是可行的,只是改变下Surface的大小和位置,大的变小,小的变大,然后大的把小的覆盖掉了,再把小的显示到上面来。但是有个问题,修改层级的方法SurfaceView#setZOrderMediaOverlay有个限制,只能在初始的的时候调用,不信可以看官方注释:

/** * Control whether the surface view's surface is placed on top of another * regular surface view in the window (but still behind the window itself). * This is typically used to place overlays on top of an underlying media * surface view. * *

Note that this must be set before the surface view's containing * window is attached to the window manager. * *

Calling this overrides any previous call to {@link #setZOrderOnTop}. */ public void setZOrderMediaOverlay(boolean isMediaOverlay) { mWindowType = isMediaOverlay ? WindowManager.LayoutParams.TYPE_APPLICATION_MEDIA_OVERLAY : WindowManager.LayoutParams.TYPE_APPLICATION_MEDIA; }

Note that this must be set before the surface view’s containing window is attached to the window manager.应该可以看得懂吧。 所以就得想个办法来绕开这个这个限制,最简单的就是先removeView(View)再addView(View)这样就相当于重新做了attach操作,setZOrderMediaOverlay操作也就可以生效了。接下来看看代码具体如何实现

private RelativeLayout.LayoutParams getSmallLayoutParams() { RelativeLayout.LayoutParams lpSmall = null; lpSmall = new RelativeLayout.LayoutParams(mWindowWidth / 2, mWindowHeight / 2); lpSmall.width = mWindowWidth / 2; lpSmall.height = mWindowHeight / 2; lpSmall.topMargin = mWindowHeight / 2; lpSmall.leftMargin = 0; return lpSmall; } private RelativeLayout.LayoutParams getNormalLayoutParams() { RelativeLayout.LayoutParams lpNormal = null; lpNormal = new RelativeLayout.LayoutParams(mWindowWidth / 2, mWindowHeight / 2); lpNormal.width = mWindowWidth; lpNormal.height = mWindowHeight; lpNormal.topMargin = 0; lpNormal.leftMargin = 0; return lpNormal; } private void changeScreen() { mIsChangeScreen = !mIsChangeScreen; RelativeLayout.LayoutParams lpSmall = getSmallLayoutParams(); RelativeLayout.LayoutParams lpNormal = getNormalLayoutParams(); mRlRoot.removeView(mSurfaceViewSmall); mRlRoot.removeView(mSurfaceView); mRlRoot.removeView(mRlFun); if (mIsChangeScreen) { mSurfaceView.setLayoutParams(lpSmall); mRlRoot.addView(mSurfaceView); mSurfaceViewSmall.setLayoutParams(lpNormal); mRlRoot.addView(mSurfaceViewSmall); // mSurfaceView.setZOrderOnTop(true); mSurfaceView.setZOrderMediaOverlay(true); } else { mSurfaceViewSmall.setLayoutParams(lpSmall); mRlRoot.addView(mSurfaceViewSmall); mSurfaceView.setLayoutParams(lpNormal); mRlRoot.addView(mSurfaceView); // mSurfaceViewSmall.setZOrderOnTop(true); mSurfaceViewSmall.setZOrderMediaOverlay(true); } mRlRoot.addView(mRlFun); }

点击小画面右上角按钮的时候,调用changeScreen()方法,这个代码其实不难理解,先removeView调大小两个Surface,修改LayoutParams后一次addView进去,然后通过调用setZOrderMediaOverlay方法修改层级关系,就实现了大小和层级的切换。 这里还有一个mRlRoot.removeView(mRlFun);和mRlRoot.addView(mRlFun);其实是把其他控件的移除和加入操作,目的是防止Surface把其他控件覆盖调。

####tips: setZOrderOnTop&setZOrderMediaOverlay 这里再简单讲解下这两个方法的区别,我再网上查到很多关于多个Surface重叠显示,放置遮盖其他控件的解决方法,都是说先调用setZOrderOnTop(true)方法,再调用setZOrderMediaOverlay(true),先不评价这个方法的正确性(因为这样确实可以达到目的),我们先看看这两个方法的含义。 setZOrderMediaOverlay方法的源码注释我已经在前面有贴,意思就是"把目标Surface显示在其他Surface之前,如果以前有其他控件覆盖,任然会覆盖在它上面" 然后setZOrderOnTop方法,

/** * Control whether the surface view's surface is placed on top of its * window. Normally it is placed behind the window, to allow it to * (for the most part) appear to composite with the views in the * hierarchy. By setting this, you cause it to be placed above the * window. This means that none of the contents of the window this * SurfaceView is in will be visible on top of its surface. * *

Note that this must be set before the surface view's containing * window is attached to the window manager. * *

Calling this overrides any previous call to {@link #setZOrderMediaOverlay}. */ public void setZOrderOnTop(boolean onTop) { if (onTop) { mWindowType = WindowManager.LayoutParams.TYPE_APPLICATION_PANEL; // ensures the surface is placed below the IME mLayout.flags |= WindowManager.LayoutParams.FLAG_ALT_FOCUSABLE_IM; } else { mWindowType = WindowManager.LayoutParams.TYPE_APPLICATION_MEDIA; mLayout.flags &= ~WindowManager.LayoutParams.FLAG_ALT_FOCUSABLE_IM; } }

这个方法的意思就是“调用之后,会把目标Surface显示到window的最前面,包括其他控件” 意思已经很明显了 ,我们在Surface上要显示一下其他控件,比如小画面右上角的按钮,所以调用setZOrderMediaOverlay(true)是最好的,进过试验,只调用setZOrderMediaOverlay(true)就可以达到想要的结果。 所以我就不太理解,为什么那么多博文要说同时调用两个方法呢。总结下自己的经验,希望对大家有所帮助。

最近研究了一下,只需要setZOrderMediaOverlay,来切换SurfaceView的层级关系。不过需要注意的是: 1.上层的SurfaceView需要调用setZOrderMediaOverlay(true),底层的SurfaceView需要调用setZOrderMediaOverlay(false) 2.需要切换的两个SurfaceView必须要有大小变化 3.自测安卓8 9 10版本都是可以的,但是安卓6会有问题



【本文地址】


今日新闻


推荐新闻


CopyRight 2018-2019 办公设备维修网 版权所有 豫ICP备15022753号-3