Android实现悬浮窗效果
作者:qq_21467035 发布时间:2022-08-17 05:59:40
标签:Android,悬浮窗
本文实例为大家分享了Android实现悬浮窗效果的具体代码,供大家参考,具体内容如下
一、权限:
<uses-permission android:name="android.permission.SYSTEM_ALERT_WINDOW" />
二、悬浮窗其实就是 WindowManager.addView(view,layoutParams),直接上代码
1、单例创建FloatWindowManager
/**
悬浮Manager
*/
public class FloatWindowManager {
private volatile static FloatWindowManager mInstance;
private WindowManager mWindowManager;
private Context mContext;
private WindowManager.LayoutParams mLayoutParams;
private int layoutY;
private int layoutX;
private ValueAnimator animator;
private TextView textView;
public static synchronized FloatWindowManager getInstance() {
if (mInstance == null) {
synchronized (FloatWindowManager.class) {
if (mInstance == null) {
mInstance = new FloatWindowManager();
}
}
}
return mInstance;
}
public FloatWindowManager initManager(Context context) {
mContext = context;
mWindowManager = (WindowManager) mContext.getSystemService(Context.WINDOW_SERVICE);
showWindow();
return this;
}
/**
* 是否有悬浮框权限
*
* @return
*/
public boolean requestPermission(Context context) {
return SettingsCompat.canDrawOverlays(context, true, false);
}
/**
* 加载 悬浮窗 没有内容
*/
private synchronized void showWindow() {
textView = new TextView(mContext);
textView.setText("此为悬浮窗口View");
textView.setTextSize(TypedValue.COMPLEX_UNIT_PX, dp2px(mContext, 15));
mLayoutParams = new WindowManager.LayoutParams();
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.O) {
mLayoutParams.type = WindowManager.LayoutParams.TYPE_APPLICATION_OVERLAY;
} else {
mLayoutParams.type = WindowManager.LayoutParams.TYPE_SYSTEM_ALERT;
}
mLayoutParams.format = PixelFormat.RGBA_8888; //窗口透明
mLayoutParams.gravity = Gravity.LEFT | Gravity.TOP; //窗口位置
mLayoutParams.flags = WindowManager.LayoutParams.FLAG_NOT_TOUCH_MODAL | WindowManager.LayoutParams.FLAG_NOT_FOCUSABLE;
DisplayMetrics displayMetrics = mContext.getResources().getDisplayMetrics();
layoutY = displayMetrics.heightPixels / 2;
layoutX = displayMetrics.widthPixels - textView.getMeasuredWidth();
mLayoutParams.width = WindowManager.LayoutParams.WRAP_CONTENT;
mLayoutParams.height = WindowManager.LayoutParams.WRAP_CONTENT;
mLayoutParams.x = layoutX;
mLayoutParams.y = layoutY;
setListener();
}
public void showFloatWindow(){
mWindowManager.addView(textView, mLayoutParams);
}
/**
* 设置 悬浮窗 view 滑动事件
*/
private void setListener() {
if (textView != null) {
textView.setOnTouchListener(new View.OnTouchListener() {
private int moveX; //动画平移距离
int startX, startY; //起始点
boolean isMove; //是否在移动
long startTime;
int finalMoveX; //最后通过动画将mView的X轴坐标移动到finalMoveX
boolean downMove = false;
@Override
public boolean onTouch(View v, MotionEvent event) {
switch (event.getAction()) {
case MotionEvent.ACTION_DOWN:
startX = (int) event.getX();
startY = (int) event.getY();
startTime = System.currentTimeMillis();
isMove = false;
downMove = false;
return false;
case MotionEvent.ACTION_MOVE:
//当移动距离大于2时候,刷新界面。
if (Math.abs(startX - event.getX()) > 2 || Math.abs(startY - event.getY()) > 2) {
downMove = true;
mLayoutParams.x = (int) (event.getRawX() - startX);
mLayoutParams.y = (int) (event.getRawY() - startY);
updateViewLayout(); //更新mView 的位置
}
return true;
case MotionEvent.ACTION_UP:
long curTime = System.currentTimeMillis();
isMove = curTime - startTime > 100;
if (isMove){
//判断mView是在Window中的位置,以中间为界
if (mLayoutParams.x + textView.getMeasuredWidth() / 2 >= mWindowManager.getDefaultDisplay().getWidth() / 2) {
finalMoveX = mWindowManager.getDefaultDisplay().getWidth() - textView.getMeasuredWidth();
} else {
finalMoveX = 0;
}
//使用动画移动mView
animator = ValueAnimator.ofInt(mLayoutParams.x, finalMoveX).setDuration(Math.abs(mLayoutParams.x - finalMoveX));
animator.addUpdateListener((ValueAnimator animation) -> {
if (animation != null) {
moveX = (int) animation.getAnimatedValue();
mLayoutParams.x = (int) animation.getAnimatedValue();
updateViewLayout();
}
});
animator.start();
}
return isMove;
}
return false;
}
});
}
}
/**
* 刷新 circle view 位置
*/
private void updateViewLayout() {
if (null != textView && null != mLayoutParams && mWindowManager != null) {
try {
mWindowManager.updateViewLayout(textView, mLayoutParams);
} catch (Exception e) {
e.printStackTrace();
}
}
}
/**
* 根据手机的分辨率从 dp 的单位 转成为 px(像素)
*/
public static int dp2px(Context context, float dpValue) {
if (context != null) {
final float scale = context.getResources().getDisplayMetrics().density;
return (int) (dpValue * scale + 0.5f);
}
return (int) dpValue;
}
2、SettingsCompat 动态权限判断(适配大部分厂商)
public class SettingsCompat {
private static final int OP_WRITE_SETTINGS = 23;
private static final int OP_SYSTEM_ALERT_WINDOW = 24;
/**
* 检查悬浮窗权限 当没有权限,跳转到权限设置界面
*
* @param context 上下文
* @param isShowDialog 没有权限,是否弹框提示跳转到权限设置界面
* @param isShowPermission 是否跳转权限开启界面
* @return true 有权限 false 没有权限(跳转权限界面、权限失败 提示用户手动设置权限)
* @by 腾讯云直播 悬浮框判断逻辑
*/
public static boolean canDrawOverlays(Context context, boolean isShowDialog, boolean isShowPermission) {
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.M) {
if (!Settings.canDrawOverlays(context)) {
if (isShowDialog) {
//去授权
SettingsCompat.manageDrawOverlays(context);
} else if (isShowPermission) {
manageDrawOverlays(context);
}
return false;
}
return true;
} else if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.JELLY_BEAN_MR2) {
if (checkOp(context, OP_SYSTEM_ALERT_WINDOW)) {
return true;
} else {
if (isShowPermission)
startFloatWindowPermissionErrorToast(context);
return false;
}
} else {
return true;
}
}
/**
* 打开 悬浮窗 授权界面
*
* @param context
*/
public static void manageDrawOverlays(Context context) {
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.M) {
try {
Intent intent = new Intent(Settings.ACTION_MANAGE_OVERLAY_PERMISSION);
intent.setData(Uri.parse("package:" + context.getPackageName()));
intent.addFlags(Intent.FLAG_ACTIVITY_NEW_TASK);
context.startActivity(intent);
} catch (Exception e) {
e.printStackTrace();
startFloatWindowPermissionErrorToast(context);
}
} else if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.JELLY_BEAN_MR2) {
if (!manageDrawOverlaysForRom(context)) {
startFloatWindowPermissionErrorToast(context);
}
}
}
/**
* 权限设置 失败提示。
*
* @param context
*/
public static void startFloatWindowPermissionErrorToast(Context context) {
if (context != null)
Toast.makeText(context, "进入设置页面失败,请手动开启悬浮窗权限", Toast.LENGTH_SHORT).show();
}
private static boolean manageDrawOverlaysForRom(Context context) {
if (RomUtil.isMiui()) {
return manageDrawOverlaysForMiui(context);
}
if (RomUtil.isEmui()) {
return manageDrawOverlaysForEmui(context);
}
if (RomUtil.isFlyme()) {
return manageDrawOverlaysForFlyme(context);
}
if (RomUtil.isOppo()) {
return manageDrawOverlaysForOppo(context);
}
if (RomUtil.isVivo()) {
return manageDrawOverlaysForVivo(context);
}
if (RomUtil.isQiku()) {
return manageDrawOverlaysForQihu(context);
}
if (RomUtil.isSmartisan()) {
return manageDrawOverlaysForSmartisan(context);
}
return false;
}
private static boolean checkOp(Context context, int op) {
AppOpsManager manager = (AppOpsManager) context.getSystemService(Context.APP_OPS_SERVICE);
try {
Method method = AppOpsManager.class.getDeclaredMethod("checkOp", int.class, int.class, String.class);
return AppOpsManager.MODE_ALLOWED == (int) method.invoke(manager, op, Binder.getCallingUid(), context.getPackageName());
} catch (Exception e) {
}
return false;
}
// 可设置Android 4.3/4.4的授权状态
private static boolean setMode(Context context, int op, boolean allowed) {
if (Build.VERSION.SDK_INT < Build.VERSION_CODES.JELLY_BEAN_MR2 || Build.VERSION.SDK_INT >= Build.VERSION_CODES.LOLLIPOP) {
return false;
}
AppOpsManager manager = (AppOpsManager) context.getSystemService(Context.APP_OPS_SERVICE);
try {
Method method = AppOpsManager.class.getDeclaredMethod("setMode", int.class, int.class, String.class, int.class);
method.invoke(manager, op, Binder.getCallingUid(), context.getPackageName(), allowed ? AppOpsManager.MODE_ALLOWED : AppOpsManager.MODE_IGNORED);
return true;
} catch (Exception e) {
}
return false;
}
/**
* 跳转界面
*
* @param context
* @param intent
* @return
*/
private static boolean startSafely(Context context, Intent intent) {
List<ResolveInfo> resolveInfos = null;
try {
resolveInfos = context.getPackageManager().queryIntentActivities(intent, PackageManager.MATCH_DEFAULT_ONLY);
if (resolveInfos != null && resolveInfos.size() > 0) {
intent.setFlags(Intent.FLAG_ACTIVITY_NEW_TASK);
context.startActivity(intent);
return true;
}
} catch (Exception e) {
e.printStackTrace();
}
return false;
}
// 小米
private static boolean manageDrawOverlaysForMiui(Context context) {
Intent intent = new Intent("miui.intent.action.APP_PERM_EDITOR");
intent.putExtra("extra_pkgname", context.getPackageName());
intent.setClassName("com.miui.securitycenter", "com.miui.permcenter.permissions.AppPermissionsEditorActivity");
if (startSafely(context, intent)) {
return true;
}
intent.setClassName("com.miui.securitycenter", "com.miui.permcenter.permissions.PermissionsEditorActivity");
if (startSafely(context, intent)) {
return true;
}
// miui v5 的支持的android版本最高 4.x
// http://www.romzj.com/list/search?keyword=MIUI%20V5#search_result
if (Build.VERSION.SDK_INT < Build.VERSION_CODES.LOLLIPOP) {
Intent intent1 = new Intent(Settings.ACTION_APPLICATION_DETAILS_SETTINGS);
intent1.setData(Uri.fromParts("package", context.getPackageName(), null));
return startSafely(context, intent1);
}
return false;
}
private final static String HUAWEI_PACKAGE = "com.huawei.systemmanager";
// 华为
private static boolean manageDrawOverlaysForEmui(Context context) {
Intent intent = new Intent();
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.LOLLIPOP) {
intent.setClassName(HUAWEI_PACKAGE, "com.huawei.systemmanager.addviewmonitor.AddViewMonitorActivity");
if (startSafely(context, intent)) {
return true;
}
}
// Huawei Honor P6|4.4.4|3.0
intent.setClassName(HUAWEI_PACKAGE, "com.huawei.notificationmanager.ui.NotificationManagmentActivity");
intent.putExtra("showTabsNumber", 1);
if (startSafely(context, intent)) {
return true;
}
intent.setClassName(HUAWEI_PACKAGE, "com.huawei.permissionmanager.ui.MainActivity");
if (startSafely(context, intent)) {
return true;
}
return false;
}
// VIVO
private static boolean manageDrawOverlaysForVivo(Context context) {
// 不支持直接到达悬浮窗设置页,只能到 i管家 首页
Intent intent = new Intent("com.iqoo.secure");
intent.setClassName("com.iqoo.secure", "com.iqoo.secure.MainActivity");
// com.iqoo.secure.ui.phoneoptimize.SoftwareManagerActivity
// com.iqoo.secure.ui.phoneoptimize.FloatWindowManager
return startSafely(context, intent);
}
// OPPO
private static boolean manageDrawOverlaysForOppo(Context context) {
Intent intent = new Intent();
intent.putExtra("packageName", context.getPackageName());
// OPPO A53|5.1.1|2.1
intent.setAction("com.oppo.safe");
intent.setClassName("com.oppo.safe", "com.oppo.safe.permission.floatwindow.FloatWindowListActivity");
if (startSafely(context, intent)) {
return true;
}
// OPPO R7s|4.4.4|2.1
intent.setAction("com.color.safecenter");
intent.setClassName("com.color.safecenter", "com.color.safecenter.permission.floatwindow.FloatWindowListActivity");
if (startSafely(context, intent)) {
return true;
}
intent.setAction("com.coloros.safecenter");
intent.setClassName("com.coloros.safecenter", "com.coloros.safecenter.sysfloatwindow.FloatWindowListActivity");
return startSafely(context, intent);
}
// 魅族
private static boolean manageDrawOverlaysForFlyme(Context context) {
Intent intent = new Intent("com.meizu.safe.security.SHOW_APPSEC");
intent.setClassName("com.meizu.safe", "com.meizu.safe.security.AppSecActivity");
intent.putExtra("packageName", context.getPackageName());
return startSafely(context, intent);
}
// 360
private static boolean manageDrawOverlaysForQihu(Context context) {
Intent intent = new Intent();
intent.setClassName("com.android.settings", "com.android.settings.Settings$OverlaySettingsActivity");
if (startSafely(context, intent)) {
return true;
}
intent.setClassName("com.qihoo360.mobilesafe", "com.qihoo360.mobilesafe.ui.index.AppEnterActivity");
return startSafely(context, intent);
}
// 锤子
private static boolean manageDrawOverlaysForSmartisan(Context context) {
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.M) {
return false;
}
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.LOLLIPOP) {
// 锤子 坚果|5.1.1|2.5.3
Intent intent = new Intent("com.smartisanos.security.action.SWITCHED_PERMISSIONS_NEW");
intent.setClassName("com.smartisanos.security", "com.smartisanos.security.SwitchedPermissions");
intent.putExtra("index", 17); // 不同版本会不一样
return startSafely(context, intent);
} else {
// 锤子 坚果|4.4.4|2.1.2
Intent intent = new Intent("com.smartisanos.security.action.SWITCHED_PERMISSIONS");
intent.setClassName("com.smartisanos.security", "com.smartisanos.security.SwitchedPermissions");
intent.putExtra("permission", new String[]{Manifest.permission.SYSTEM_ALERT_WINDOW});
// Intent intent = new Intent("com.smartisanos.security.action.MAIN");
// intent.setClassName("com.smartisanos.security", "com.smartisanos.security.MainActivity");
return startSafely(context, intent);
}
}
}
3、厂商 RomUtil
public class RomUtil {
private static final String TAG = "RomUtil";
public static final String ROM_MIUI = "MIUI";
public static final String ROM_EMUI = "EMUI";
public static final String ROM_FLYME = "FLYME";
public static final String ROM_OPPO = "OPPO";
public static final String ROM_SMARTISAN = "SMARTISAN";
public static final String ROM_VIVO = "VIVO";
public static final String ROM_QIKU = "QIKU";
public static final String ROM_LENOVO = "LENOVO";
public static final String ROM_SAMSUNG = "SAMSUNG";
private static final String KEY_VERSION_MIUI = "ro.miui.ui.version.name";
private static final String KEY_VERSION_EMUI = "ro.build.version.emui";
private static final String KEY_VERSION_OPPO = "ro.build.version.opporom";
private static final String KEY_VERSION_SMARTISAN = "ro.smartisan.version";
private static final String KEY_VERSION_VIVO = "ro.vivo.os.version";
private static final String KEY_VERSION_GIONEE = "ro.gn.sv.version";
private static final String KEY_VERSION_LENOVO = "ro.lenovo.lvp.version";
private static final String KEY_VERSION_FLYME = "ro.build.display.id";
private static final String KEY_EMUI_VERSION_CODE = "ro.build.hw_emui_api_level";
private static final String KEY_MIUI_VERSION_CODE = "ro.miui.ui.version.code";
private static final String KEY_MIUI_HANDY_MODE_SF = "ro.miui.has_handy_mode_sf";
private static final String KEY_MIUI_REAL_BLUR = "ro.miui.has_real_blur";
private static final String KEY_FLYME_PUBLISHED = "ro.flyme.published";
private static final String KEY_FLYME_FLYME = "ro.meizu.setupwizard.flyme";
private static final String KEY_FLYME_ICON_FALG = "persist.sys.use.flyme.icon";
private static final String KEY_FLYME_SETUP_FALG = "ro.meizu.setupwizard.flyme";
private static final String KEY_FLYME_PUBLISH_FALG = "ro.flyme.published";
private static final String KEY_VIVO_OS_NAME = "ro.vivo.os.name";
private static final String KEY_VIVO_OS_VERSION = "ro.vivo.os.version";
private static final String KEY_VIVO_ROM_VERSION = "ro.vivo.rom.version";
public static boolean isEmui() {
return check(ROM_EMUI);
}
public static boolean isMiui() {
return check(ROM_MIUI);
}
public static boolean isVivo() {
return check(ROM_VIVO);
}
public static boolean isOppo() {
return check(ROM_OPPO);
}
public static boolean isFlyme() {
return check(ROM_FLYME);
}
public static boolean isQiku() {
return check(ROM_QIKU) || check("360");
}
public static boolean isSmartisan() {
return check(ROM_SMARTISAN);
}
private static String sName;
public static String getName() {
if (sName == null) {
check("");
}
return sName;
}
private static String sVersion;
public static String getVersion() {
if (sVersion == null) {
check("");
}
return sVersion;
}
public static boolean check(String rom) {
if (sName != null) {
return sName.equals(rom);
}
if (!TextUtils.isEmpty(sVersion = getProp(KEY_VERSION_MIUI))) {
sName = ROM_MIUI;
} else if (!TextUtils.isEmpty(sVersion = getProp(KEY_VERSION_EMUI))) {
sName = ROM_EMUI;
} else if (!TextUtils.isEmpty(sVersion = getProp(KEY_VERSION_OPPO))) {
sName = ROM_OPPO;
} else if (!TextUtils.isEmpty(sVersion = getProp(KEY_VERSION_VIVO))) {
sName = ROM_VIVO;
} else if (!TextUtils.isEmpty(sVersion = getProp(KEY_VERSION_SMARTISAN))) {
sName = ROM_SMARTISAN;
} else {
sVersion = Build.DISPLAY;
if (sVersion.toUpperCase().contains(ROM_FLYME)) {
sName = ROM_FLYME;
} else {
sVersion = Build.UNKNOWN;
sName = Build.MANUFACTURER.toUpperCase();
}
}
return sName.equals(rom);
}
public static String getProp(String name) {
String line = null;
BufferedReader input = null;
try {
Process p = Runtime.getRuntime().exec("getprop " + name);
input = new BufferedReader(new InputStreamReader(p.getInputStream()), 1024);
line = input.readLine();
input.close();
} catch (IOException ex) {
Log.e(TAG, "Unable to read prop " + name, ex);
return null;
} finally {
if (input != null) {
try {
input.close();
} catch (IOException e) {
e.printStackTrace();
}
}
}
return line;
}
}
来源:https://blog.csdn.net/qq_21467035/article/details/115177456


猜你喜欢
- 目录前言错误实例演示实现ApplicationContextAware接口lookup methodlookup method签名总结前言看
- 我们都知道,当RecyclerView数据源更新后,还需要通过adapter调用对应的方法,从而让RecyclerView重新绘制页面本次也
- 有的时候,我们需要对一堆数据进行统计分析后生成HTML或Excel格式报表。本来这并不是一件很难的事,但确是件比较麻烦的事情。最令人头痛的是
- 好几天没有跟进Spring MVC的学习了,之前看了点源码都忘的差不多了。这次就跟着之前的问题,继续总结下Spring MVC中的小知识。u
- 前言偶然逛国外博客,看到了一个介绍文字动画的库,进入 pub 一看,立马就爱上这个动画库了,几乎你能想到的文字动画效果它都有!现在正式给大家
- 前言本文主要写的是:springboot下ueditor上传功能的实现及遇到的一些问题的处理整体项目结构展示Springboot整合uedi
- ADO.NET数据访问技术的一个突出特点就是支持离线访问,而实现这种离线访问的技术核心急速DataSet对象,该对象通过数据驻留在内存来实现
- 当前比较成熟一点的应用基本上都会在进入应用之显示一个启动界面.这个启动界面或简单,或复杂,或简陋,或华丽,用意不同,风格也不同.下面来观摩几
- 前言我们在写搬砖的过程中,少不了需要将A对象转成B对象,对对象进行对象的转换是一个操作重复且繁琐的工作。于是市面上就有许多的对象转换工具来解
- 使用Java的方式配置Spring我们现在要完全不使用Spring的xml配置,全权使用Java来配置Spring!JavaConfig是S
- 我们第三章分析过客户端接入的流程, 这一小节带大家剖析客户端发送数据, Server读取数据的流程:首先温馨提示, 这一小节高度耦合第三章的
- 本文实例为大家分享了Android App自动更新通知栏下载的具体代码,供大家参考,具体内容如下版本更新说明这里有调用UpdateServi
- 一、前言Android 实现卫星式菜单也叫弧形菜单,主要要做的工作如下:1.动画的处理2.自定义ViewGroup来实现卫星式菜单View(
- 最近接了一个项目其中有功能要实现一个清理内存,要求和微信的效果一样。于是想到用surfaceView而不是继承view。下面小编给大家解析下
- Maven 翻译为"专家"、"内行",是 Apache 下的一个纯 Java 开发的开源项目。基于项
- Word中设置水印时,可使用预设的文字或自定义文字设置为水印效果,但通常添加水印效果时,会对所有页面都设置成统一效果,如果需要对每一页或者某
- 一、基本概念Task优势ThreadPool相比Thread来说具备了很多优势,但是ThreadPool却又存在一些使用上的不方便,例如:T
- 介绍众所周知,AOP(面向切面编程)是Spring框架的特色功能之一。通过设置横切关注点(cross cutting concerns),A
- 背景:SpringMVC如何响应json格式的数据?技术实现方式1:在Controller使用@RestController注解方式2:在C
- 一、前言文稿扫描大家用的都比较频繁、想是各种证件、文件都可以通过扫描文稿功能保存到手机。相比直接拍照,在扫描文稿时,程序会对图像进行一些矫正