安卓悬浮窗----可移动的悬浮窗

目录

  • 前言
  • 一、添加对悬浮窗功能的支持
  • 二、通过service实现悬浮窗
    • 2.1 窗口属性和标志
    • 2.2 窗口移动
  • 三、完整代码


前言

记录一下基础的悬浮窗实现,分为几个重要的点进行阐述。

一、添加对悬浮窗功能的支持

app要实现悬浮窗功能,首先app要添加对悬浮窗功能的支持。

manifest文件添加权限:

   <uses-permission android:name="android.permission.SYSTEM_ALERT_WINDOW" />

app内也要去进行界面跳转,在设置里打开该应用的悬浮窗权限支持。

  if (!Settings.canDrawOverlays(this)) {Intent intent = new Intent();intent.setAction(Settings.ACTION_MANAGE_OVERLAY_PERMISSION);startActivity(intent);}

在这里插入图片描述

二、通过service实现悬浮窗

通过Button开启服务的方式来实现悬浮窗,使用Windowmanager添加悬浮窗View。

2.1 窗口属性和标志

悬浮窗的属性一般为TYPE_APPLICATION_OVERLAY、TYPE_SYSTEM_ALERT、TYPE_TOAST、TYPE_APPLICATION_OVERLAY 等,这里采用TYPE_APPLICATION_OVERLAY 赋予悬浮窗基本属性。

        if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.O) {params.type = WindowManager.LayoutParams.TYPE_APPLICATION_OVERLAY;} else {params.type = WindowManager.LayoutParams.TYPE_PHONE;}
// 设置悬浮框不可触摸params.flags = WindowManager.LayoutParams.FLAG_NOT_TOUCH_MODAL| WindowManager.LayoutParams.FLAG_NOT_FOCUSABLE;

2.2 窗口移动

对view进行ontouch事件的重写,更新坐标即可

  // 设置悬浮框的Touch监听btnView.setOnTouchListener(new View.OnTouchListener() {//保存悬浮框最后位置的变量int lastX, lastY;int paramX, paramY;@Overridepublic boolean onTouch(View v, MotionEvent event) {switch (event.getAction()) {case MotionEvent.ACTION_DOWN:lastX = (int) event.getRawX();lastY = (int) event.getRawY();paramX = params.x;paramY = params.y;break;case MotionEvent.ACTION_MOVE:int dx = (int) event.getRawX() - lastX;int dy = (int) event.getRawY() - lastY;params.x = paramX + dx;params.y = paramY + dy;// 更新悬浮窗位置windowManager.updateViewLayout(btnView, params);break;}return true;}});

三、完整代码

确实挺简单的,没什么可讲的。

activity

public class WindowActivity extends AppCompatActivity implements View.OnClickListener {private Button btn_on;Button btn_off;Boolean isOpen = false;Intent mIntent;@Overrideprotected void onCreate(Bundle savedInstanceState) {super.onCreate(savedInstanceState);setContentView(R.layout.activity_window);bindViews();}private void bindViews() {btn_on = findViewById(R.id.btn_on);btn_on.setOnClickListener(this);btn_off = findViewById(R.id.btn_off);btn_off.setOnClickListener(this);}@Overridepublic void onClick(View v) {switch (v.getId()) {case R.id.btn_on:mIntent = new Intent(WindowActivity.this, FloatService.class);mIntent.putExtra(FloatService.OPERATION, FloatService.OPERATION_SHOW);if (!Settings.canDrawOverlays(this)) {Intent intent = new Intent();intent.setAction(Settings.ACTION_MANAGE_OVERLAY_PERMISSION);startActivity(intent);} else {startService(mIntent);Toast.makeText(WindowActivity.this, "悬浮框已开启~", Toast.LENGTH_SHORT).show();isOpen = true;}break;case R.id.btn_off:if (isOpen) {stopService(mIntent);isOpen = false;}Toast.makeText(WindowActivity.this, "悬浮框已关闭~", Toast.LENGTH_SHORT).show();}}@Overrideprotected void onDestroy() {super.onDestroy();if (isOpen) {stopService(mIntent);isOpen = false;}}
}

FloatService

public class FloatService extends Service {Button btnView;WindowManager windowManager;WindowManager.LayoutParams params;Boolean isAdded;public static String OPERATION = "是否需要开启";public static int OPERATION_SHOW = 1;public static int OPERATION_HIDE = 2;int HANDLE_CHECK_ACTIVITY = 0;@Nullable@Overridepublic IBinder onBind(Intent intent) {return null;}@Overridepublic int onStartCommand(Intent intent, int flags, int startId) {int operation = intent.getIntExtra(OPERATION, 3);if (operation == OPERATION_SHOW) {mHandler.sendEmptyMessage(HANDLE_CHECK_ACTIVITY);} else if (operation == OPERATION_HIDE) {mHandler.removeMessages(HANDLE_CHECK_ACTIVITY);}return super.onStartCommand(intent, flags, startId);}@Overridepublic void onCreate() {createWindowView();super.onCreate();}@SuppressLint("ClickableViewAccessibility")private void createWindowView() {btnView = new Button(getApplicationContext());btnView.setBackgroundResource(R.drawable.author);windowManager = (WindowManager) getApplicationContext().getSystemService(Context.WINDOW_SERVICE);params = new WindowManager.LayoutParams();// 设置悬浮框不可触摸params.flags = WindowManager.LayoutParams.FLAG_NOT_TOUCH_MODAL| WindowManager.LayoutParams.FLAG_NOT_FOCUSABLE;// 悬浮窗不可触摸,不接受任何事件,同时不影响后面的事件响应params.format = PixelFormat.RGBA_8888;// 设置悬浮框的宽高params.width = 200;params.height = 200;params.gravity = Gravity.LEFT;params.x = 200;params.y = 000;// 设置Window Typeif (Build.VERSION.SDK_INT >= Build.VERSION_CODES.O) {params.type = WindowManager.LayoutParams.TYPE_APPLICATION_OVERLAY;} else {params.type = WindowManager.LayoutParams.TYPE_PHONE;}// 设置悬浮框的Touch监听btnView.setOnTouchListener(new View.OnTouchListener() {//保存悬浮框最后位置的变量int lastX, lastY;int paramX, paramY;@Overridepublic boolean onTouch(View v, MotionEvent event) {switch (event.getAction()) {case MotionEvent.ACTION_DOWN:lastX = (int) event.getRawX();lastY = (int) event.getRawY();paramX = params.x;paramY = params.y;break;case MotionEvent.ACTION_MOVE:int dx = (int) event.getRawX() - lastX;int dy = (int) event.getRawY() - lastY;params.x = paramX + dx;params.y = paramY + dy;// 更新悬浮窗位置windowManager.updateViewLayout(btnView, params);break;}return true;}});windowManager.addView(btnView, params);isAdded = true;}/*** 判断当前界面是否是桌面* android 6.0以上只能判断当前应用包名和Launcher*/private boolean isAtHome() {ActivityManager mActivityManager = (ActivityManager) getSystemService(Context.ACTIVITY_SERVICE);List<ActivityManager.RunningTaskInfo> runningTaskInfos = mActivityManager.getRunningTasks(1);Log.d("henry", "是否在主页面" + runningTaskInfos);return getHomeApplicationList().contains(runningTaskInfos.get(0).topActivity.getPackageName());}/*** 获得属于桌面的应用的应用包名称** @return 返回包含所有包名的字符串列表*//*** 获得属于桌面的应用的应用包名称* 返回包含所有包名的字符串列表数组** @return*/private List<String> getHomeApplicationList() {List<String> names = new ArrayList<String>();PackageManager packageManager = this.getPackageManager();Intent intent = new Intent(Intent.ACTION_MAIN);intent.addCategory(Intent.CATEGORY_HOME);List<ResolveInfo> resolveInfos = packageManager.queryIntentActivities(intent, PackageManager.MATCH_DEFAULT_ONLY);for (ResolveInfo resolveInfo : resolveInfos) {names.add(resolveInfo.activityInfo.packageName);}Log.d("henry", "主屏幕应用列表" + names);return names;}//定义一个更新界面的Handler@SuppressLint("HandlerLeak")private Handler mHandler = new Handler() {@Overridepublic void handleMessage(Message msg) {if (msg.what == HANDLE_CHECK_ACTIVITY) {
//                if (isAtHome()) {if (!isAdded) {windowManager.addView(btnView, params);isAdded = true;new Thread(new Runnable() {public void run() {for (int i = 0; i < 10; i++) {try {Thread.sleep(1000);} catch (InterruptedException e) {e.printStackTrace();}Message m = new Message();m.what = 2;mHandler.sendMessage(m);}}}).start();}
//                } else {
//                    if (isAdded) {
//                        windowManager.removeView(btnView);
//                        isAdded = false;
//                    }
//                }mHandler.sendEmptyMessageDelayed(HANDLE_CHECK_ACTIVITY, 100);}}};@Overridepublic void onDestroy() {if (isAdded) {windowManager.removeView(btnView);}mHandler.removeCallbacksAndMessages(null);windowManager = null;mHandler = null;super.onDestroy();}
}

别忘了在Manifest文件声明service

        <service android:name="com.henry.windowManagerTest.My_Floating_Window.FloatService" />

看一下实现效果:

在这里插入图片描述

后续增加缩放+MPAndroidChart效果。

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.hqwc.cn/news/700049.html

如若内容造成侵权/违法违规/事实不符,请联系编程知识网进行投诉反馈email:809451989@qq.com,一经查实,立即删除!

相关文章

Properties配置文件和源码

先对测试类进行get方法复写得到getReqType 判断caseinfo等于get时&#xff0c;就是get请求&#xff0c;反之就不是 这里的url和param都是xxx代替&#xff0c;如果直接写内容&#xff0c;每次都会请求 三目运算优化 为什么要用配置文件 test里时url,可以将ip和端口写在配置文…

AD中在DRC后如何显示详细错误(冲突)细节

最近在使用AD过程中出现了一个问题&#xff0c;在这里做一下记录&#xff0c;以便之后查询。 在我进行DRC后&#xff0c;出现了错误&#xff0c;但是当我去放大查找具体原因时发现以前有的细节现在却没有显示了&#xff0c;如下图所示&#xff0c;放大后并未显示详细的错误信息…

【解决】Unity Build 应用程序运行即崩溃问题

开发平台&#xff1a;Unity 2021.3.7f1c1   一、问题描述 编辑器 Build 工程结束&#xff0c;但控制台 未显示 Build completed with a result of Succeeded [时间长度] 信息。该情况下打包流程正常&#xff0c;但应用程序包打开即崩溃。   二、问题测试记录 测试1&#xf…

40V/300mA快速瞬态低压差线性稳压器替代ADP7142

概述(替代ADP7142/LT1962/LT1521/LT3060) PCD4900 是一款 CMOS、低压差&#xff08;LDO&#xff09;线性稳压器&#xff0c;采用 1.9V 至 40V 电源供电&#xff0c;最大输出电流为 300 mA。这款高输入电压 LDO 适用于调节 36V 至 1.2V 供电的高性能模拟和混合信号电路。该器件…

Python实现麦克风录音保存到wav

功能展示&#xff1a; 运行环境&#xff1a; Python: 3.10.4 64-bit 操作系统&#xff1a; 截图环境&#xff1a;win10 64-bit 视频录屏环境&#xff1a;win10 64-bit 功能说明&#xff1a; 点击界面开始按钮开始录音&#xff0c;点击停止按钮结束录音。 源码文件列表&…

CodeGeeX 智能编程助手 6 项功能升级,在Visual Studio插件市场霸榜2周!

CodeGeeX是支持适配Visual Studio 2019的唯一一款国产智能辅助编程工具&#xff0c;已经在Visual Studio趋势上霸榜2周&#xff01; CodeGeeX v1.0.4版本上线Visual Studio插件市场&#xff0c;带来了多项新功能和性能优化&#xff0c;提升编程体验。 新功能亮点速览&#xf…

号卡极团分销管理系统 ue_serve.php 任意文件上传漏洞复现

0x01 产品简介 号卡极团分销管理系统,同步对接多平台,同步订单信息,支持敢探号一键上架,首页多套UI+商品下单页多套模板,订单查询支持实时物流信息、支持代理商自定义域名、泛域名绑定,内置敢探号、172平台、号氪云平台第三方接口以及号卡网同系统对接! 0x02 漏洞概述…

如何快速生成接口文档(swagger和knife4j两种方式及其使用)

如何快速生成接口文档&#xff08;swagger和knife4j两种方式&#xff09; 1、什么是接口文档&#xff1f; 在项目开发中&#xff0c;web项目的前后端分离开发&#xff0c;APP开发&#xff0c;需要由前后端工程师共同定义接口&#xff0c;编写接口文档&#xff0c;之后大家都根…

AI与人类生活的融合:安克创新CEO阳萌的深度洞见

安克创新CEO阳萌分享了他对人工智能未来发展的深刻见解。阳萌不仅深入探讨了大模型技术的应用前景&#xff0c;还对AI与人类生活的融合提出了引人入胜的思考。以下是对这次访谈内容的总结和分析。 大模型技术的现实应用 阳萌提到&#xff0c;尽管大模型在处理通用知识方面表…

自动删除 PC 端微信缓存数据,包括从所有聊天中自动下载的大量文件、视频、图片等数据内容,解放你的空间。

Clean My PC Wechat 自动删除 PC 端微信自动下载的大量文件、视频、图片等数据内容&#xff0c;解放一年几十 G 的空间占用。 该工具不会删除文字的聊天记录&#xff0c;请放心使用。请给个 Star 吧&#xff0c;非常感谢&#xff01; 现已经支持 Windows 系统中的所有微信版本…

LeetCode2095删除链表的中间节点

题目描述 给你一个链表的头节点 head 。删除 链表的 中间节点 &#xff0c;并返回修改后的链表的头节点 head 。长度为 n 链表的中间节点是从头数起第 ⌊n / 2⌋ 个节点&#xff08;下标从 0 开始&#xff09;&#xff0c;其中 ⌊x⌋ 表示小于或等于 x 的最大整数。对于 n 1、…

JWT令牌技术实现登录校验

一.简单登录功能 在登录界面中&#xff0c;我们可以输入用户的用户名以及密码&#xff0c;然后点击 "登录" 按钮就要请求服务器&#xff0c;服务端判断用户输入的用户名或者密码是否正确。如果正确&#xff0c;则返回成功结果&#xff0c;跳转至系统首页面。 1.功能…