版权说明:本文档由用户提供并上传,收益归属内容提供方,若内容存在侵权,请进行举报或认领
文档简介
【移动应用开发技术】Android如何实现通话最小化悬浮框效果
这篇文章主要介绍Android如何实现通话最小化悬浮框效果,文中介绍的非常详细,具有一定的参考价值,感兴趣的小伙伴们一定要看完!一、实现效果(gif效果可能录制的不是特别好)二、实现思路关于这个功能的实现其实不难,这里我把实现思路拆分为了两步:1、视频通话Activity的最小化。2、视频通话悬浮框的开启具体思路是这样的:当用户点击最小化按钮的时候,最小化我们的视频通话Activity(这时Activity处于后台状态),移除原先在Activity的视频画布(因为我用的是网易云信,这里他们只能允许一个视频画布存在,这里看情况要不要移除),于此同时,延时个几百毫秒,开启悬浮框,新建一个新的视频画布然后动态添加到悬浮框里面去,监听悬浮框的触摸事件,让悬浮框可以拖拽移动;监听悬浮框的点击事件,如果用户点击了悬浮框,则移除悬浮框里面新建的那个视频画布,然后重新调起我们在后台的视频通话Activity,紧接着新建一个新的视频画布重新动态的添加到Activity里面去。关于视频画布的添加移除方法,这里要看一下所接入的第三方SDK,如用的若是网易云信的SDK,他们的方法如下(下面摘自他们的SDK说明文档),也就是说移除画布我只需要传入null就行了。1.Activity是如何实现最小化的?Activity最小化可能你没有听过,但是只要姿势对的话,其实实现起来非常简单,因为Activity本身就自带了一个moveTaskToBack(booleannonRoot),如果我们要实现最小化,只需要调用moveTaskToBack(true)传入一个true值就可以了,但是这里有一个前提,就是需要设置Activity的启动模式为singleInstance模式,两步搞定。(注:这里先记住一个小知识点,就是activity最小化后重新从后台回到前台会回调onRestart()方法)@Override
public
boolean
moveTaskToBack(boolean
nonRoot)
{
return
super.moveTaskToBack(nonRoot);
}2.悬浮框是如何开启的?这里我把悬浮框的实现方法写在一个服务Service里面,将悬浮框的开启关闭与服务Service的绑定解绑所关联起来,开启服务即相当于开启我们的悬浮框,解绑服务则相当于关闭关闭的悬浮框,以此来达到更好的控制效果。a.首先我们声明一个服务类,取名为FloatVideoWindowService:public
class
FloatVideoWindowService
extends
Service
{
@Nullable
@Override
public
IBinder
onBind(Intent
intent)
{
return
new
MyBinder();
}
public
class
MyBinder
extends
Binder
{
public
FloatVideoWindowService
getService()
{
return
FloatVideoWindowService.this;
}
}
@Override
public
void
onCreate()
{
super.onCreate();
}
@Override
public
int
onStartCommand(Intent
intent,
int
flags,
int
startId)
{
return
super.onStartCommand(intent,
flags,
startId);
}
@Override
public
void
onDestroy()
{
super.onDestroy();
}
}b.为悬浮框建立一个布局文件alert_float_video_layout,这里根据需求去写,如果只是像我上面gif那样,只需要悬浮框显示对方的视频画布,那么布局文件可以如下所示:(其中悬浮框大小我这里固定为长80dp,高110dp,id为small_size_preview的Linearlayout主要是一个容器,可以动态的添加view到里面去,也就是我们的视频画布)<?xml
version="1.0"
encoding="utf-8"?>
<LinearLayout
xmlns:android="/apk/res/android"
xmlns:app="/apk/res-auto"
android:layout_width="wrap_content"
android:layout_height="wrap_content">
<FrameLayout
android:layout_width="80dp"
android:layout_height="110dp"
android:background="@color/black_1f2d3d">
<LinearLayout
android:id="@+id/small_size_preview"
android:layout_width="match_parent"
android:layout_height="match_parent"
android:background="@color/transparent"
android:orientation="vertical"
/>
</FrameLayout>
</LinearLayout>c.布局定义好后,接下来就要对悬浮框做一些初始化操作了,初始化操作这里我们放在服务的onCreate()生命周期里面执行,因为只需要执行一次就行了。这里的初始化主要包括对:悬浮框的基本参数(位置,宽高等),悬浮框的点击事件以及悬浮框的触摸事件(即可拖动范围)等的设置,代码注释已经很清楚,直接看代码,如下所示:public
class
FloatVideoWindowService
extends
Service
{
private
WindowManager
mWindowManager;
private
WindowManager.LayoutParams
wmParams;
private
LayoutInflater
inflater;
//constant
private
boolean
clickflag;
//view
private
View
mFloatingLayout;
//浮动布局
private
LinearLayout
smallSizePreviewLayout;
//容器父布局
@Nullable
@Override
public
IBinder
onBind(Intent
intent)
{
return
new
MyBinder();
}
public
class
MyBinder
extends
Binder
{
public
FloatVideoWindowService
getService()
{
return
FloatVideoWindowService.this;
}
}
@Override
public
void
onCreate()
{
super.onCreate();
initWindow();//设置悬浮窗基本参数(位置、宽高等)
initFloating();//悬浮框点击事件的处理
}
@Override
public
int
onStartCommand(Intent
intent,
int
flags,
int
startId)
{
return
super.onStartCommand(intent,
flags,
startId);
}
@Override
public
void
onDestroy()
{
super.onDestroy();
}
/**
*
设置悬浮框基本参数(位置、宽高等)
*/
private
void
initWindow()
{
mWindowManager
=
(WindowManager)
getApplicationContext().getSystemService(Context.WINDOW_SERVICE);
wmParams
=
getParams();//设置好悬浮窗的参数
//
悬浮窗默认显示以左上角为起始坐标
wmParams.gravity
=
Gravity.LEFT
|
Gravity.TOP;
//悬浮窗的开始位置,因为设置的是从左上角开始,所以屏幕左上角是x=0;y=0
wmParams.x
=
70;
wmParams.y
=
210;
//得到容器,通过这个inflater来获得悬浮窗控件
inflater
=
LayoutInflater.from(getApplicationContext());
//
获取浮动窗口视图所在布局
mFloatingLayout
=
inflater.inflate(R.layout.alert_float_video_layout,
null);
//
添加悬浮窗的视图
mWindowManager.addView(mFloatingLayout,
wmParams);
}
private
WindowManager.LayoutParams
getParams()
{
wmParams
=
new
WindowManager.LayoutParams();
//设置window
type
下面变量2002是在屏幕区域显示,2003则可以显示在状态栏之上
wmParams.type
=
WindowManager.LayoutParams.TYPE_TOAST;
//设置可以显示在状态栏上
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()
{
smallSizePreviewLayout
=
mFloatingLayout.findViewById(R.id.small_size_preview);
//悬浮框点击事件
smallSizePreviewLayout.setOnClickListener(new
View.OnClickListener()
{
@Override
public
void
onClick(View
v)
{
//在这里实现点击重新回到Activity
}
});
//悬浮框触摸事件,设置悬浮框可拖动
smallSizePreviewLayout.setOnTouchListener(new
FloatingListener());
}
//开始触控的坐标,移动时的坐标(相对于屏幕左上角的坐标)
private
int
mTouchStartX,
mTouchStartY,
mTouchCurrentX,
mTouchCurrentY;
//开始时的坐标和结束时的坐标(相对于自身控件的坐标)
private
int
mStartX,
mStartY,
mStopX,
mStopY;
//判断悬浮窗口是否移动,这里做个标记,防止移动后松手触发了点击事件
private
boolean
isMove;
private
class
FloatingListener
implements
View.OnTouchListener
{
@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
+=
mTouchCurrentX
-
mTouchStartX;
wmParams.y
+=
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;
}
//如果是移动事件不触发OnClick事件,防止移动的时候一放手形成点击事件
return
isMove;
}
}
}d.在悬浮框成功被初始化以及相关参数被设置后,接下来就需要将对方的视频画布添加到悬浮框里面去了,这样我们才能看到对方的视频画面嘛,同样我们是在Service的oncreate这个生命周期完成这个操作的,这里视频画布的添加方式使用的网易云信的SDK,具体的添加方式视不同的SDK而定,代码如下所示:/**
*
初始化预览窗口
*/
private
void
initSurface()
{
if
(smallRender
==
null)
{
smallRender
=
new
AVChatSurfaceViewRenderer(getApplicationContext());
}
addIntoSmallSizePreviewLayout(smallRender);
}
/**
*
添加surfaceview到smallSizePreviewLayout
*/
private
void
addIntoSmallSizePreviewLayout(SurfaceView
surfaceView)
{
if
(surfaceView.getParent()
!=
null)
{
((ViewGroup)
surfaceView.getParent()).removeView(surfaceView);
}
smallSizePreviewLayout.addView(surfaceView);
surfaceView.setZOrderMediaOverlay(true);
}e.我们上面说到要将服务service的绑定与解绑与悬浮框的开启和关闭相结合,所以既然我们在服务的oncreate()方法中开启了悬浮框,那么就应该在其ondestroy()方法中对悬浮框进行关闭,关闭悬浮框的本质是将相关view给移除掉,接着清除我们的视频画布,在服务的ondestroy()方法中执行如下代码:@Override
public
void
onDestroy()
{
super.onDestroy();
if
(mFloatingLayout
!=
null)
{
//
移除悬浮窗口
mWindowManager.removeView(mFloatingLayout);
}
//清除视频画布
AVChatManager.getInstance().setupRemoteVideoRender(account,
null,
false,
0);
}f.服务的绑定方式有bindService和startService两种,使用不同的绑定方式其生命周期也会不一样,已知我们需要让悬浮框在视频通话activityfinish掉的时候也顺便关掉,那么理所当然我们就应该采用bind方式来启动服务,让他的生命周期跟随他的开启者,也即是跟随开启它的activity生命周期。intent
=
new
Intent(this,
FloatVideoWindowService.class);//开启服务显示悬浮框
bindService(intent,
mVideoServiceConnection,
Context.BIND_AUTO_CREATE);
ServiceConnection
mVideoServiceConnection
=
new
ServiceConnection()
{
@Override
public
void
onServiceConnected(ComponentName
name,
IBinder
service)
{
//
获取服务的操作对象
FloatVideoWindowService.MyBinder
binder
=
(FloatVideoWindowService.MyBinder)
service;
binder.getService();
}
@Override
public
void
onServiceDisconnected(ComponentName
name)
{
}
};三、完整的流程现在我们将上面所说的给串联起来,思路会更加清晰一点,假设现在我正在进行视频通话,点击视频最小化按钮,我们应该按顺序执
温馨提示
- 1. 本站所有资源如无特殊说明,都需要本地电脑安装OFFICE2007和PDF阅读器。图纸软件为CAD,CAXA,PROE,UG,SolidWorks等.压缩文件请下载最新的WinRAR软件解压。
- 2. 本站的文档不包含任何第三方提供的附件图纸等,如果需要附件,请联系上传者。文件的所有权益归上传用户所有。
- 3. 本站RAR压缩包中若带图纸,网页内容里面会有图纸预览,若没有图纸预览就没有图纸。
- 4. 未经权益所有人同意不得将文件中的内容挪作商业或盈利用途。
- 5. 人人文库网仅提供信息存储空间,仅对用户上传内容的表现方式做保护处理,对用户上传分享的文档内容本身不做任何修改或编辑,并不能对任何下载内容负责。
- 6. 下载文件中如有侵权或不适当内容,请与我们联系,我们立即纠正。
- 7. 本站不保证下载资源的准确性、安全性和完整性, 同时也不承担用户因使用这些下载资源对自己和他人造成任何形式的伤害或损失。
最新文档
- 课题申报参考:金代民族交往交流交融的考古学观察
- 课题申报参考:减税降费政策实施效果评估和策略优化研究
- 二零二五版环保项目临时工劳动合同4篇
- 基于2025年度计划的环保项目合作协议3篇
- 2025年智能水电表更换与数据采集服务合同4篇
- 2025年度个人退房协议书范本(适用于商业地产)4篇
- 二零二五版建筑工程公司资质借用与施工监督服务协议3篇
- 二零二五年度商业综合体场地租赁合同范本6篇
- 专利授权事务全权委托合同书版B版
- 2025年度排水沟施工安全协议书范本
- GB/T 45107-2024表土剥离及其再利用技术要求
- 2024-2025学年八年级上学期1月期末物理试题(含答案)
- 商场电气设备维护劳务合同
- 2023年国家公务员录用考试《行测》真题(行政执法)及答案解析
- 2024智慧医疗数据字典标准值域代码
- 年产12万吨装配式智能钢结构项目可行性研究报告模板-立项备案
- 【独家揭秘】2024年企业微信年费全解析:9大行业收费标准一览
- 医疗器械经销商会议
- 《±1100kV特高压直流换流变压器使用技术条件》
- 1-1 拥抱梦想:就这样埋下一颗种子【2022中考作文最热8主题押题24道 构思点拨+范文点评】
- 《风电场项目经济评价规范》(NB-T 31085-2016)
评论
0/150
提交评论