赞
踩
跳转到开启悬浮窗权限页面。如果悬浮窗权限已开启,直接将当前任务栈置于后台,开启服务即可。
其实回调方法,并没有直接告诉我们是否授权成功,所以我们需要在回调中再次判断
override fun onActivityResult(requestCode: Int, resultCode: Int, data: Intent) {
if (requestCode == 0) {
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.M) {
if (!Settings.canDrawOverlays(this)) {
Toast.makeText(this, “授权失败”, Toast.LENGTH_SHORT).show()
} else {
Handler().postDelayed({
val intent = Intent(this@Main2Activity, FloatWinfowServices::class.java)
intent.putExtra(“rangeTime”, rangeTime)
hasBind = bindService(intent, mVideoServiceConnection, Context.BIND_AUTO_CREATE)
moveTaskToBack(true)
}, 1000)
}
}
}
}
这里我们可以看到回调中延迟了1秒,因为测试发现某些机型反应“过快”,收到回调的时候还以为没有授权成功,其实已经成功了。
绑定Service我们需要一个ServiceConnection对象
internal var mVideoServiceConnection: ServiceConnection = object : ServiceConnection {
override fun onServiceConnected(name: ComponentName, service: IBinder) {
// 获取服务的操作对象
val binder = service as FloatWinfowServices.MyBinder
binder.service
}
override fun onServiceDisconnected(name: ComponentName) {}
}
Main2Activity的完整代码如下所示:
/**
private val chronometer: Chronometer? = null
private var hasBind = false
private val rangeTime: Long = 0
override fun onCreate(savedInstanceState: Bundle?) {
super.onCreate(savedInstanceState)
setContentView(R.layout.activity_main2)
}
fun zoom(v: View) {
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.M) {
if (!Settings.canDrawOverlays(this)) {
Toast.makeText(this, “当前无权限,请授权”, Toast.LENGTH_SHORT)
GlobalDialogSingle(this, “”, “当前未获取悬浮窗权限”, “去开启”, DialogInterface.OnClickListener { dialog, which ->
dialog.dismiss()
startActivityForResult(Intent(Settings.ACTION_MANAGE_OVERLAY_PERMISSION, Uri.parse(“package:” + packageName)), 0)
}).show()
} else {
moveTaskToBack(true)
val intent = Intent(this@Main2Activity, FloatWinfowServices::class.java)
hasBind = bindService(intent, mVideoServiceConnection, Context.BIND_AUTO_CREATE)
}
}
}
internal var mVideoServiceConnection: ServiceConnection = object : ServiceConnection {
override fun onServiceConnected(name: ComponentName, service: IBinder) {
// 获取服务的操作对象
val binder = service as FloatWinfowServices.MyBinder
binder.service
}
override fun onServiceDisconnected(name: ComponentName) {}
}
override fun onActivityResult(requestCode: Int, resultCode: Int, data: Intent) {
if (requestCode == 0) {
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.M) {
if (!Settings.canDrawOverlays(this)) {
Toast.makeText(this, “授权失败”, Toast.LENGTH_SHORT).show()
} else {
Handler().postDelayed({
val intent = Intent(this@Main2Activity, FloatWinfowServices::class.java)
intent.putExtra(“rangeTime”, rangeTime)
hasBind = bindService(intent, mVideoServiceConnection, Context.BIND_AUTO_CREATE)
moveTaskToBack(true)
}, 1000)
}
}
}
}
override fun onRestart() {
super.onRestart()
Log.d(“RemoteView”, “重新显示了”)
//不显示悬浮框
if (hasBind) {
unbindService(mVideoServiceConnection)
hasBind = false
}
}
override fun onNewIntent(intent: Intent) {
super.onNewIntent(intent)
}
override fun onDestroy() {
super.onDestroy()
}
}
新建悬浮窗Service FloatWinfowServices,因为我们使用的BindService,我们在onBind方法中初始化service中的布局
override fun onBind(intent: Intent): IBinder? {
initWindow()
//悬浮框点击事件的处理
initFloating()
return MyBinder()
}
service中我们通过WindowManager来添加一个布局显示。
/**
悬浮窗的参数主要设置悬浮窗的类型为
WindowManager.LayoutParams.TYPE_APPLICATION_OVERLAY
8.0 以下可设置为:
wmParams!!.type = WindowManager.LayoutParams.TYPE_PHONE
代码如下所示:
private //设置window type 下面变量2002是在屏幕区域显示,2003则可以显示在状态栏之上
//设置可以显示在状态栏上
//设置悬浮窗口长宽数据
val params: WindowManager.LayoutParams
get() {
wmParams = WindowManager.LayoutParams()
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.O) {
wmParams!!.type = WindowManager.LayoutParams.TYPE_APPLICATION_OVERLAY
} else {
wmParams!!.type = WindowManager.LayoutParams.TYPE_PHONE
}
wmParams!!.flags = WindowManager.LayoutParams.FLAG_NOT_FOCUSABLE or WindowManager.LayoutParams.FLAG_NOT_TOUCH_MODAL or
WindowManager.LayoutParams.FLAG_LAYOUT_IN_SCREEN or WindowManager.LayoutParams.FLAG_LAYOUT_INSET_DECOR or
WindowManager.LayoutParams.FLAG_WATCH_OUTSIDE_TOUCH
wmParams!!.width = WindowManager.LayoutParams.WRAP_CONTENT
wmParams!!.height = WindowManager.LayoutParams.WRAP_CONTENT
return wmParams
}
当点击悬浮窗的时候回到Activity2页面,并且悬浮窗消失,所以我们只需要给悬浮窗添加点击事件
linearLayout!!.setOnClickListener { startActivity(Intent(this@FloatWinfowServices, Main2Activity::class.java)) }
当Service走到onDestory的时候将view移除,对于Activity2页面来说 当onResume的时候 解绑Service,当onstop的时候 绑定Service。
从效果图中我们可以看到悬浮窗可以拖拽的,所以还要设置触摸事件,当移动距离超过某个值的时候让onTouch消费事件,这样就不会触发点击事件了。这个算是view比较基础的知识,相信大家都明白了。
//开始触控的坐标,移动时的坐标(相对于屏幕左上角的坐标)
private var mTouchStartX: Int = 0
private var mTouchStartY: Int = 0
private var mTouchCurrentX: Int = 0
private var mTouchCurrentY: Int = 0
//开始时的坐标和结束时的坐标(相对于自身控件的坐标)
private var mStartX: Int = 0
private var mStartY: Int = 0
private var mStopX: Int = 0
private var mStopY: Int = 0
//判断悬浮窗口是否移动,这里做个标记,防止移动后松手触发了点击事件
private var isMove: Boolean = false
private inner class FloatingListener : View.OnTouchListener {
override fun onTouch(v: View, event: MotionEvent): Boolean {
val action = event.action
when (action) {
MotionEvent.ACTION_DOWN -> {
isMove = false
mTouchStartX = event.rawX.toInt()
mTouchStartY = event.rawY.toInt()
mStartX = event.x.toInt()
mStartY = event.y.toInt()
}
MotionEvent.ACTION_MOVE -> {
mTouchCurrentX = event.rawX.toInt()
mTouchCurrentY = event.rawY.toInt()
wmParams!!.x += mTouchCurrentX - mTouchStartX
wmParams!!.y += mTouchCurrentY - mTouchStartY
winManager!!.updateViewLayout(mFloatingLayout, wmParams)
mTouchStartX = mTouchCurrentX
mTouchStartY = mTouchCurrentY
}
MotionEvent.ACTION_UP -> {
mStopX = event.x.toInt()
mStopY = event.y.toInt()
if (Math.abs(mStartX - mStopX) >= 1 || Math.abs(mStartY - mStopY) >= 1) {
isMove = true
}
}
else -> {
}
}
//如果是移动事件不触发OnClick事件,防止移动的时候一放手形成点击事件
return isMove
}
}
自我介绍一下,小编13年上海交大毕业,曾经在小公司待过,也去过华为、OPPO等大厂,18年进入阿里一直到现在。
深知大多数初中级安卓工程师,想要提升技能,往往是自己摸索成长,但自己不成体系的自学效果低效又漫长,而且极易碰到天花板技术停滞不前!
因此收集整理了一份《2024年最新Android移动开发全套学习资料》送给大家,初衷也很简单,就是希望能够帮助到想自学提升又不知道该从何学起的朋友,同时减轻大家的负担。
由于文件比较大,这里只是将部分目录截图出来,每个节点里面都包含大厂面经、学习笔记、源码讲义、实战项目、讲解视频
如果你觉得这些内容对你有帮助,可以添加下面V无偿领取!(备注Android)
为了方便有学习需要的朋友,我把资料都整理成了视频教程(实际上比预期多花了不少精力),由于篇幅有限,都放在了我的GitHub上,点击即可免费获取!
Androidndroid架构视频+BAT面试专题PDF+学习笔记
当程序员容易,当一个优秀的程序员是需要不断学习的,从初级程序员到高级程序员,从初级架构师到资深架构师,或者走向管理,从技术经理到技术总监,每个阶段都需要掌握不同的能力。早早确定自己的职业方向,才能在工作和能力提升中甩开同龄人。
当程序员容易,当一个优秀的程序员是需要不断学习的,从初级程序员到高级程序员,从初级架构师到资深架构师,或者走向管理,从技术经理到技术总监,每个阶段都需要掌握不同的能力。早早确定自己的职业方向,才能在工作和能力提升中甩开同龄人。
无论你现在水平怎么样一定要 持续学习 没有鸡汤,别人看起来的毫不费力,其实费了很大力,没有人能随随便便成功。
升中甩开同龄人。
当程序员容易,当一个优秀的程序员是需要不断学习的,从初级程序员到高级程序员,从初级架构师到资深架构师,或者走向管理,从技术经理到技术总监,每个阶段都需要掌握不同的能力。早早确定自己的职业方向,才能在工作和能力提升中甩开同龄人。
无论你现在水平怎么样一定要 持续学习 没有鸡汤,别人看起来的毫不费力,其实费了很大力,没有人能随随便便成功。
加油,共勉。
Copyright © 2003-2013 www.wpsshop.cn 版权所有,并保留所有权利。