一、在samsung android8.0 真机运行效果图如下

二、废话不多说,直接上代码

1、代码架构

2、java/com/example/suspendedwindow/MainActivity.java

package com.example.suspendedwindow;import androidx.appcompat.app.AppCompatActivity;import java.util.Timer;
import java.util.TimerTask;import android.annotation.SuppressLint;
import android.graphics.Rect;
import android.os.Bundle;
import android.os.Handler;
import android.app.Activity;
import android.content.Context;
import android.content.Intent;
import android.graphics.PixelFormat;
import android.util.Log;
import android.view.Gravity;
import android.view.MotionEvent;
import android.view.View;
import android.view.View.OnClickListener;
import android.view.WindowManager;
import android.widget.Button;
import android.widget.FrameLayout;
import android.widget.ImageView;
import android.widget.LinearLayout;
import android.widget.RelativeLayout;
import android.widget.TextView;
import android.widget.Toast;/*** 倒计时60s悬浮窗** @author zy**/
public class MainActivity extends Activity {protected static final String TAG = "CountDownActivity";protected static final int TIME = 1;private Context context = MainActivity.this;private TextView tv_time;private Button cancle;private static Timer countDown = null;private int mValue = 60;private int statusBarHeight;// 状态栏高度WindowManager wm;WindowManager.LayoutParams params ;View countDownView;private boolean viewAdded = false;// 透明窗体是否已经显示//private WindowManager.LayoutParams layoutParams ;Handler post = new Handler();LinearLayout    commonCardContainer;LinearLayout.LayoutParams params_window;int width = 10;boolean lock = false ;int remeberx = 0;int remebery = 0;@Overrideprotected void onCreate(Bundle savedInstanceState) {super.onCreate(savedInstanceState);setContentView(R.layout.activity_main);}/*** 点击显示悬浮窗** @param*/@SuppressLint("WrongConstant")public void show(View v) {wm = (WindowManager) getApplicationContext().getSystemService(WINDOW_SERVICE); // 注意:这里必须是全局的context// 判断UI控件是否存在,存在则移除,确保开启任意次应用都只有一个悬浮窗if (countDownView != null) {wm.removeView(countDownView);}/*params = new WindowManager.LayoutParams(WindowManager.LayoutParams.WRAP_CONTENT,WindowManager.LayoutParams.WRAP_CONTENT, WindowManager.LayoutParams.TYPE_SYSTEM_ERROR,WindowManager.LayoutParams.FLAG_NOT_FOCUSABLE, PixelFormat.TRANSPARENT);
*/params = new WindowManager.LayoutParams();params.width  = WindowManager.LayoutParams.WRAP_CONTENT;params.height = WindowManager.LayoutParams.WRAP_CONTENT;params.flags = WindowManager.LayoutParams.FLAG_NOT_FOCUSABLE | WindowManager.LayoutParams.FLAG_NOT_TOUCH_MODAL;// 系统级别的窗口params.type =  WindowManager.LayoutParams.TYPE_APPLICATION_OVERLAY;//| WindowManager.LayoutParams.TYPE_SYSTEM_OVERLAY;WindowManager.LayoutParams.TYPE_APPLICATION_OVERLAY//  params.flags = WindowManager.LayoutParams.FLAG_NOT_FOCUSABLE | WindowManager.LayoutParams.FLAG_NOT_TOUCH_MODAL;// 居中显示// params.gravity = Gravity.CENTER;//params.gravity = Gravity.RIGHT|Gravity.BOTTOM; //悬浮窗开始在右下角显示params.gravity = Gravity.LEFT | Gravity.TOP;// 设置背景透明params.format = PixelFormat.TRANSPARENT;countDownView = new View(getApplicationContext()); // 不依赖activity的生命周期countDownView = View.inflate(getApplicationContext(),R.layout.countdown_weight, null);cancle = (Button) countDownView.findViewById(R.id.cancle);tv_time = (TextView) countDownView.findViewById(R.id.tv_time);tv_time.setText("60");wm.addView(countDownView, params);viewAdded = true;/*** 监听窗体移动事件*/countDownView.setOnTouchListener(new View.OnTouchListener() {float[] temp = new float[] { 0f, 0f };public boolean onTouch(View v, MotionEvent event) {Log.i(TAG, "temp[0]:" + temp[0]+" temp[1]:" + temp[1] + " X:" + event.getX()+" Y:"+event.getY() + " RawX:"+event.getRawX() + " RawY:"+event.getRawY());if(lock)return true;int eventaction = event.getAction();switch (eventaction) {case MotionEvent.ACTION_DOWN: // 按下事件,记录按下时手指在悬浮窗的XY坐标值temp[0] = event.getX();temp[1] = event.getY();break;case MotionEvent.ACTION_MOVE:refreshView((int) (event.getRawX() - temp[0]),(int) (event.getRawY() - temp[1]));break;}return true;}});// 设置“取消”倒计时按钮的监听cancle.setOnClickListener(new OnClickListener() {@Overridepublic void onClick(View view) {/*Log.e(TAG, "取消倒计时");wm.removeView(countDownView);countDownView = null;countDown.cancel();mValue = 999;*/
/*ImageView twenty_four_hour = (ImageView) countDownView.findViewById(R.id.img);LinearLayout.LayoutParams lp = (LinearLayout.LayoutParams) twenty_four_hour.getLayoutParams();lp.width = ++lp.width;twenty_four_hour.setLayoutParams(lp);Log.i(TAG, "lp.width:"+lp.width);
*/lock = !lock;if(lock){Toast.makeText(MainActivity.this, "Locked", Toast.LENGTH_SHORT).show();params.flags = 0 ;refresh();}else {Toast.makeText(MainActivity.this, "Unocked", Toast.LENGTH_SHORT).show();params.flags = WindowManager.LayoutParams.FLAG_NOT_FOCUSABLE | WindowManager.LayoutParams.FLAG_NOT_TOUCH_MODAL;refresh();}}});// 添加倒计时功能countDown = new Timer();countDown.schedule(new TimerTask() {@Overridepublic void run() {mValue--;post.post(drawCount);if (mValue == 0) {// 执行关机操作(这里可以使任意其他操作,根据自己的需求)Log.e(TAG, "关机");wm.removeView(countDownView);countDownView = null;// 取消定时countDown.cancel();finish();}}}, 0, 1000);// refreshView(295, 949);finish();}private void refreshView(int x, int y) {// 状态栏高度不能立即取,不然得到的值是0if (statusBarHeight == 0) {View rootView = countDownView.getRootView();Rect r = new Rect();rootView.getWindowVisibleDisplayFrame(r);statusBarHeight = r.top;}params.x = x;// y轴减去状态栏的高度,因为状态栏不是用户可以绘制的区域,不然拖动的时候会有跳动params.y = y - statusBarHeight;// STATUS_HEIGHT;Log.i(TAG, " x:"+x+" y:"+y+" params.x:"+params.x+" params.y:" +params.y +" statusBarHeight:"+statusBarHeight);//     params.x = 1080- params.x;//   params.y = 1920- params.y;//params.x = 311 ;//params.y = 1114 ;remeberx = params.x;remebery = params.y;refresh();
/*ImageView twenty_four_hour = (ImageView) countDownView.findViewById(R.id.img);LinearLayout.LayoutParams lp = (LinearLayout.LayoutParams) twenty_four_hour.getLayoutParams();twenty_four_hour.setLayoutParams(lp);Log.i(TAG, "lp.width:"+lp.width);*/}/*** 添加悬浮窗或者更新悬浮窗 如果悬浮窗还没添加则添加 如果已经添加则更新其位置*/private void refresh() {// 如果已经添加了就只更新viewif (viewAdded) {wm.updateViewLayout(countDownView, params);} else {wm.addView(countDownView, params);viewAdded = true;}}/*** 模拟其他操作* @param view*/public void other(View view) {Toast.makeText(context, "别的操作", Toast.LENGTH_SHORT).show();ImageView twenty_four_hour = (ImageView) findViewById(R.id.img);RelativeLayout.LayoutParams lp = (RelativeLayout.LayoutParams) twenty_four_hour.getLayoutParams();lp.width = lp.width + 1;twenty_four_hour.setLayoutParams(lp);Log.i(TAG, "lp.width:"+lp.width+ " topMargin:"+lp.topMargin);// startActivity(new Intent(context, NewActivity.class));}Runnable drawCount = new Runnable() {@Overridepublic void run() {tv_time.setText(Integer.toString(mValue));}};@Overrideprotected void onDestroy() {super.onDestroy();Log.e(TAG, "倒计时结束");};
}

3、res/layout/activity_main.xml

<RelativeLayout xmlns:android="http://schemas.android.com/apk/res/android"xmlns:tools="http://schemas.android.com/tools"android:layout_width="match_parent"android:layout_height="match_parent"tools:context=".MainActivity" ><ImageViewandroid:id="@+id/img"android:layout_width="152px"android:layout_height="wrap_content"android:layout_marginTop="126px"android:text="60"android:textColor="#ff0000"android:background="@drawable/background"android:textSize="25dp" /><Buttonandroid:layout_width="wrap_content"android:layout_height="wrap_content"android:onClick="show"android:text="点击显示弹窗" /><Buttonandroid:layout_alignParentBottom="true"android:layout_width="wrap_content"android:layout_height="wrap_content"android:onClick="other"android:text="别的操作" /></RelativeLayout>

4、res/layout/countdown_weight.xml

<?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"><LinearLayoutandroid:layout_width="wrap_content"android:layout_height="wrap_content"android:id="@+id/twenty_four_hour"android:layout_gravity="center"android:gravity="center_horizontal"android:orientation="vertical"android:padding="10dp" ><ImageViewandroid:id="@+id/img"android:layout_width="433px"android:layout_height="290px"android:text="60"android:textColor="#ff0000"android:background="@drawable/background"android:textSize="25dp" /><TextViewandroid:id="@+id/tv_time"android:layout_width="wrap_content"android:layout_height="wrap_content"android:text="60"android:textColor="#ff0000"android:textSize="25dp" /><Buttonandroid:id="@+id/cancle"android:layout_width="wrap_content"android:layout_height="wrap_content"android:text="取消"android:textSize="25dp"android:textColor="#000" /><!-- android:background="@drawable/xml_sel_button_bg" --></LinearLayout></FrameLayout>

5、申请权限

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

6、遇到的一个坑

三、参考文章

(664条消息) Android悬浮窗看这篇就够了_AndroidLMY的博客-CSDN博客_android 悬浮窗

(664条消息) Android悬浮窗的坑_android_cai_niao的博客-CSDN博客_android studio 悬浮窗

(664条消息) Android常用控件之悬浮窗_dztai的博客-CSDN博客_安卓悬浮窗

(664条消息) Android悬浮窗的实现--可以置顶,可以设置优先级的view_兮谁与歌的博客-CSDN博客_倒计时悬浮窗

2022-10-10 Android 在其他应用上的悬浮窗View相关推荐

  1. Android窗口Window的创建(悬浮窗)

    创建悬浮窗以及基于无障碍服务的窗口 关于悬浮窗的创建 启动悬浮窗 关于前台服务 启动服务 无障碍窗口 知识点 参考链接 关于悬浮窗的创建 首先需要获取WindowManager WindowManag ...

  2. android仿照360壁纸照片显示特效,Android仿360桌面手机卫士悬浮窗效果

    大家好,今天给大家带来一个仿360手机卫士悬浮窗效果的教程,在开始之前请允许我先说几句不相干的话. 不知不觉我发现自己接触Android已有近三个年头了,期间各种的成长少不了各位高手的帮助,总是有很多 ...

  3. 【Android 学习】实现仿360悬浮窗

    本篇博客转自郭霖的博客http://blog.csdn.net/guolin_blog/article/details/8689140 360手机卫士我相信大家都知道,好多人手机上都会装这一款软件,那 ...

  4. Android开发之仿手机卫士悬浮窗效果

    基本的实现原理,这种桌面悬浮窗的效果很类似与Widget,但是它比Widget要灵活的多.主要是通过WindowManager这个类来实现的,调用这个类的addView方法用于添加一个悬浮窗,upda ...

  5. Android实现仿360手机卫士悬浮窗效果

    大家好,今天给大家带来一个仿360手机卫士悬浮窗效果的教程,在开始之前请允许我说几句不相干的废话. 不知不觉我发现自己接触Android已有近三个年头了,期间各种的成长少不了各位高手的帮助,总是有很多 ...

  6. Android M及以上版本系统 悬浮窗权限 的解决方案

    Android M及以上版本系统 悬浮窗权限 的解决方案 Android的窗口体系中,WindowManager占有非常重要的地位,平时我们使用悬浮窗会遇到一些权限的问题. 当 Android工程在 ...

  7. Android仿优酷视频的悬浮窗播放

    Android仿优酷视频的悬浮窗播放 之前接了需求要让视频播放时可以像优酷视频那样在悬浮窗里播放,并且悬浮窗和主播放页面之间要实现无缝切换,项目中使用的是自封装的ijkplayer 这个要求就代表不能 ...

  8. android浮窗播放器,Android仿优酷视频的悬浮窗播放效果

    之前接了需求要让视频播放时可以像优酷视频那样在悬浮窗里播放,并且悬浮窗和主播放页面之间要实现无缝切换,项目中使用的是自封装的ijkplayer 这个要求就代表不能在悬浮窗中新建视频控件,所以需要在悬浮 ...

  9. Android各版本查询和开启悬浮窗权限

    Android 各版本查询和开启悬浮窗权限 如果你是从事Android开发的程序员,那么你肯定对于权限这个词不会陌生,Android的权限分为一般权限和危险权限,一般权限(只需在AndroidMani ...

  10. Android 10.0 根据包名默认授予app悬浮窗权限

    1.概述 在10.0的设备开发中,对于在app中授予悬浮窗权限,也是通常用的功能,但在设备产品中预制app,需求要求默认授予悬浮窗权限,就不需要在app中动态申请悬浮窗权限了,所以就来分析下这个实现这 ...

最新文章

  1. 上当记,收国外快递时,注意相关责任定义
  2. c#读取excel两列数据并绘制xy曲线_EXCEL绘制三Y轴图表
  3. 在深度神经网络中你有多吸引人?
  4. C++11 auto和decltype关键字
  5. 【定时同步系列3】8PSK调制+OM定时+信号分段处理+误码率曲线之MATLAB仿真(实信号模型)
  6. [js] 如何提升JSON.stringify的性能
  7. strus2中配置jqgrid入门
  8. 手工卡纸做机器人_亲子手工,带孩子用彩色卡纸做一朵可爱的小花,有教程
  9. Facebook 游戏开发更新文档 API 参考文档 v5.0
  10. Clearcase no version selected issue
  11. 如何轻松玩转APP设计规范——从Axure画草图PS复刻墨刀原型简单交互
  12. 802d简明调试手册_802d简明调试手册.pdf
  13. android obtain,Android Message.obtain() 之 高效原因分析
  14. mysql explain中的名词解释
  15. 移植st官方usb-hid程序出现babble detected错误
  16. Android手账本案例
  17. SAP生产订单删除步骤
  18. nbsp; ensp; emsp; thinsp;zwnj;zwj; 6种空白空格的区别
  19. C++控制不同进制输出(二进制,八进制,十进制,十六进制)各种进制之间的转换
  20. 当初我要是这么学操作系统就好了(附思维导图)

热门文章

  1. 【量子信息与量子计算简明教程|陈汉武】阅读笔记1——第一章 量子信息与量子计算的基础概念
  2. 【异常】java.lang.NoClassDefFoundError: com/lowagie/text/pdf/PdfContentByte
  3. xp系统打印服务器自动关闭,xp打印机服务器设置
  4. vlfeat python
  5. H5开发,打包成APK
  6. ENVI53 辐射校正、大气校正、影像裁剪超详细教程
  7. TTL和CMOS的区别
  8. lammps教程:velocity命令三种使用方法
  9. “SD卡已损坏。可能需要重新格式化卡” 解决办法!
  10. C#将集合key键以ASCII码从小到大排序