马上注册,结交更多好友,享用更多功能,让你轻松玩转社区。
您需要 登录 才可以下载或查看,没有账号?立即注册
×
目录
一.使用悬浮窗功能的原因:
二.悬浮窗的具体实现步骤:
一.添加权限:
二.动态申请权限:
三.WindowManager.LayoutParams的TYPE类型:
四.编写代码:
1.XML代码:
2.Activity相干代码:
3.WindowManager相干代码
一.使用悬浮窗功能的原因:
在Android开辟中我们想要做到提示用户关键信息的作用的时间.比方App更新信息之类的,有很多种方式去实现,紧张的话还是三种方式Dialog,AlertDialog,PopupWindow,但是他们都有一个共同的缺点那就是依靠于Activity,而悬浮窗是不依靠Activity的,甚至,App在背景运行,悬浮窗仍旧会弹出来,只要App进程不被杀死,但是悬浮窗也有缺点,那就是权限标题
二.悬浮窗的具体实现步骤:
一.添加权限:
- <!-- 悬浮窗需要添加该权限-->
- <uses-permission android:name="android.permission.SYSTEM_ALERT_WINDOW" />
复制代码 二.动态申请权限:
- private fun initFloatWindow() {
- // 权限判断
- if (!Settings.canDrawOverlays(applicationContext)) {
- // 启动Activity让用户授权
- val mIntent = Intent(Settings.ACTION_MANAGE_OVERLAY_PERMISSION,
- Uri.parse("package:${packageName}"))
- startActivityForResult(mIntent, 10)
- } else {
- // 已经有权限了,就去初始化对应的视图或者悬浮窗弹窗的初始化
- initView()
- }
- }
- override fun onActivityResult(requestCode: Int, resultCode: Int, data: Intent?) {
- super.onActivityResult(requestCode, resultCode, data)
- if (resultCode == 10) {
- if (Settings.canDrawOverlays(applicationContext)) {
- initView()
- } else {
- ToastUtils.showToast(this, "请设置对应权限")
- }
- }
- }
复制代码 这段代码是用于在Activity中动态申请悬浮窗权限的,上面谁人 ToastUtils是我本身封装的吐司类,这个很简单,相信大家都会,终极我们进入对应的Activity当中我们会跳转到如许一个界面来获取该应用的悬浮窗权限,像电视大概车载开辟之类的对于权限已经做了静默处理了,总不能我们在使用车载导航的时间,弹出一个是否开启定位吧哈哈
三.WindowManager.LayoutParams的TYPE类型:
为什么要将这个TYPE类型放在第三位呢,因为这个就是导致各种标题出现的罪魁祸首
- type = when {
- Build.VERSION.SDK_INT >= Build.VERSION_CODES.O -> WindowManager.LayoutParams.TYPE_APPLICATION_OVERLAY
- Build.VERSION.SDK_INT >= Build.VERSION_CODES.N_MR1 -> WindowManager.LayoutParams.TYPE_PHONE
- else -> WindowManager.LayoutParams.TYPE_TOAST
- }
复制代码 上述这段代码是用来根据不同的Android版本设置窗口类型的,使用的Kotlin的when表达式,来判断设备的Android版本(Build.VERSION.SDK_INT),并根据版本返回不同的窗口类型(TYPE)
1. 假如 Android 版本大于或等于 Oreo(API 级别 26),则窗口类型为TYPE_APPLICATION_OVERLAY ,这通常用于应用程序的悬浮窗。
2. 假如 Android 版本大于或等于 Nougat MR1(API 级别 25),则窗口类型为 TYPE_PHONE ,这通常用于电话应用的窗口。
3. 假如以上两个条件都不满意,则使用 TYPE_TOAST ,这通常用于显示短暂的 Toast 消息。
我举一个例子,如下:
这是因为我的手机为Android7我用的是被镌汰的TYPE类型,这个情况下我们需要更换TYPE类型就能办理对应的标题
四.编写代码:
1.XML代码:
- <?xml version="1.0" encoding="utf-8"?>
- <LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
- android:layout_width="match_parent"
- android:layout_height="match_parent"
- android:orientation="vertical"
- >
- <TextView
- android:background="#f46d43"
- android:paddingVertical="10dp"
- android:gravity="center"
- android:textSize="28sp"
- android:textColor="#ffffff"
- android:text="WindowManager与悬浮框"
- android:layout_width="match_parent"
- android:layout_height="wrap_content"/>
- <Button
- android:textAllCaps="false"
- android:id="@+id/activityshow_btn"
- android:text="Activity显示悬浮框"
- android:layout_width="match_parent"
- android:layout_height="wrap_content"/>
- <Button
- android:textAllCaps="false"
- android:id="@+id/serviceshow_btn"
- android:text="Service显示悬浮框"
- android:layout_width="match_parent"
- android:layout_height="wrap_content"/>
- <Button
- android:textAllCaps="false"
- android:id="@+id/activityshow_btn_cancle"
- android:text="Activity隐藏悬浮框"
- android:layout_width="match_parent"
- android:layout_height="wrap_content"/>
- </LinearLayout>
复制代码 2.Activity相干代码:
- /**
- * 悬浮窗: 不依赖Activity, 当Activity销毁的时候悬浮窗仍然可以显示,注意悬浮窗需要手动获取权限以及声明权限
- */
- class FloatingWindowActivity : AppCompatActivity(), View.OnClickListener {
- private lateinit var mActivityFloatingWindow: ActivityFloatingWindow
- private val mServiceshowBtn by lazy { findViewById<Button>(R.id.serviceshow_btn) }
- private val mActivityshowBtn by lazy { findViewById<Button>(R.id.activityshow_btn) }
- private val m_activityshow_btn_cancle by lazy { findViewById<Button>(R.id.activityshow_btn_cancle) }
- override fun onCreate(savedInstanceState: Bundle?) {
- super.onCreate(savedInstanceState)
- setContentView(R.layout.activity_floating_window)
- initFloatWindow()
- initListener()
- }
- private fun initFloatWindow() {
- // 权限判断
- if (!Settings.canDrawOverlays(applicationContext)) {
- // 启动Activity让用户授权
- val mIntent = Intent(Settings.ACTION_MANAGE_OVERLAY_PERMISSION,
- Uri.parse("package:${packageName}"))
- startActivityForResult(mIntent, 10)
- } else {
- // 已经有权限了
- initView()
- }
- }
- override fun onActivityResult(requestCode: Int, resultCode: Int, data: Intent?) {
- super.onActivityResult(requestCode, resultCode, data)
- if (resultCode == 10) {
- if (Settings.canDrawOverlays(applicationContext)) {
- initView()
- } else {
- ToastUtils.showToast(this, "请设置对应权限")
- }
- }
- }
- private fun initView() {
- mActivityFloatingWindow = ActivityFloatingWindow(this)
- }
- private fun initListener() {
- mServiceshowBtn.setOnClickListener(this)
- mActivityshowBtn.setOnClickListener(this)
- m_activityshow_btn_cancle.setOnClickListener(this)
- }
- override fun onClick(v: View?) {
- when(v?.id) {
- R.id.serviceshow_btn -> {
- }
- R.id.activityshow_btn -> {
- activityShowFloatingWindow()
- }
- R.id.activityshow_btn_cancle -> {
- mActivityFloatingWindow.remove()
- }
- }
- }
- private fun activityShowFloatingWindow() {
- mActivityFloatingWindow.showFloatWindow()
- }
- }
复制代码 3.WindowManager相干代码
- class ActivityFloatingWindow(context: Context) : View.OnTouchListener {
- private var mContext: Context
- private lateinit var mWindowParams: WindowManager.LayoutParams
- private lateinit var mWindowManager: WindowManager
- private lateinit var rootLayout: View
- init {
- mContext = context
- initFloatWindow()
- }
- private var mInViewX = 0f
- private var mInViewY = 0f
- private var mDownInScreenX = 0f
- private var mDownInScreenY = 0f
- private var mInScreenX = 0f
- private var mInScreenY = 0f
- var isMoving = false
- /**
- * 初始化布局
- */
- private fun initFloatWindow() {
- rootLayout = LayoutInflater.from(mContext)
- .inflate(R.layout.floatingwidow_in_activity, null)
- rootLayout.setOnTouchListener(this)
- mWindowParams = WindowManager.LayoutParams()
- mWindowManager = MyApp.mApplicationContext?.getSystemService(Context.WINDOW_SERVICE) as WindowManager
- mWindowParams.type = WindowManager.LayoutParams.TYPE_PHONE
- mWindowParams.format = PixelFormat.RGBA_8888
- // 设置悬浮窗不获取焦点的原因就是为了传递事件
- mWindowParams.flags = WindowManager.LayoutParams.FLAG_NOT_FOCUSABLE
- mWindowParams.gravity = Gravity.START or Gravity.TOP
- mWindowParams.height = WindowManager.LayoutParams.WRAP_CONTENT
- mWindowParams.width = WindowManager.LayoutParams.WRAP_CONTENT
- defaultPosition()
- }
- fun showFloatWindow() {
- if (null == rootLayout.parent) {
- mWindowManager.addView(rootLayout, mWindowParams)
- }
- }
- fun remove() {
- if (null != rootLayout.parent) {
- mWindowManager.removeView(rootLayout)
- }
- }
- override fun onTouch(v: View?, event: MotionEvent?): Boolean {
- return floatLayoutTouch(event!!)
- }
- private fun floatLayoutTouch(motionEvent: MotionEvent): Boolean {
- when (motionEvent.action) {
- MotionEvent.ACTION_DOWN -> {
- // 获取相对View的坐标,即以此View左上角为原点
- mInViewX = motionEvent.x
- mInViewY = motionEvent.y
- // 获取相对屏幕的坐标,即以屏幕左上角为原点
- mDownInScreenX = motionEvent.rawX
- mDownInScreenY = motionEvent.rawY
- mInScreenX = motionEvent.rawX
- mInScreenY = motionEvent.rawY
- isMoving = true
- }
- MotionEvent.ACTION_MOVE -> {
- // 更新浮动窗口位置参数
- mInScreenX = motionEvent.rawX
- mInScreenY = motionEvent.rawY
- mWindowParams.x = (mInScreenX - mInViewX).toInt()
- mWindowParams.y = (mInScreenY - mInViewY).toInt()
- // 手指移动的时候更新小悬浮窗的位置
- mWindowManager.updateViewLayout(rootLayout, mWindowParams)
- isMoving = true
- }
- MotionEvent.ACTION_UP -> {
- isMoving = false
- // 如果手指离开屏幕时,xDownInScreen和xInScreen相等,且yDownInScreen和yInScreen相等,则视为触发了单击事件。
- if (mDownInScreenX == mInScreenX && mDownInScreenY == mInScreenY) {
- Toast.makeText(mContext, "Click", Toast.LENGTH_SHORT).show()
- }
- }
- }
- return true
- }
- private fun defaultPosition() {
- val metrics = DisplayMetrics()
- // 默认固定位置,靠屏幕左边缘的中间
- mWindowManager.defaultDisplay.getMetrics(metrics)
- mWindowParams.x = 0
- mWindowParams.y = metrics.heightPixels/2
- }
- }
复制代码 !!!这里的WIndowManager相干的代码我保举大家使用单例,我这里没有使用单例,运行结果如下:

免责声明:如果侵犯了您的权益,请联系站长,我们会及时删除侵权内容,谢谢合作!更多信息从访问主页:qidao123.com:ToB企服之家,中国第一个企服评测及商务社交产业平台。 |