Android实现悬浮窗效果

Cherise ·
更新时间:2024-09-20
· 968 次阅读

本文实例为大家分享了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;     } }



Android

需要 登录 后方可回复, 如果你还没有账号请 注册新账号