Android仿微信视屏悬浮窗效果
程序员文章站
2023-12-27 10:40:27
在项目中需要对接入的腾讯云音视频,可以悬浮窗显示,悬浮窗可拖拽,并且在悬浮窗不影响其他的activity的焦点。
这个大神的文章android基于腾讯云实时音视频仿微信视频通话最小化...
在项目中需要对接入的腾讯云音视频,可以悬浮窗显示,悬浮窗可拖拽,并且在悬浮窗不影响其他的activity的焦点。
这个大神的文章android基于腾讯云实时音视频仿微信视频通话最小化悬浮,他讲的是视频通话时,将远端视频以悬浮窗形式展示,根据他的代码我进行了部分简化
1.悬浮窗效果:点击缩小按钮,将当前远端视屏加载进悬浮窗,且悬浮窗可拖拽,不影响其他界面焦点;点击悬浮窗可返回原来的activity
2.实现悬浮窗需要:
在androidmanifest中申请悬浮窗权限<uses-permission android:name="android.permission.system_alert_window"/>
在androidmanifest中注册floatwindowservice
3.视屏activity实现:
-将activity置于后台关键代码:movetasktoback(true);//将activity置于后台
-开启悬浮窗
/** * 定义服务绑定的回调 开启视频通话服务连接 */ private serviceconnection mvideocallserviceconnection = new serviceconnection() { @override public void onserviceconnected(componentname name, ibinder service) { // 获取服务的操作对象 floatwindowservice.mybinder binder = (floatwindowservice.mybinder) service; binder.getservice(); } @override public void onservicedisconnected(componentname name) { } }; /* * 开启悬浮video服务 */ private void startvideoservice() { //最小化activity movetasktoback(true);//将activity置于后台 //开启服务显示悬浮框 intent servicevideointent = new intent(this, floatwindowservice.class); mservicebound = bindservice(servicevideointent, mvideocallserviceconnection, context.bind_auto_create);//绑定service }
-悬浮窗结束时
//在ondestroy()与onrestart()中解绑并销毁相关内容 if (mservicebound) { unbindservice(mvideocallserviceconnection);//解绑 mservicebound = false; }
4.悬浮窗实现相关代码:
/** * 视频悬浮窗服务 */ public class floatwindowservice extends service implements view.ontouchlistener { private windowmanager mwindowmanager; private windowmanager.layoutparams wmparams; private layoutinflater inflater; //浮动布局view private view mfloatinglayout; //容器父布局 private view mmainview; //开始触控的坐标,移动时的坐标(相对于屏幕左上角的坐标) private int mtouchstartx, mtouchstarty, mtouchcurrentx, mtouchcurrenty; //开始时的坐标和结束时的坐标(相对于自身控件的坐标) private int mstartx, mstarty, mstopx, mstopy; //判断悬浮窗口是否移动,这里做个标记,防止移动后松手触发了点击事件 private boolean ismove; @override public void oncreate() { super.oncreate(); initwindow();//设置悬浮窗基本参数(位置、宽高等) } @nullable @override public ibinder onbind(intent intent) { currentbiguserid = intent.getstringextra("localuserid"); remoteuserid = intent.getstringextra("remoteuserid"); initfloating();//悬浮框点击事件的处理 return new mybinder(); } public class mybinder extends binder { public floatwindowservice getservice() { return floatwindowservice.this; } } @override public int onstartcommand(intent intent, int flags, int startid) { return super.onstartcommand(intent, flags, startid); } @override public void ondestroy() { super.ondestroy(); if (mfloatinglayout != null) { // 移除悬浮窗口 mwindowmanager.removeview(mfloatinglayout); mfloatinglayout = null; } } /** * 设置悬浮框基本参数(位置、宽高等) */ private void initwindow() { mwindowmanager = (windowmanager) getapplicationcontext().getsystemservice(context.window_service); //设置好悬浮窗的参数 wmparams = getparams(); // 悬浮窗默认显示以左上角为起始坐标 wmparams.gravity = gravity.right | gravity.top; //悬浮窗的开始位置,因为设置的是从右上角开始,所以屏幕左上角是x=屏幕最大值;y=0 wmparams.x = 10; wmparams.y = 120; //得到容器,通过这个inflater来获得悬浮窗控件 inflater = layoutinflater.from(getapplicationcontext()); // 获取浮动窗口视图所在布局 mfloatinglayout = inflater.inflate(r.layout.dlg_floatview, null); // 添加悬浮窗的视图 mwindowmanager.addview(mfloatinglayout, wmparams); } private windowmanager.layoutparams getparams() { wmparams = new windowmanager.layoutparams(); if (build.version.sdk_int >= build.version_codes.o) { wmparams.type = windowmanager.layoutparams.type_application_overlay; } else { wmparams.type = windowmanager.layoutparams.type_phone; } //设置可以显示在状态栏上 wmparams.flags = windowmanager.layoutparams.flag_not_focusable | windowmanager.layoutparams.flag_not_touch_modal | windowmanager.layoutparams.flag_layout_in_screen | windowmanager.layoutparams.flag_layout_inset_decor | windowmanager.layoutparams.flag_watch_outside_touch; //设置悬浮窗口长宽数据 wmparams.width = windowmanager.layoutparams.wrap_content; wmparams.height = windowmanager.layoutparams.wrap_content; return wmparams; } //加载远端视屏:在这对悬浮窗内内容做操作 private void initfloating() { //将子view加载进悬浮窗view mmainview = mfloatinglayout.findviewbyid(r.id.trtc_video_view_layout_float);//悬浮窗父布局 view mchildview = renderview.getchildview();//加载进悬浮窗的子view,这个view来自天转过来的那个activity里面的那个需要加载的view mmainview.addview(mchildview);//将需要悬浮显示的viewadd到mtxcloudvideoview中 //悬浮框触摸事件,设置悬浮框可拖动 mtxcloudvideoview.setontouchlistener(this::ontouch); //悬浮框点击事件 mtxcloudvideoview.setonclicklistener(new view.onclicklistener() { @override public void onclick(view v) { //在这里实现点击重新回到activity intent intent = new intent(floatwindowservice.this, rtcactivity.class);//从该service跳转至该activity会将该activity从后台唤醒,所以activity会走onrestart() intent.setflags(intent.flag_activity_new_task);//从service跳转至rtcactivity,需要intent.flag_activity_new_task,不然会崩溃 startactivity(intent); } }); } //触摸事件 @override public boolean ontouch(view v, motionevent event) { int action = event.getaction(); switch (action) { case motionevent.action_down: ismove = false; mtouchstartx = (int) event.getrawx(); mtouchstarty = (int) event.getrawy(); mstartx = (int) event.getx(); mstarty = (int) event.gety(); break; case motionevent.action_move: mtouchcurrentx = (int) event.getrawx(); mtouchcurrenty = (int) event.getrawy(); wmparams.x += mtouchstartx - mtouchcurrentx; wmparams.y += mtouchcurrenty - mtouchstarty; alog.dtag("floatinglistener() ontouch",mtouchcurrentx,mtouchstartx,mtouchcurrenty,mtouchstarty); mwindowmanager.updateviewlayout(mfloatinglayout, wmparams); mtouchstartx = mtouchcurrentx; mtouchstarty = mtouchcurrenty; break; case motionevent.action_up: mstopx = (int) event.getx(); mstopy = (int) event.gety(); if (math.abs(mstartx - mstopx) >= 1 || math.abs(mstarty - mstopy) >= 1) { ismove = true; } break; default: break; } //如果是移动事件不触发onclick事件,防止移动的时候一放手形成点击事件 return ismove; } }
ps:使用service做悬浮窗的载体是为了,将悬浮框的开启关闭与服务service的绑定解绑所关联起来,开启服务即相当于开启我们的悬浮框,解绑服务则相当于关闭悬浮框,以此来达到更好的控制效果。
以上就是本文的全部内容,希望对大家的学习有所帮助,也希望大家多多支持。