仿微信悬浮窗,可缩放悬浮窗,支持自定义展开布局

转载请注明来源 https://blog.csdn.net/u011453163/article/details/84655641

话不多说,先上效果图
在这里插入图片描述
代码是用kotlin写的,kotlin也是刚学的,写的可能不怎么好。

起因

说说为什么开发这个功能,首先自己公司的项目里用到了悬浮球的功能,以前见到的悬浮球一般是作为快捷入口,有点像狗皮膏药,有时候很烦。一直觉得微信的用户体验很好,微信也用了悬浮球,但是相对克制,给用户选择的空间很大,效果也挺炫的,忍不住也想尝试的开发一个,然后就有了这份代码了。

思路
在这里插入图片描述
1、 通过WindowManager 添加两个view,一个是控制器悬浮球,一个是展开页面的载体

fun addFloatingWindow() {
        if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.M) {
            if (!Settings.canDrawOverlays(context)) {
                Log.d("FloatingWindow", "没有悬浮窗权限")
                return
            }
        }
        if (!isAddView) {
            isAddView = true
            windowManager?.apply {
                addView(floatingBaseView, baseLayoutParams)
                addView(controller, controlLayoutParams)
            }
        }
    }

kotlin 没有高亮。。。。
这里只是做了简单的权限判断,实际应用到项目中,权限自行管理即可

2、通过切换WindowManager.LayoutParams 的 flag 来改变两个view的焦点问题

    baseLayoutParams?.flags = WindowManager.LayoutParams.FLAG_NOT_TOUCH_MODAL
    controlLayoutParams?.flags = WindowManager.LayoutParams.FLAG_NOT_TOUCHABLE or WindowManager.LayoutParams.FLAG_NOT_FOCUSABLE

关于flag网上已经有很多文章写的很明白了 这里就不赘述了,这里之所以要切换焦点,使用底层的view是全屏的且一直存在的,如果不切换焦点的话,触摸事件无法传递。

3、缩放效果
3.1、 最开始的想法是通过改变WindowManager.LayoutParams的宽高来处理的,但是在window上使用动画,总是出现掉帧,达不到效果
3.2、第二种也是为什么我要使用两层的原因,我使用view来做这个动画效果,使用view做缩放动画有两种方式 :一 、canvas.clip(…) 无法消除锯齿(放弃) 二、使用paint的Xfermod来处理 能消除锯齿(采用)

  private fun init() {
        pathRect = Path()
        pathCircle = Path()
        paint = Paint().apply {
            xfermode=PorterDuffXfermode(PorterDuff.Mode.DST_OUT)
            isAntiAlias = true
        }
    }


    override fun draw(canvas: Canvas) {
        canvas.saveLayer(0f, 0f, this.width.toFloat(), this.height.toFloat(), null, Canvas.ALL_SAVE_FLAG)
        super.draw(canvas)
        /**
         * 取路径区域重叠的部分
         */
        canvas.drawFilter = PaintFlagsDrawFilter(0, Paint.ANTI_ALIAS_FLAG or Paint.FILTER_BITMAP_FLAG)
        pathRect?.reset()
        pathRect?.addRect(0f, 0f, width.toFloat(), height.toFloat(), Path.Direction.CW)
        pathCircle?.reset()
        pathCircle?.addCircle(circleX.toFloat(), circleY.toFloat(), circleRadius.toFloat(), Path.Direction.CW)
        pathRect?.op(pathCircle, Path.Op.XOR)
        canvas.drawPath(pathRect, paint)

    }

4、移除区域
因为是两层的关系 ,所以做移除区域就很简单,只要底层画出相应的块即可。

  override fun onDraw(canvas: Canvas?) {
        super.onDraw(canvas)
        canvas?.apply {
            drawFilter = pfilter
            if (showDelArea) {
                paint.color = delAreaBgColor
                if (isInside) {
                    drawCircle(width.toFloat(), height.toFloat(), delRadius.toFloat(), paint)
                } else {
                    drawCircle(width.toFloat(), height.toFloat(), srcRadius.toFloat(), paint)
                }
                paint.color = delTextColor
                paint.getTextBounds(delText, 0, delText.length, textRect)
                drawText(delText, width.toFloat() - (srcRadius-textRect.width())/2-textRect.width(), height.toFloat() -(srcRadius-textRect.height())/2, paint)
            }
        }
    }

Demo
github源码
FloatingView

如何引入
在项目根 build.gradle 添加

allprojects {
    repositories {
			...
			maven { url 'https://jitpack.io' }
		}
}

在使用的模块下添加

dependencies {
	 implementation 'com.github.zhenbinwei:KotlinFloatingView:1.0.0'
}

需要使用的权限

<!-- 显示系统窗口权限 -->
<uses-permission android:name="android.permission.SYSTEM_ALERT_WINDOW"/>
<!-- 在 屏幕最顶部显示addview-->
<uses-permission android:name="android.permission.SYSTEM_OVERLAY_WINDOW" />

如何使用

//创建实例对象
FloatingWindow  floatingWindow=new FloatingWindow(this);
//设置展开的布局
floatingWindow.addRealContentView(View.inflate(this,R.layout.test,null));
//设置悬浮窗图标
floatingWindow.setMiniWindowIcon(R.mipmap.ic_launcher_round);
//显示
floatingWindow.addFloatingWindow();
//关闭
floatingWindow.removeFloatingWindow();
  • 2
    点赞
  • 7
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值