最近在转型kotlin,然后又对天猫的茅台活动有点兴趣,于是,脑子一热,抽了十几分钟,写了个简易的秒表助手。如下图:
![](https://img.haomeiwen.com/i1447307/a1c2ce306af71cd3.png)
分析如下:
首先,要做到悬浮窗口,那么久必须要依赖Window属性,在window中,添加自定义的view。然后脑子浮现出了这一幅图:
![](https://img.haomeiwen.com/i1447307/df12fc32cf90c942.png)
emu,感觉完成一大半了,最关键的就是 windowManager.addView(floatingView, layoutParams)这个操作了吧。
然后分析点,第二点:如何让该应用处于后台也能运行呢?没错,那就是Service,开一个Service不就好了吗,于是乎,就先创建了一个Servie,并在AndroidManifest中完成注册。
<service
android:name=".FloatingWindowService"
android:enabled="true" />
最后一个问题: 如何实时把系统时间抛给主线程? 这里我利用了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<TextView>(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,完成。
网友评论