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
,并实现了添加到悬浮窗的功能。