一个Android平台下的仿微信视频聊天悬浮窗

闲来无事想自己手写一个类似微信视频聊天悬浮窗的效果,由于笔者是第一次写技术类的文章,不足的地方,还请各位多多谅解哈。另外由于篇幅有限,在此只展示关键代码。首先我们定义二个按钮,一个是创建悬浮窗,一个是让悬浮窗消失,如下所示:

this.button_open=findViewById(R.id.button_open)
this.button_close=findViewById(R.id.button_close)
this.button_open.setOnClickListener(this)
this.button_close.setOnClickListener(this)
overridefunonClick(v:View){
when(v.getId()){
R.id.button_open->
showFloatingView(this)
R.id.button_close->
if(this::mFloatingViewManager.isInitialized){
this.mFloatingViewManager.removeAllFloatingView()

我们来屡一下创建悬浮窗的逻辑,在创建悬浮窗之前,我们需要申请到系统的权限,如果没有获取到权限系统是会报错的,首先我们在AndroidManifest.xml文件中加上一条

<uses-permissionandroid:name="android.permission.SYSTEM_ALERT_WINDOW"/>,这还不够,我们还需要在代码中检测是否成功获取到了悬浮窗的权限,如果没有被授权还要动态申请,其代码如下所示:

if(!Settings.canDrawOverlays(this@MainActivity)){
showDialog()
}
else{
show()
}

其中showDialog方法的关键代码如下所示:

val intent=Intent(Settings.ACTION_MANAGE_OVERLAY_PERMISSION,
Uri.parse("package:"+this@MainActivity.getPackageName()))
startActivityForResult(intent,OVERLAY_PERMISSION_REQUEST_CODE)

其主要作用为获取权限,获取权限后我们就可以展示悬浮窗啦,以下为展示悬浮窗的代码

//获取悬浮窗布局
valfloatView=LayoutInflater.from(this).inflate(R.layout.call_float_view,null,false)
valdm=DisplayMetrics()
valwindowManager=getSystemService(Context.WINDOW_SERVICE)asWindowManager
windowManager.getDefaultDisplay().getMetrics(dm)//获取本机像素数据

this.mFloatingViewManager=FloatViewManager(this)

valconfigs=FloatViewManager.Configs()
configs.floatingViewX=dm.widthPixels/2//悬浮窗所在的初始位置的x坐标
configs.floatingViewY=dm.heightPixels/3//悬浮窗所在的初始位置的y坐标
configs.overMargin=(-(8*dm.density)).toInt()//设置悬浮窗的margin

this.mFloatingViewManager.addFloatingView(floatView,configs)

其中比较关键的一句代码就是this.mFloatingViewManager.addFloatingView(floatView,configs)了,我们来看一下这个方法都干了什么,

fun addFloatingView(view:View,configs:Configs){
valfloatingView=FloatView(this.mContext,configs.floatingViewX,configs.floatingViewY)//初始化一个FloatView
floatingView.setOnTouchListener(this)
floatingView.setOverMargin(configs.overMargin)//设置Margin
floatingView.setAnimateInitialMove(configs.animateInitialMove)//是否展示动画
valtargetParams=FrameLayout.LayoutParams(configs.floatingViewWidth,configs.floatingViewHeight)
view.setLayoutParams(targetParams)//设置视图的宽高
floatingView.addView(view)

this.mFloatingViewList.add(floatingView)

this.mWindowManager.addView(floatingView,floatingView.getWindowLayoutParams())//显示悬浮窗
},

可见,这个addView方法大致只是设置了一下视图的参数,然后把它加到了WindowManager里面,那么又有小伙伴问了,你写的这个FloatView是个什么东西呀,其实他是继承了FrameLayout的一个ViewGroup。我们只需要关注移动视图事件的方法即可,该方法的代码如下

elseif(action==MotionEvent.ACTION_MOVE){
this.mIsMoveAccept=true
this.updateViewPosition(getXByTouch(),getYByTouch())
}

其大致意思就是更新视图的位置,这样一来我们的悬浮窗就可以随着手指移动啦.

我们再设置当手指抬起的时间,让悬浮窗滑动到屏幕边缘,ACTION_UP事件的代码如下

if(this.mIsMoveAccept){
moveToEdge(true)
}
funmoveToEdge(withAnimation:Boolean){
valcurrentX=getXByTouch()
valcurrentY=getYByTouch()
moveToEdge(currentX,currentY,withAnimation)
}

Fun moveToEdge(startX:Int,startY:Int,withAnimation:Boolean){
vargoalPositionX=startX
valgoalPositionY=startY
valisMoveRightEdge=startX>(this.mMetrics.widthPixels-getWidth())/2
if(isMoveRightEdge){
goalPositionX=this.mPositionLimitRect.right
}else{
goalPositionX=this.mPositionLimitRect.left
}
moveTo(startX,startY,goalPositionX,goalPositionY,withAnimation)
}
funmoveTo(currentX:Int,currentY:Int,goalPositionX:Int,goalPositionY:Int,withAnimation:Boolean){
valgoalPositionX=Math.min(
Math.max(this.mPositionLimitRect.left,goalPositionX),
this.mPositionLimitRect.right
)
valgoalPositionY=Math.min(
Math.max(this.mPositionLimitRect.top,goalPositionY),
this.mPositionLimitRect.bottom
)
if(withAnimation){
this.mParams.y=goalPositionY
this.mMoveEdgeAnimator=ValueAnimator.ofInt(currentX,goalPositionX)
this.mMoveEdgeAnimator.addUpdateListener{listener->
mParams.x=mMoveEdgeAnimator.getAnimatedValue()asInt
mWindowManager.updateViewLayout(this@FloatView,mParams)
}
this.mMoveEdgeAnimator.setDuration(MOVE_TO_EDGE_DURATION)
this.mMoveEdgeAnimator.setInterpolator(this.mMoveEdgeInterpolator)
this.mMoveEdgeAnimator.start()
}
else{
if(this.mParams.x!=goalPositionX||this.mParams.y!=goalPositionY){
this.mParams.x=goalPositionX
this.mParams.y=goalPositionY
this.mWindowManager.updateViewLayout(this@FloatView,this.mParams)
}
}
this.mViewTouchX=0f
this.mViewTouchY=0f
this.mScreenTouchDownX=0f
this.mScreenTouchDownY=0f
this.mIsMoveAccept=false
}

从代码可以看出,我们先要判断悬浮窗是滑动到左边缘还是滑动到右边缘,然后判断是否需要执行动画,如果需要执行动画则执行,否则直接将悬浮窗拉到边缘,这样我们的悬浮窗就完成啦,是不是很简单呢,我们再来看一下销毁悬浮窗的逻辑:

funremoveAllFloatingView(){
if(this::mFloatingViewList!=null){
for(floatingViewinmFloatingViewList){
this.mWindowManager.removeViewImmediate(floatingView)
}
this.mFloatingViewList.clear()
}
}

只需要几句代码就可以完成销毁,这样我们的工作就完成啦

其实现的效果如下所示:

最后附上源码,想详细学习的朋友可以下载,看完的小伙伴还请给我赞呦

源码地址

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值