实用小工具--Android悬浮秒表

最近在转型kotlin,然后又对天猫的茅台活动有点兴趣,于是,脑子一热,抽了十几分钟,写了个简易的秒表助手。如下图:


image.png

分析如下:
首先,要做到悬浮窗口,那么久必须要依赖Window属性,在window中,添加自定义的view。然后脑子浮现出了这一幅图:


image.png

emu,感觉完成一大半了,最关键的就是 windowManager.addView(floatingView, layoutParams)这个操作了吧。

然后分析点,第二点:如何让该应用处于后台也能运行呢?没错,那就是Service,开一个Service不就好了吗,于是乎,就先创建了一个Servie,并在AndroidManifest中完成注册。

   

最后一个问题: 如何实时把系统时间抛给主线程? 这里我利用了kotlin的协程属性

  GlobalScope.launch(Dispatchers.IO) {
                println("deal data===>");
                while (state) {
                    val format = SimpleDateFormat("HH:mm:ss", Locale.getDefault())
                    val myDate: String = format.format(Date())
                    withContext(Dispatchers.Main) {
                        println("deal UI===>");
                        tvContent.setText(myDate.toString());
                    }
                }

            }

如上图所示,然IO线程不断处理日期数据,处理完后,转换为主线程,然后把控件数据更新,即完成功能。

ok,动手操作一下吧。

import android.annotation.SuppressLint
import android.app.ActionBar
import android.app.Service
import android.content.Intent
import android.graphics.PixelFormat
import android.os.Build
import android.os.IBinder
import android.provider.Settings
import android.view.Gravity
import android.view.LayoutInflater
import android.view.View
import android.view.WindowManager
import android.widget.TextView
import androidx.annotation.RequiresApi
import kotlinx.coroutines.*
import java.text.SimpleDateFormat
import java.util.*

class FloatingWindowService : Service() {
    private lateinit var windowManager: WindowManager
    private lateinit var layoutParams: WindowManager.LayoutParams
    private lateinit var tvContent: TextView
    private var floatingView: View? = null
    private var state = true;

    // 用来判断floatingView是否attached 到 window manager,防止二次removeView导致崩溃
    private var attached = false
    override fun onCreate() {
        super.onCreate()

        // 获取windowManager并设置layoutParams
        windowManager = getSystemService(WINDOW_SERVICE) as WindowManager
        layoutParams = WindowManager.LayoutParams().apply {
            type = if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.O) {
                WindowManager.LayoutParams.TYPE_APPLICATION_OVERLAY
            } else {
                WindowManager.LayoutParams.TYPE_PHONE
            }
            format = PixelFormat.RGBA_8888
//            format = PixelFormat.TRANSPARENT
            gravity = Gravity.START or Gravity.TOP
            flags =
                WindowManager.LayoutParams.FLAG_NOT_TOUCH_MODAL or WindowManager.LayoutParams.FLAG_NOT_FOCUSABLE
            width = ActionBar.LayoutParams.WRAP_CONTENT
            height = ActionBar.LayoutParams.WRAP_CONTENT
            x = 300
            y = 300
        }

    }

    override fun onBind(intent: Intent?): IBinder? {
        return null
    }

    @RequiresApi(Build.VERSION_CODES.M)
    @SuppressLint("ClickableViewAccessibility")
    override fun onStartCommand(intent: Intent?, flags: Int, startId: Int): Int {
        if (Settings.canDrawOverlays(this)) {
            floatingView = LayoutInflater.from(this).inflate(R.layout.activity_main, null)
            tvContent = floatingView!!.findViewById(R.id.tv_time);
            // 设置TextView滚动
            windowManager.addView(floatingView, layoutParams)
            attached = true

            GlobalScope.launch(Dispatchers.IO) {
                println("deal data===>");
                while (state) {
                    val format = SimpleDateFormat("HH:mm:ss", Locale.getDefault())
                    val myDate: String = format.format(Date())
                    withContext(Dispatchers.Main) {
                        println("deal UI===>");
                        tvContent.setText(myDate.toString());
                    }
                }

            }

        }
        return super.onStartCommand(intent, flags, startId)
    }

    override fun onDestroy() {

        if (attached) {
            windowManager.removeView(floatingView)
            state = false
        }
    }


}

最后,注意要在一个activity里面 ,startService 哈。

   if (Settings.canDrawOverlays(this)) {
                val service = Intent(this, FloatingWindowService::class.java);
                startService(service);
            } else {
                startActivity(Intent(Settings.ACTION_MANAGE_OVERLAY_PERMISSION));
            }

ok,完成。

你可能感兴趣的:(实用小工具--Android悬浮秒表)