<em>Mac</em>Book项目 2009年学校开始实施<em>Mac</em>Book项目,所有师生配备一本<em>Mac</em>Book,并同步更新了校园无线网络。学校每周进行电脑技术更新,每月发送技术支持资料,极大改变了教学及学习方式。因此2011
2021-06-01 09:32:01
本文範例為大家分享了Android實現懸浮窗效果的具體程式碼,供大家參考,具體內容如下
<uses-permission android:name="android.permission.SYSTEM_ALERT_WINDOW" />
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; } }
以上就是本文的全部內容,希望對大家的學習有所幫助,也希望大家多多支援it145.com。
相關文章
<em>Mac</em>Book项目 2009年学校开始实施<em>Mac</em>Book项目,所有师生配备一本<em>Mac</em>Book,并同步更新了校园无线网络。学校每周进行电脑技术更新,每月发送技术支持资料,极大改变了教学及学习方式。因此2011
2021-06-01 09:32:01
综合看Anker超能充系列的性价比很高,并且与不仅和iPhone12/苹果<em>Mac</em>Book很配,而且适合多设备充电需求的日常使用或差旅场景,不管是安卓还是Switch同样也能用得上它,希望这次分享能给准备购入充电器的小伙伴们有所
2021-06-01 09:31:42
除了L4WUDU与吴亦凡已经多次共事,成为了明面上的厂牌成员,吴亦凡还曾带领20XXCLUB全队参加2020年的一场音乐节,这也是20XXCLUB首次全员合照,王嗣尧Turbo、陈彦希Regi、<em>Mac</em> Ova Seas、林渝植等人全部出场。然而让
2021-06-01 09:31:34
目前应用IPFS的机构:1 谷歌<em>浏览器</em>支持IPFS分布式协议 2 万维网 (历史档案博物馆)数据库 3 火狐<em>浏览器</em>支持 IPFS分布式协议 4 EOS 等数字货币数据存储 5 美国国会图书馆,历史资料永久保存在 IPFS 6 加
2021-06-01 09:31:24
开拓者的车机是兼容苹果和<em>安卓</em>,虽然我不怎么用,但确实兼顾了我家人的很多需求:副驾的门板还配有解锁开关,有的时候老婆开车,下车的时候偶尔会忘记解锁,我在副驾驶可以自己开门:第二排设计很好,不仅配置了一个很大的
2021-06-01 09:30:48
不仅是<em>安卓</em>手机,苹果手机的降价力度也是前所未有了,iPhone12也“跳水价”了,发布价是6799元,如今已经跌至5308元,降价幅度超过1400元,最新定价确认了。iPhone12是苹果首款5G手机,同时也是全球首款5nm芯片的智能机,它
2021-06-01 09:30:45