欧美bbbwbbbw肥妇,免费乱码人妻系列日韩,一级黄片

Kotlin實(shí)現(xiàn)Android系統(tǒng)懸浮窗詳解

 更新時(shí)間:2021年12月15日 09:56:05   作者:Swuagg  
大家好,本篇文章主要講的是Kotlin實(shí)現(xiàn)Android系統(tǒng)懸浮窗詳解,感興趣的同學(xué)趕快來(lái)看一看吧,對(duì)你有幫助的話(huà)記得收藏一下,方便下次瀏覽

Android 彈窗淺談

我們知道 Android 彈窗中,有一類(lèi)彈窗會(huì)在應(yīng)用之外也顯示,這是因?yàn)樗簧昝鞒闪?strong>系統(tǒng)彈窗,除此之外還有2類(lèi)彈窗分別是:子彈窗應(yīng)用彈窗

應(yīng)用彈窗:就是我們常規(guī)使用的 Dialog 之類(lèi)彈窗,依賴(lài)于應(yīng)用的 Activity;子彈窗:依賴(lài)于父窗口,比如 PopupWindow;系統(tǒng)彈窗:比如狀態(tài)欄、Toast等,本文所講的系統(tǒng)懸浮窗就是系統(tǒng)彈窗。

系統(tǒng)懸浮窗具體實(shí)現(xiàn)

權(quán)限申請(qǐng)

<uses-permission android:name="android.permission.SYSTEM_ALERT_WINDOW" />
<uses-permission android:name="android.permission.SYSTEM_OVERLAY_WINDOW" />

代碼設(shè)計(jì)

下面的包結(jié)構(gòu)截圖,簡(jiǎn)單展示了實(shí)現(xiàn)系統(tǒng)懸浮窗的代碼結(jié)構(gòu),更復(fù)雜的業(yè)務(wù)需要可在此基礎(chǔ)上進(jìn)行擴(kuò)展。

FloatWindowService:系統(tǒng)懸浮窗在此 Service 中彈出;

FloatWindowManager:系統(tǒng)懸浮窗管理類(lèi);

FloatLayout:系統(tǒng)懸浮窗布局;

HomeKeyObserverReceiver:

監(jiān)聽(tīng) Home 鍵;

FloatWindowUtils:系統(tǒng)懸浮窗工具類(lèi)。

具體實(shí)現(xiàn)

FloatWindowService 類(lèi)

class FloatWindowService : Service() {
 
    private val TAG = FloatWindowService::class.java.simpleName
    private var mFloatWindowManager: FloatWindowManager? = null
    private var mHomeKeyObserverReceiver: HomeKeyObserverReceiver? = null
 
    override fun onCreate() {
        TLogUtils.i(TAG, "onCreate: ")
        mFloatWindowManager = FloatWindowManager(applicationContext)
        mHomeKeyObserverReceiver = HomeKeyObserverReceiver()
 
        registerReceiver(mHomeKeyObserverReceiver, IntentFilter(Intent.ACTION_CLOSE_SYSTEM_DIALOGS))
        mFloatWindowManager!!.createWindow()
    }
 
    override fun onBind(intent: Intent?): IBinder? {
        return null
    }
 
    override fun onStartCommand(intent: Intent?, flags: Int, startId: Int): Int {
        return START_NOT_STICKY
    }
 
    override fun onDestroy() {
        TLogUtils.i(TAG, "onDestroy: ")
        mFloatWindowManager?.removeWindow()
        if (mHomeKeyObserverReceiver != null) {
            unregisterReceiver(mHomeKeyObserverReceiver)
        }
    }
 
}

FloatWindowManager 類(lèi)

包括系統(tǒng)懸浮窗的創(chuàng)建、顯示、銷(xiāo)毀(以及更新)。

 
public void addView(View view, ViewGroup.LayoutParams params); // 添加 View 到 Window
public void updateViewLayout(View view, ViewGroup.LayoutParams params); //更新 View 在 Window 中的位置
public void removeView(View view); //刪除 View

FloatWindowManager 類(lèi)代碼

class FloatWindowManager constructor(context: Context) {
 
    var isShowing = false
    private val TAG = FloatWindowManager::class.java.simpleName
    private var mContext: Context = context
    private var mFloatLayout = FloatLayout(mContext)
    private var mLayoutParams: WindowManager.LayoutParams? = null
    private var mWindowManager: WindowManager = context.getSystemService(Context.WINDOW_SERVICE) as WindowManager
 
    fun createWindow() {
        TLogUtils.i(TAG, "createWindow: start...")
        // 對(duì)象配置操作使用apply,額外的處理使用also
        mLayoutParams = WindowManager.LayoutParams().apply {
            type = if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.O) {
                // Android 8.0以后需要使用TYPE_APPLICATION_OVERLAY,不允許使用以下窗口類(lèi)型來(lái)在其他應(yīng)用和窗口上方顯示提醒窗口:TYPE_PHONE、TYPE_PRIORITY_PHONE、TYPE_SYSTEM_ALERT、TYPE_SYSTEM_OVERLAY、TYPE_SYSTEM_ERROR。
                WindowManager.LayoutParams.TYPE_APPLICATION_OVERLAY
            } else {
                // 在A(yíng)ndroid 8.0之前,懸浮窗口設(shè)置可以為T(mén)YPE_PHONE,這種類(lèi)型是用于提供用戶(hù)交互操作的非應(yīng)用窗口。
                // 在A(yíng)PI Level  = 23的時(shí)候,需要在A(yíng)ndroid Manifest.xml文件中聲明權(quán)限SYSTEM_ALERT_WINDOW才能在其他應(yīng)用上繪制控件
                WindowManager.LayoutParams.TYPE_PHONE
            }
            // 設(shè)置圖片格式,效果為背景透明
            format = PixelFormat.RGBA_8888
            // 設(shè)置浮動(dòng)窗口不可聚焦(實(shí)現(xiàn)操作除浮動(dòng)窗口外的其他可見(jiàn)窗口的操作)
            flags = WindowManager.LayoutParams.FLAG_NOT_FOCUSABLE
            // 調(diào)整懸浮窗顯示的??课恢脼橛覀?cè)置頂
            gravity = Gravity.TOP or Gravity.END
            width = 800
            height = 200
            x = 20
            y = 40
        }
 
        mWindowManager.addView(mFloatLayout, mLayoutParams)
        TLogUtils.i(TAG, "createWindow: end...")
        isShowing = true
    }
 
    fun showWindow() {
        TLogUtils.i(TAG, "showWindow: isShowing = $isShowing")
        if (!isShowing) {
            if (mLayoutParams == null) {
                createWindow()
            } else {
                mWindowManager.addView(mFloatLayout, mLayoutParams)
                isShowing = true
            }
        }
    }
 
    fun removeWindow() {
        TLogUtils.i(TAG, "removeWindow: isShowing = $isShowing")
        mWindowManager.removeView(mFloatLayout)
        isShowing = false
    }
 
}

FloatLayout 類(lèi)及其 Layout

系統(tǒng)懸浮窗自定義View:FloatLayout

class FloatLayout @JvmOverloads constructor(context: Context, attrs: AttributeSet? = null, defStyleAttr: Int = 0, defStyleRes: Int = 0) :
    ConstraintLayout(context, attrs, defStyleAttr, defStyleRes) {
 
    private var mTime: TCLTextView
    private var mDistance: TCLTextView
    private var mSpeed: TCLTextView
    private var mCalories: TCLTextView
 
    init {
        val view = LayoutInflater.from(context).inflate(R.layout.do_exercise_view_float_layout, this, true)
        mTime = view.findViewById(R.id.float_layout_tv_time)
        mDistance = view.findViewById(R.id.float_layout_tv_distance)
        mSpeed = view.findViewById(R.id.float_layout_tv_speed)
        mCalories = view.findViewById(R.id.float_layout_tv_calories)
    }
 
}

布局文件:float_layout_tv_time

HomeKeyObserverReceiver 類(lèi)

class HomeKeyObserverReceiver : BroadcastReceiver() {
 
    override fun onReceive(context: Context?, intent: Intent?) {
        try {
            val action = intent!!.action
            val reason = intent.getStringExtra("reason")
            TLogUtils.d(TAG, "HomeKeyObserverReceiver: action = $action,reason = $reason")
            if (Intent.ACTION_CLOSE_SYSTEM_DIALOGS == action && "homekey" == reason) {
                val keyCode = intent.getIntExtra("keycode", KeyEvent.KEYCODE_UNKNOWN)
                TLogUtils.d(TAG, "keyCode = $keyCode")
                context?.stopService(Intent(context, FloatWindowService::class.java))
            }
        } catch (ex: Exception) {
            ex.printStackTrace()
        }
    }
 
    companion object {
        private val TAG = HomeKeyObserverReceiver::class.java.simpleName
    }
 
}

FloatWindowUtils 類(lèi)

object FloatWindowUtils {
 
    const val REQUEST_FLOAT_CODE = 1000
    private val TAG = FloatWindowUtils::class.java.simpleName
 
    /**
     * 判斷Service是否開(kāi)啟
     */
    fun isServiceRunning(context: Context, ServiceName: String): Boolean {
        if (TextUtils.isEmpty(ServiceName)) {
            return false
        }
        val myManager = context.getSystemService(Context.ACTIVITY_SERVICE) as ActivityManager
        val runningService = myManager.getRunningServices(1000) as ArrayList<ActivityManager.RunningServiceInfo>
        runningService.forEach {
            if (it.service.className == ServiceName) {
                return true
            }
        }
        return false
    }
 
    /**
     * 檢查懸浮窗權(quán)限是否開(kāi)啟
     */
    @SuppressLint("NewApi")
    fun checkSuspendedWindowPermission(context: Activity, block: () -> Unit) {
        if (commonROMPermissionCheck(context)) {
            block()
        } else {
            Toast.makeText(context, "請(qǐng)開(kāi)啟懸浮窗權(quán)限", Toast.LENGTH_SHORT).show()
            context.startActivityForResult(Intent(Settings.ACTION_MANAGE_OVERLAY_PERMISSION).apply {
                data = Uri.parse("package:${context.packageName}")
            }, REQUEST_FLOAT_CODE)
        }
    }
 
    /**
     * 判斷懸浮窗權(quán)限權(quán)限
     */
    fun commonROMPermissionCheck(context: Context?): Boolean {
        var result = true
        if (Build.VERSION.SDK_INT >= 23) {
            try {
                val clazz: Class<*> = Settings::class.java
                val canDrawOverlays = clazz.getDeclaredMethod("canDrawOverlays", Context::class.java)
                result = canDrawOverlays.invoke(null, context) as Boolean
            } catch (e: Exception) {
                TLogUtils.e(TAG, e)
            }
        }
        return result
    }
 
}

總結(jié)

本文并未詳細(xì)討論系統(tǒng)懸浮窗的拖動(dòng)功能,實(shí)現(xiàn)系統(tǒng)懸浮穿基本功能可以總結(jié)為以下幾個(gè)步驟:

1. 聲明及申請(qǐng)權(quán)限;
2. 構(gòu)建懸浮窗需要的控件 Service、Receiver、Manager、Layout、Util;
3. 使用 WindowManager 創(chuàng)建、顯示、銷(xiāo)毀(以及更新)Layout。

到此這篇關(guān)于Kotlin實(shí)現(xiàn)Android系統(tǒng)懸浮窗詳解的文章就介紹到這了,更多相關(guān)Android Kotlin系統(tǒng)懸浮窗內(nèi)容請(qǐng)搜索腳本之家以前的文章或繼續(xù)瀏覽下面的相關(guān)文章希望大家以后多多支持腳本之家!

相關(guān)文章

最新評(píng)論