Android仿腾讯视频实现悬浮窗效果
作者:吐尔洪江Coding 发布时间:2021-06-16 00:49:23
前言
相信大家对Android悬浮窗应该是很熟悉了,比如说腾讯视频、爱奇艺等APP都有悬浮窗功能。在你打游戏的同时还可以看视频,充分利用屏幕空间。还有微信,360手机卫士等APP也有悬浮窗功能。那么Android悬浮窗是怎么实现的呢?
项目源码:Android仿腾讯视频悬浮窗的实现
其实并不难,核心代码就只有一行:
windowManager.addView(view, layoutParams)
效果图
对view比较熟悉的同学们应该发现了,其实我们的悬浮窗就是一个view,我把只需要把view添加到windowManager上就可以了。那么,开始讲细节了:
权限一定要记得加:
<uses-permission android:name="android.permission.SYSTEM_ALERT_WINDOW" />
因为我们的悬浮窗要在Launcher上或者在其他APP上面运行,所以这里就用到了service,因为service可以默默地在后台运行。
实现大致步骤:
1.检查权限(如果没有权限跳转到授权界面)
2.在service中用inflate方法获取我们需要的view,设置位置参数等,加入到windowManager里面
3.启动悬浮窗服务
view布局
<?xml version="1.0" encoding="utf-8"?>
<FrameLayout
xmlns:android="http://schemas.android.com/apk/res/android"
android:layout_width="wrap_content"
android:layout_height="wrap_content">
<ImageView
android:layout_width="200dp"
android:layout_height="100dp"
android:src="@drawable/huge"></ImageView>
<ImageView
android:id="@+id/close"
android:src="@drawable/close"
android:layout_width="30dp"
android:layout_height="30dp"
android:layout_marginLeft="170dp">
</ImageView>
</FrameLayout>
对应的界面:
FloatingWindowService
package com.example.floatingwindow
import android.annotation.SuppressLint
import android.app.Service
import android.content.Context
import android.content.Intent
import android.os.Build
import android.os.IBinder
import android.view.*
import android.widget.ImageView
import android.widget.Toast
class 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<ImageView>(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.floatingwindow
import android.content.Intent
import android.net.Uri
import android.os.Build
import android.os.Bundle
import android.provider.Settings
import android.widget.Toast
import androidx.appcompat.app.AppCompatActivity
class 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仿腾讯视频悬浮窗的实现
来源:https://blog.csdn.net/daydayup05/article/details/117428891
猜你喜欢
- 本文实例讲述了Java使用Jdbc连接Oracle执行简单查询操作。分享给大家供大家参考,具体如下:Java Jdbc 连接 Oracle
- 本文实例展示了DevExpress根据条件设置GridControl RepositoryItem是否可编辑的方法。一般在C#项目的开发中,
- 提示:java.util.zipoutputstream
- 有时候因为安全问题,需要把配置文件的中数据库用户名密码由明文改成密文,大多数其实是为了应付甲方而已。1.pom.xml引入依赖<dep
- Feign获取异常信息最近在使用Feign调用时,出现了异常,原本使用的是fallback,直接返回了自定义的结果@Override &nb
- 在项目中有一个需求是需要在局域网内跨PC远程调用一个程序,并且要求有界面显示,调查了一些资料,能实现远程调用的.Net技术大概有PsExec
- Android API Demos中有很多非常Nice的例子,这些例子的代码都写的很出色,如果大家把API Demos中的每个例子研究透了,
- 编写RedisConfig首先我们要明白RedisConfig中需要包含什么,首先看看我们直接使用RedisTemplate的问题,我们就知
- jwt简介冒泡排序:(Bubble Sort)是一种简单的交换排序。之所以叫做冒泡排序,因为我们可以把每个元素当成一个小气泡,根据气泡大小,
- 这种属性应用方式是field_name=@field_value@。两个@符号是springboot为替代${}属性占位符产生,原因是${}
- 场景:有一个喜欢吃饺子,他有三种不同的方式去吃,蒸饺子,煮饺子,煎饺子,想要用策略模式来设计这个场景,怎么弄?1.复习简单工厂模式具体的代码
- 本文为大家分享了Tablayout简单的使用方法,供大家参考,具体内容如下一、TabLayout普通用法在项目中使用viewpager的时候
- 1. 前言在Java开发中接触的开发者大多数不太注重对接口的测试,结果在联调对接中出现各种问题。也有的使用Postman等工具进行测试,虽然
- 废话不多说了,直接给大家贴关键代码了。具体代码如下所示:using System;using System.Collections.Gene
- 演示 - 初始化销毁顺序/* 初始化和销毁的执行顺序 */@SpringBootApplicationpublic
- 本文为大家分享了java画出五子棋游戏棋盘的方法,供大家参考,具体内容如下棋盘模块:画五子棋棋盘:19条横线、19条竖线步骤一:显示棋盘我有
- 条码的应用已深入生活和工作的方方面面。在处理条码时,常需要和各种文档格式相结合。当需要在文档中插入、编辑或者删除条码时,可借助于一些专业的类
- 背景Springboot 默认把异常的处理集中到一个ModelAndView中了,但项目的实际过程中,这样做,并不能满足我们的要求。具体的自
- 本文实例演示了visual C#下一个类的定义及实现方法,虽然是一个较为基础的C#代码实例,对于新手来说仍然有很好的参考价值。具体的实例代码
- java中的LIST在删除时,一般会用list.remove(o); 但这样往往会出现问题,先来看下面的这段代码:package com.d