Android 護眼模式功能實現

相較於 iOS,安卓在 Q 以前並無原生支持護眼模式,因此咱們得本身實現一套。java

其實網上代碼也挺多的,甚至有些公司的產品就是這個功能。app

先來說下實現的思路,不想先聽思路的能夠直接跳到代碼那塊,代碼也不難。ide

1. 實現思路

  • Window 上覆蓋一個 View,作到跨 App 及狀態欄和導航欄總體覆蓋
  • View 的背景設置成過濾藍光的透明色(濾光層)
  • 注意好 Android 系統兼容性問題

2. 代碼

建立一個 EyeCareService,該 Service 用來添加 Window 上的濾光層ui

class EyeCareService : Service() {
    private lateinit var windowManager: WindowManager
    private lateinit var coverLayout: FrameLayout
    override fun onBind(intent: Intent?): IBinder? {
        return null
    }

    override fun onCreate() {
        super.onCreate()
        windowManager = getSystemService(Context.WINDOW_SERVICE) as WindowManager
        val params = WindowManager.LayoutParams().apply {
            if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.O) {
                this.type = WindowManager.LayoutParams.TYPE_APPLICATION_OVERLAY or
                        WindowManager.LayoutParams.TYPE_STATUS_BAR
            } else {
                this.type = WindowManager.LayoutParams.TYPE_SYSTEM_OVERLAY
            }
            this.flags = WindowManager.LayoutParams.FLAG_NOT_TOUCH_MODAL or
                    WindowManager.LayoutParams.FLAG_NOT_TOUCHABLE or
                    WindowManager.LayoutParams.FLAG_LAYOUT_NO_LIMITS or
                    WindowManager.LayoutParams.FLAG_LAYOUT_IN_SCREEN or
                    WindowManager.LayoutParams.FLAG_NOT_FOCUSABLE
            this.format = PixelFormat.TRANSLUCENT
        }
        windowManager.defaultDisplay.apply {
            params.gravity = Gravity.START or Gravity.TOP
            val point = Point()
            this.getRealSize(point)
            params.width = point.x
            params.height = point.y
        }

        coverLayout = FrameLayout(this)
        coverLayout.setBackgroundColor(Utils.getColor(30))
        windowManager.addView(coverLayout, params)
    }

    override fun onDestroy() {
        windowManager.removeViewImmediate(coverLayout)
        super.onDestroy()
    }


}
複製代碼

濾光層設置獲取顏色方法的方法,通常傳入 30 便可this

/** * 過濾藍光 * * @param blueFilterPercent 藍光過濾比例[10-80] */
public static @ColorInt
int getColor(int blueFilterPercent) {
	int realFilter = blueFilterPercent;
	if (realFilter < 10) {
		realFilter = 10;
	} else if (realFilter > 80) {
		realFilter = 80;
	}
	int a = (int) (realFilter / 80f * 180);
	int r = (int) (200 - (realFilter / 80f) * 190);
	int g = (int) (180 - (realFilter / 80f) * 170);
	int b = (int) (60 - realFilter / 80f * 60);
	return Color.argb(a, r, g, b);
}
複製代碼

activity 中調用啓動方法spa

private fun openEyeCareMode() {

    if (Build.VERSION.SDK_INT >= 23) {
        if (Settings.canDrawOverlays(this)) { //有懸浮窗權限開啓服務綁定 綁定權限
            val intent = Intent(this, EyeCareService::class.java)
            startService(intent)
        } else { //沒有懸浮窗權限,去開啓懸浮窗權限
            try {
                val intent = Intent(Settings.ACTION_MANAGE_OVERLAY_PERMISSION)
                startActivityForResult(intent, OVERLAY_PERMISSION_REQ_CODE)
            } catch (e: Exception) {
                e.printStackTrace()
            }
        }
    } else { //默認有懸浮窗權限 可是 華爲, 小米,oppo等手機會有本身的一套Android6.0如下 會有本身的一套懸浮窗權限管理 也須要作適配
        val intent = Intent(this, EyeCareService::class.java)
        startService(intent)
    }

}

override fun onActivityResult(requestCode: Int, resultCode: Int, data: Intent?) {
    super.onActivityResult(requestCode, resultCode, data)
    if (requestCode == OVERLAY_PERMISSION_REQ_CODE) {
        if (Build.VERSION.SDK_INT >= 23) {
            if (!Settings.canDrawOverlays(this)) {
                //權限授予失敗,沒法開啓懸浮窗
                return
            } else {
                //權限授予成功
            }//有懸浮窗權限開啓服務綁定 綁定權限
        }
        val intent = Intent(this, EyeCareService::class.java)
        startService(intent)
    }
}
複製代碼

3. 實際效果

4. 後話

剩下一些考慮點,在這我就不貼代碼了,稍微講下code

  • 須要實現關閉邏輯,startService 時 intent傳遞下關閉參數
  • 考慮前臺服務,防止退入後臺過段時Service被系統直接殺死,致使功能失效
相關文章
相關標籤/搜索