仿微信懸浮窗,可縮放懸浮窗,支持自定義展開佈局

轉載請註明來源 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();
發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章