一、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
微信掃一掃
支付寶掃一掃