日韩性视频-久久久蜜桃-www中文字幕-在线中文字幕av-亚洲欧美一区二区三区四区-撸久久-香蕉视频一区-久久无码精品丰满人妻-国产高潮av-激情福利社-日韩av网址大全-国产精品久久999-日本五十路在线-性欧美在线-久久99精品波多结衣一区-男女午夜免费视频-黑人极品ⅴideos精品欧美棵-人人妻人人澡人人爽精品欧美一区-日韩一区在线看-欧美a级在线免费观看

歡迎訪問 生活随笔!

生活随笔

當前位置: 首頁 > 运维知识 > Android >内容正文

Android

Android 可滑动的悬浮View

發布時間:2024/3/26 Android 35 豆豆
生活随笔 收集整理的這篇文章主要介紹了 Android 可滑动的悬浮View 小編覺得挺不錯的,現在分享給大家,幫大家做個參考.

這種創建懸浮view的前提是獲取到了懸浮的權限

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

思路:
1,獲取WindowManager

2,使用LayoutInflater新建懸浮的View

3,為懸浮的View設置LayoutParam(包括默認的相對屏幕的對齊方式,讓view獲取焦點等)

4,為懸浮的View設置觸摸的監聽,重寫OnTouch(View view, MotionEvent event),根據手勢處理view移動停止時的位置(根據當前view停留的位置坐標,結合屏幕寬度,確定貼邊是左還是右)

在展示懸浮View時要動態判斷是否有懸浮權限:

/*** Created by Lyq* on 2021-09-17*/ public class PermissionUtils {/**** 檢查懸浮窗開啟權限* @param context* @return*/public static boolean checkFloatPermission(Context context) {if (Build.VERSION.SDK_INT < Build.VERSION_CODES.KITKAT)//19 4.4return true;if (Build.VERSION.SDK_INT < Build.VERSION_CODES.M) {//23 6.0try {Class cls = Class.forName("android.content.Context");Field declaredField = cls.getDeclaredField("APP_OPS_SERVICE");declaredField.setAccessible(true);Object obj = declaredField.get(cls);if (!(obj instanceof String)) {return false;}String str2 = (String) obj;obj = cls.getMethod("getSystemService", String.class).invoke(context, str2);cls = Class.forName("android.app.AppOpsManager");Field declaredField2 = cls.getDeclaredField("MODE_ALLOWED");declaredField2.setAccessible(true);Method checkOp = cls.getMethod("checkOp", Integer.TYPE, Integer.TYPE, String.class);int result = (Integer) checkOp.invoke(obj, 24, Binder.getCallingUid(), context.getPackageName());return result == declaredField2.getInt(cls);} catch (Exception e) {return false;}} else {if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.O) {//26 8.0AppOpsManager appOpsMgr = (AppOpsManager) context.getSystemService(Context.APP_OPS_SERVICE);if (appOpsMgr == null)return false;int mode = appOpsMgr.checkOpNoThrow("android:system_alert_window", android.os.Process.myUid(), context.getPackageName());return Settings.canDrawOverlays(context) || mode == AppOpsManager.MODE_ALLOWED || mode == AppOpsManager.MODE_IGNORED;} else {return Settings.canDrawOverlays(context);}}}}

懸浮View的具體實現(封的工具類):

/*** Created by Lyq* on 2021-09-17*/ public class FloatingViewUtils {private Context context;private int screenWidth;private WindowManager.LayoutParams layoutParams;private WindowManager windowManager;private View floatView;private FloatingViewUtils() {}private static class InstanceHolder {@SuppressLint("StaticFieldLeak")private static final FloatingViewUtils sInstance = new FloatingViewUtils();private InstanceHolder() {}}public static FloatingViewUtils getInstance() {return FloatingViewUtils.InstanceHolder.sInstance;}public void init(Context context) {this.context = context;if (windowManager != null) return;//獲取WindowManager服務windowManager = (WindowManager) context.getSystemService(Context.WINDOW_SERVICE);//獲取屏寬screenWidth = getScreenWidth(MyApplication.getInstance());}/*** 展示懸浮窗* @param layoutId 懸浮窗布局文件id*/@SuppressLint("RtlHardcoded")public void showFloatingWindow(@LayoutRes int layoutId) {// 新建懸浮窗控件LayoutInflater layoutInflater = LayoutInflater.from(context);//? ? ? ? View floatView = layoutInflater.inflate(R.layout.floating_view, null);View floatView = layoutInflater.inflate(layoutId, null);if (floatView == null) {throw new NullPointerException("懸浮窗view為null 檢查布局文件是否可用");}showFloatingWindow(floatView);}/*** 展示懸浮窗* @param floatView 懸浮窗view*/@SuppressLint("RtlHardcoded")public void showFloatingWindow(@NonNull View floatView) {if (this.floatView != null) return;//有懸浮窗在顯示 不再顯示新的懸浮窗// 新建懸浮窗控件if (floatView == null) {throw new NullPointerException("懸浮窗view為null 確認view不為null");}this.floatView = floatView;//設置觸摸事件floatView.setOnTouchListener(new FloatingOnTouchListener());//懸浮窗設置點擊事件floatView.setOnClickListener(new View.OnClickListener() {@Overridepublic void onClick(View v) {Toast.makeText(context, "點擊了懸浮窗", Toast.LENGTH_SHORT).show();unInit();}});// 設置LayoutParamlayoutParams = new WindowManager.LayoutParams();if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.O){layoutParams.type = WindowManager.LayoutParams.TYPE_APPLICATION_OVERLAY;}layoutParams.gravity = Gravity.LEFT | Gravity.CENTER;//設置flags 不然懸浮窗出來后整個屏幕都無法獲取焦點,layoutParams.flags = WindowManager.LayoutParams.FLAG_NOT_FOCUSABLE;layoutParams.format = PixelFormat.RGBA_8888;layoutParams.width = WindowManager.LayoutParams.WRAP_CONTENT;layoutParams.height = WindowManager.LayoutParams.WRAP_CONTENT;layoutParams.x = 0;layoutParams.y = 0;// 將懸浮窗控件添加到WindowManagerwindowManager.addView(floatView, layoutParams);}/*** 移除懸浮View*/public void unInit() {hideFloatWindow();this.context = null;// 獲取WindowManager服務windowManager = null;}/*** 隱藏懸浮窗* ? ?*/public void hideFloatWindow() {if (floatView != null) {windowManager.removeViewImmediate(floatView);floatView = null;}}private class FloatingOnTouchListener implements View.OnTouchListener {private int x;private int y;//標記是否執行move事件 如果執行了move事件? 在up事件的時候判斷懸浮窗的位置讓懸浮窗處于屏幕左邊或者右邊private boolean isScroll;//標記懸浮窗口是否移動了? 防止設置點擊事件的時候 窗口移動松手后觸發點擊事件private boolean isMoved;//事件開始時和結束的時候? X和Y坐標位置private int startX;private int startY;@Overridepublic boolean onTouch (View view, MotionEvent event){switch (event.getAction()) {case MotionEvent.ACTION_DOWN:x = (int) event.getRawX();y = (int) event.getRawY();isMoved = false;isScroll = false;startX = (int) event.getRawX();startY = (int) event.getRawY();break;case MotionEvent.ACTION_MOVE:int nowX = (int) event.getRawX();int nowY = (int) event.getRawY();int movedX = nowX - x;int movedY = nowY - y;x = nowX;y = nowY;layoutParams.x = layoutParams.x + movedX;layoutParams.y = layoutParams.y + movedY;// 更新懸浮窗控件布局windowManager.updateViewLayout(view, layoutParams);isScroll = true;break;case MotionEvent.ACTION_UP:int stopX = (int) event.getRawX();int stopY = (int) event.getRawY();if (Math.abs(startX - stopX) >= 1 || Math.abs(startY - stopY) >= 1) {isMoved = true;}if (isScroll) {autoView(view);}break;}return isMoved;}//懸浮窗view自動停靠在屏幕左邊或者右邊private void autoView (View view){// 得到view在屏幕中的位置int[] location = new int[2];view.getLocationOnScreen(location);//判斷view位置是否在屏幕中線的左側,是的話貼屏幕左邊,否則貼屏幕右邊if (location[0] < (getScreenWidth(MyApplication.getInstance()))/2) {layoutParams.x = 0;} else {layoutParams.x = getScreenWidth(MyApplication.getInstance()) - view.getWidth();}windowManager.updateViewLayout(view, layoutParams);}}public View getFloatView() {return floatView;}/*** 獲取屏幕寬度* @param context* @return*/public int getScreenWidth(Context context) {WindowManager manager = (WindowManager) context.getSystemService(Context.WINDOW_SERVICE);return manager.getDefaultDisplay().getWidth();} }

具體的使用:

class MainActivity : AppCompatActivity() {private val FloatRequestCode = 1002override fun onCreate(savedInstanceState: Bundle?) {super.onCreate(savedInstanceState)setContentView(R.layout.activity_main)val test_button = findViewById<Button>(R.id.test_button)test_button.setOnClickListener {val checkFloatPermission = PermissionUtils.checkFloatPermission(this)if(!checkFloatPermission){requestFloatPermission(this,FloatRequestCode)}else{Toast.makeText(this,"有懸浮權限",Toast.LENGTH_SHORT).show()val floatingWindowUtils = FloatingViewUtils.getInstance()floatingWindowUtils.init(this)floatingWindowUtils.showFloatingWindow(R.layout.layout_floatview)}}}/*** 懸浮窗開啟權限* @param context* @param requestCode*/fun requestFloatPermission(context: Activity, requestCode: Int) {val intent = Intent(Settings.ACTION_MANAGE_OVERLAY_PERMISSION)intent.data = Uri.parse("package:" + context.packageName)context.startActivityForResult(intent, requestCode)}/*** 設置中開啟后再次校驗,來展示懸浮View*/override fun onActivityResult(requestCode: Int, resultCode: Int, data: Intent?) {super.onActivityResult(requestCode, resultCode, data)if (requestCode == FloatRequestCode) {val handler = Handler()handler.postDelayed(Runnable {if (!checkFloatPermission(this)) {requestFloatPermission(this, FloatRequestCode)}}, 1000)}}}

總結

以上是生活随笔為你收集整理的Android 可滑动的悬浮View的全部內容,希望文章能夠幫你解決所遇到的問題。

如果覺得生活随笔網站內容還不錯,歡迎將生活随笔推薦給好友。