前言

相信大家对Android悬浮窗应该是很熟悉了,比如说腾讯视频、爱奇艺等APP都有悬浮窗功能。在你打游戏的同时还可以看视频,充分利用屏幕空间。还有微信,360手机卫士等APP也有悬浮窗功能。那么Android悬浮窗是怎么实现的呢?

项目源码:Android仿腾讯视频悬浮窗的实现

其实并不难,核心代码就只有一行:

windowManager.addView(view, layoutParams)

效果图

 对view比较熟悉的同学们应该发现了,其实我们的悬浮窗就是一个view,我把只需要把view添加到windowManager上就可以了。那么,开始讲细节了:

权限一定要记得加:

因为我们的悬浮窗要在Launcher上或者在其他APP上面运行,所以这里就用到了service,因为service可以默默地在后台运行。

实现大致步骤:

1.检查权限(如果没有权限跳转到授权界面)

2.在service中用inflate方法获取我们需要的view,设置位置参数等,加入到windowManager里面

3.启动悬浮窗服务

 

view布局

<?xml version="1.0" encoding="utf-8"?>            

对应的界面: 

FloatingWindowService

package com.example.floatingwindowimport android.annotation.SuppressLintimport android.app.Serviceimport android.content.Contextimport android.content.Intentimport android.os.Buildimport android.os.IBinderimport android.view.*import android.widget.ImageViewimport android.widget.Toastclass FloatingWindowService : Service(){    private lateinit var layoutParams: WindowManager.LayoutParams    private lateinit var windowManager: WindowManager    override fun onBind(intent: Intent): IBinder? {        // TODO: Return the communication channel to the service.        throw UnsupportedOperationException("Not yet implemented")    }    override fun onStartCommand(intent: Intent, flags: Int, startId: Int): Int {        showFloatingWindow()        return super.onStartCommand(intent, flags, startId)    }    @SuppressLint("ClickableViewAccessibility")    private fun showFloatingWindow() {        // 获取WindowManager服务        windowManager = getSystemService(Context.WINDOW_SERVICE) as WindowManager        // 新建悬浮窗控件        val view = LayoutInflater.from(this).inflate(R.layout.window, null)        // 设置LayoutParam        layoutParams = WindowManager.LayoutParams()        if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.O) {            layoutParams.type = WindowManager.LayoutParams.TYPE_APPLICATION_OVERLAY        } else {            layoutParams.type = WindowManager.LayoutParams.TYPE_PHONE        }        //设置位置        layoutParams.gravity = Gravity.LEFT or Gravity.TOP        layoutParams.x = windowManager.defaultDisplay.width        layoutParams.y = 200        //设置flag        layoutParams.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        //设置view的宽高        layoutParams.width = WindowManager.LayoutParams.WRAP_CONTENT        layoutParams.height = WindowManager.LayoutParams.WRAP_CONTENT        //添加拖拽事件        view.setOnTouchListener(FloatingOnTouchListener())        val close = view.findViewById(R.id.close)        close.setOnClickListener {            stopSelf()            windowManager.removeView(view)            Toast.makeText(this,"close",Toast.LENGTH_SHORT).show()        }        // 将悬浮窗控件添加到WindowManager        windowManager.addView(view, layoutParams)    }    override fun onDestroy() {        super.onDestroy()        Toast.makeText(this,"onDestroy",Toast.LENGTH_SHORT).show()    }    inner class FloatingOnTouchListener : View.OnTouchListener {        private var x = 0f        private var y = 0f        @SuppressLint("ClickableViewAccessibility")        override fun onTouch(v: View?, event: MotionEvent?): Boolean {            when(event?.action){                MotionEvent.ACTION_DOWN ->{                    x = event.rawX                    y = event.rawY                }                MotionEvent.ACTION_MOVE ->{                    val nowX = event.rawX                    val nowY = event.rawY                    val movedX = nowX - x                    val movedY = nowY - y                    x = nowX                    y = nowY                    layoutParams.x = (layoutParams.x + movedX).toInt()                    layoutParams.y = (layoutParams.y + movedY).toInt()                    windowManager.updateViewLayout(v, layoutParams)                }                MotionEvent.ACTION_UP ->{                }            }            return false        }    }}

先获取windowManager,加载我们的悬浮窗view,这里的TYPE_APPLICATION_OVERLAY的作用是把我们的view设置成系统顶层窗口,显示在其他一切内容之上。TYPE_SYSTEM_OVERLAY的作用也是一样的,只不过现在被遗弃调了。

设置初始位置:

初始位置,这里可以看一下Android坐标系相关知识,Android 零坐标在屏幕左上方。这里设置一下xy坐标的位置就可以。

设置flag: 

设置flag的作用是让view不获取焦点。如果不做处理,view会遮住屏幕其他控件的点击事件。

拖拽功能:

FloatingOnTouchListener是一个内部类,它可以使用FloatingWindowService类中的变量。实现OnTouchListener接口,当屏幕点击时记录下当前位置,屏幕滑动时计算出划过的距离,修改layoutParams的xy坐标,调用windowManager.updateViewLayout(v, layoutParams)方法就可以更新view当前位置。



MainActivity

package com.example.floatingwindowimport android.content.Intentimport android.net.Uriimport android.os.Buildimport android.os.Bundleimport android.provider.Settingsimport android.widget.Toastimport androidx.appcompat.app.AppCompatActivityclass MainActivity : AppCompatActivity() {    override fun onCreate(savedInstanceState: Bundle?) {        super.onCreate(savedInstanceState)        startFloatingService()    }    private fun startFloatingService() {        if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.M) {            if (!Settings.canDrawOverlays(this)) {                startActivityForResult(Intent(Settings.ACTION_MANAGE_OVERLAY_PERMISSION, Uri.parse("package:$packageName")), 0)            } else {                startService(Intent(this@MainActivity, FloatingWindowService::class.java))            }        }    }    override fun onActivityResult(requestCode: Int, resultCode: Int, data: Intent?) {        super.onActivityResult(requestCode, resultCode, data)        if (requestCode == 0) {            if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.M) {                if (!Settings.canDrawOverlays(this)) {                    Toast.makeText(this, "授权失败", Toast.LENGTH_SHORT).show()                } else {                    Toast.makeText(this, "授权成功", Toast.LENGTH_SHORT).show()                    startService(Intent(this@MainActivity, FloatingWindowService::class.java))                }            }        }    }}

到这里悬浮窗的实现基本就结束了。

项目图片资源:资源

码云项目源码:Android仿腾讯视频悬浮窗的实现

 

更多相关文章

  1. Android时区问题
  2. Android之——史上最简单最酷炫的3D图片浏览效果的实现
  3. Android中EditText(或TextView)中的InputType类型含义与如何定义
  4. Android+JavaEE实现登录注册(2)——Android登录界面设计
  5. Android(安卓)Studio官方文档之Android(安卓)Studio代理配置篇
  6. android中根据控件宽度,实现展示文本内容,解决中英文自动换行
  7. android shape 代码实现按钮背景
  8. android shape 代码实现按钮背景
  9. android设置Activity背景色为透明的2种方法

随机推荐

  1. 7.1.2 DatePicker结合案例详解
  2. android 验证手机号和邮箱格式
  3. Android应用程序获取ROOT权限代码
  4. Android 6.0 Math代替FloatMath
  5. android2.3中支持的语言对照
  6. android 横竖屏切换与数据保存
  7. Android 检测程序异常崩溃,重启应用
  8. Android service里面启动activity和alert
  9. 设计模式--静态工厂设计模式在android中
  10. android读取data/data/包名/file路径下的