一、Android懸浮窗實現保活的缺點
在Android應用開發中,有時需要使用懸浮窗來實現一些特殊的需求,比如浮層彈窗、置頂通知等。但是懸浮窗會帶來一些問題,保活就是其中之一。當我們啟動懸浮窗時,系統需要給我們的應用分配系統資源,而此時如果經常操作該懸浮窗,那麼系統可能就會kill掉你的應用,導致懸浮窗無法正常顯示。當然,我們可以通過設置一些參數來緩解這個問題,比如設置懸浮窗類型、優化代碼等。
以下是一個簡單的示例代碼,用於設置懸浮窗類型為TYPE_SYSTEM_ALERT:
WindowManager.LayoutParams layoutParams = new WindowManager.LayoutParams(); layoutParams.width = WindowManager.LayoutParams.WRAP_CONTENT; layoutParams.height = WindowManager.LayoutParams.WRAP_CONTENT; layoutParams.type = WindowManager.LayoutParams.TYPE_SYSTEM_ALERT;//設置懸浮窗類型 layoutParams.format = PixelFormat.RGBA_8888; layoutParams.flags = WindowManager.LayoutParams.FLAG_NOT_TOUCH_MODAL | WindowManager.LayoutParams.FLAG_NOT_FOCUSABLE; //創建懸浮窗 WindowManager windowManager = (WindowManager) getSystemService(Context.WINDOW_SERVICE); View view = LayoutInflater.from(this).inflate(R.layout.layout_float_window, null); windowManager.addView(view, layoutParams);
二、Android開發懸浮窗
在Android中,懸浮窗的開發需要用到WindowManager類,同時需要注意懸浮窗的許可權問題,可以在Manifest.xml中添加如下許可權:
<uses-permission android:name="android.permission.SYSTEM_ALERT_WINDOW"/>
為了保證懸浮窗正常顯示,需要在代碼中動態添加許可權:
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.M && !Settings.canDrawOverlays(context)) { //開啟懸浮窗設置頁面 Intent intent = new Intent(Settings.ACTION_MANAGE_OVERLAY_PERMISSION); context.startActivity(intent); }
除了上述許可權的設置,我們還需要在懸浮窗布局中添加一個關閉按鈕,用於在關閉懸浮窗時釋放資源。以下代碼為一個示例:
//獲取關閉按鈕 Button btnCloseFloatWindow = (Button) viewFloatWindow.findViewById(R.id.btnCloseFloatWindow); //添加關閉按鈕監聽器 btnCloseFloatWindow.setOnClickListener(new View.OnClickListener() { @Override public void onClick(View v) { windowManager.removeView(viewFloatWindow);//移除懸浮窗 } });
三、Android懸浮窗實現靈動島
靈動島是一個比較流行的懸浮窗應用,它的主要功能是在屏幕上顯示一個可供拖拽、伸縮和輪廓調整的懸浮窗,可以通過長按懸浮窗,彈出菜單執行不同的功能。以下是該應用的功能實現簡述:
首先,我們需要定義懸浮窗布局,並添加可拖動、伸縮和輪廓調整的屬性:
//獲取懸浮窗布局 floatingView = LayoutInflater.from(this).inflate(R.layout.layout_floating, null); //添加可拖動、伸縮和輪廓調整屬性 floatLayoutParams = new WindowManager.LayoutParams(WindowManager.LayoutParams.WRAP_CONTENT, WindowManager.LayoutParams.WRAP_CONTENT, Build.VERSION.SDK_INT >= Build.VERSION_CODES.O ? WindowManager.LayoutParams.TYPE_APPLICATION_OVERLAY : WindowManager.LayoutParams.TYPE_PHONE, WindowManager.LayoutParams.FLAG_NOT_FOCUSABLE | WindowManager.LayoutParams.FLAG_LAYOUT_NO_LIMITS, PixelFormat.TRANSLUCENT);
接下來,我們需要監聽懸浮窗拖拽事件:
//獲取可拖動區域並添加觸摸事件監聽器 LinearLayout drag = floatingView.findViewById(R.id.dragView); drag.setOnTouchListener(new View.OnTouchListener() { private int x; private int y; private float screenX; private float screenY; private float viewX; private float viewY; private int touchState; @Override public boolean onTouch(View view, MotionEvent event) { boolean moved = false; switch (event.getActionMasked()) { case MotionEvent.ACTION_DOWN: //按下事件 x = floatLayoutParams.x; y = floatLayoutParams.y; screenX = event.getRawX(); screenY = event.getRawY(); viewX = event.getX(); viewY = event.getY(); if (touchState == 0) { touchState = 1; } else if (touchState == 2) { touchState = 3; } break; case MotionEvent.ACTION_MOVE: //移動事件 float moveX = event.getRawX() - screenX; float moveY = event.getRawY() - screenY; if (touchState == 1 && Math.abs(moveX) < 5 && Math.abs(moveY) < 5) { break; } float moveViewX = event.getX() - viewX; float moveViewY = event.getY() - viewY; updateViewPosition((int) (x + moveX), (int) (y + moveY)); updateViewSize((int) moveViewX, (int) moveViewY); moved = true; if (touchState == 1) { touchState = 2; } else if (touchState == 3) { touchState = 4; } break; case MotionEvent.ACTION_UP: //抬起事件 case MotionEvent.ACTION_CANCEL: //取消事件 if (!moved && touchState == 1) { Intent intent = new Intent(FloatingService.this, LddWebViewActivity.class); intent.setFlags(Intent.FLAG_ACTIVITY_NEW_TASK); startActivity(intent); } touchState = 0; break; } return true; } });
最後,我們需要添加彈出菜單的功能:
//獲取菜單按鈕並添加點擊事件監聽器 ImageView menu = floatingView.findViewById(R.id.menuView); menu.setOnClickListener(new View.OnClickListener() { @Override public void onClick(View view) { PopupMenu popupMenu = new PopupMenu(FloatingService.this, view); MenuInflater inflater = popupMenu.getMenuInflater(); inflater.inflate(R.menu.menu_ldd, popupMenu.getMenu()); popupMenu.setOnMenuItemClickListener(new PopupMenu.OnMenuItemClickListener() { @Override public boolean onMenuItemClick(MenuItem item) { switch (item.getItemId()) { case R.id.sections: Intent intent = new Intent(FloatingService.this, LddSectionsActivity.class); intent.setFlags(Intent.FLAG_ACTIVITY_NEW_TASK); startActivity(intent); break; case R.id.tools: Intent intent2 = new Intent(FloatingService.this, LddToolsActivity.class); intent2.setFlags(Intent.FLAG_ACTIVITY_NEW_TASK); startActivity(intent2); break; case R.id.exit: stopSelf(); break; } return true; } }); popupMenu.show(); } });
四、Android懸浮窗貼邊
懸浮窗貼邊可以提高應用使用的體驗效果,以下代碼實現了左、右、上、下四個方向的懸浮窗貼邊:
//獲取屏幕寬高 DisplayMetrics displayMetrics = Resources.getSystem().getDisplayMetrics(); int screenWidth = displayMetrics.widthPixels; int screenHeight = displayMetrics.heightPixels; //設置懸浮窗初始位置 floatLayoutParams.x = 0; floatLayoutParams.y = 0; windowManager.addView(floatingView, floatLayoutParams); //獲取方向按鈕並添加點擊事件監聽器 ImageView leftView = floatingView.findViewById(R.id.leftView); leftView.setOnClickListener(new View.OnClickListener() { @Override public void onClick(View view) { floatLayoutParams.x = 0; windowManager.updateViewLayout(floatingView, floatLayoutParams); } }); ImageView topView = floatingView.findViewById(R.id.topView); topView.setOnClickListener(new View.OnClickListener() { @Override public void onClick(View view) { floatLayoutParams.y = 0; windowManager.updateViewLayout(floatingView, floatLayoutParams); } }); ImageView rightView = floatingView.findViewById(R.id.rightView); rightView.setOnClickListener(new View.OnClickListener() { @Override public void onClick(View view) { floatLayoutParams.x = screenWidth - floatingView.getWidth(); windowManager.updateViewLayout(floatingView, floatLayoutParams); } }); ImageView bottomView = floatingView.findViewById(R.id.bottomView); bottomView.setOnClickListener(new View.OnClickListener() { @Override public void onClick(View view) { floatLayoutParams.y = screenHeight - floatingView.getHeight(); windowManager.updateViewLayout(floatingView, floatLayoutParams); } });
五、Android Studio懸浮窗
Android Studio是當前使用最廣泛的Android應用開發工具,我們可以在它內部實現懸浮窗顯示。以下是一個示例代碼:
//獲取主布局 View content = findViewById(android.R.id.content); //獲取windowManager WindowManager windowManager = (WindowManager) getSystemService(WINDOW_SERVICE); //創建LayoutParams對象 WindowManager.LayoutParams layoutParams = new WindowManager.LayoutParams(); layoutParams.type = WindowManager.LayoutParams.TYPE_APPLICATION_OVERLAY; layoutParams.flags = WindowManager.LayoutParams.FLAG_NOT_TOUCH_MODAL | WindowManager.LayoutParams.FLAG_NOT_FOCUSABLE; layoutParams.width = WindowManager.LayoutParams.WRAP_CONTENT; layoutParams.height = WindowManager.LayoutParams.WRAP_CONTENT; //獲取懸浮窗布局 View floatView = LayoutInflater.from(MainActivity.this).inflate(R.layout.layout_float_window, null); //添加懸浮窗插入到view中並設置位置 windowManager.addView(floatView, layoutParams);
六、Android懸浮窗源碼
以下是Android懸浮窗源碼:
public class FloatWindowService extends Service { private WindowManager.LayoutParams mParams; private WindowManager mWindowManager; private View mFloatView; @Override public void onCreate() { super.onCreate(); //創建一個浮動窗口實例 mFloatView = LayoutInflater.from(this).inflate(R.layout.float_layout, null); mParams = new WindowManager.LayoutParams(); mParams.type = Build.VERSION.SDK_INT >= Build.VERSION_CODES.O ? WindowManager.LayoutParams.TYPE_APPLICATION_OVERLAY : WindowManager.LayoutParams.TYPE_PHONE; mParams.format = PixelFormat.RGBA_8888; mParams.flags = WindowManager.LayoutParams.FLAG_NOT_TOUCH_MODAL | WindowManager.LayoutParams.FLAG_NOT_FOCUSABLE; mParams.gravity = Gravity.LEFT | Gravity.TOP; mParams.width = WindowManager.LayoutParams.WRAP_CONTENT; mParams.height = WindowManager.LayoutParams.WRAP_CONTENT; //添加懸浮窗 mWindowManager = (WindowManager) getSystemService(WINDOW_SERVICE); assert mWindowManager != null; mWindowManager.addView(mFloatView, mParams); } @Nullable @Override public IBinder onBind(Intent intent) { return null; } @Override public void onDestroy() { super.onDestroy(); if (mFloatView != null) { mWindowManager.removeView(mFloatView); } } }
七、Android應用內懸浮窗
應用內懸浮窗適用於一些特殊的應用場景,例如掃描應用、音樂應用等。以下是一個示例代碼:
public class MainActivity extends AppCompatActivity implements View.OnClickListener { private SuspendButtonView suspendButtonView; private WindowManager windowManager; private WindowManager.LayoutParams layoutParams; @Override protected void onCreate(Bundle savedInstanceState) { super.onCreate(savedInstanceState); setContentView(R.layout.activity_main); setUpView(); } private void setUpView() { suspendButtonView = new SuspendButtonView(this); suspendButtonView.setOnClickListener(this); suspendButtonView.setImageResource(R.drawable.ic_launcher); layoutParams = new WindowManager.LayoutParams(); layoutParams.type = WindowManager.LayoutParams.TYPE_APPLICATION; layoutParams.flags = WindowManager.LayoutParams.FLAG_NOT_FOCUSABLE; layoutParams.format = PixelFormat.TRANSLUCENT; layoutParams.gravity = Gravity.LEFT | Gravity.TOP; layoutParams.width = WindowManager.LayoutParams.WRAP_CONTENT; layoutParams.height = WindowManager.LayoutParams.WRAP_CONTENT; windowManager = (WindowManager) getSystemService(WINDOW_SERVICE); windowManager.addView(suspendButtonView, layoutParams); } @Override protected void onDestroy() { super.onDestroy(); if (suspendButtonView != null) { windowManager.removeView(suspendButtonView); } } @Override public void onClick(View view) { switch (view.getId()) { case R.id.suspend_button_view: break; } } }
以上代碼定義了一個SuspendButtonView類,它繼承自ImageView,並實現了添加到懸浮窗的功能。
原創文章,作者:小藍,如若轉載,請註明出處:https://www.506064.com/zh-tw/n/302706.html