android开发悬浮窗的app

悬浮窗(Floating Window)是一种常见的应用场景,如视频播放器、弹幕、游戏手柄等等。在Android系统上,悬浮窗主要是通过系统级窗口实现的。本文将详细介绍如何使用Android系统提供的API开发一个悬浮窗app。

1. 悬浮窗权限

在Android 6.0及以上版本中,悬浮窗权限成为了敏感权限。开发者需要动态申请悬浮窗权限,否则会抛出异常。申请悬浮窗权限可以通过以下代码实现:

```java

if(Build.VERSION.SDK_INT >= Build.VERSION_CODES.M && !Settings.canDrawOverlays(context)) {

Intent intent = new Intent(Settings.ACTION_MANAGE_OVERLAY_PERMISSION);

intent.setData(Uri.parse("package:" + context.getPackageName()));

context.startActivity(intent);

}

```

需要注意的是,申请悬浮窗权限需要在AndroidManifest.xml文件中添加以下声明:

```xml

```

2. 创建系统级窗口

在Android系统中,系统级窗口是一种特殊的窗口,可以位于所有应用程序的窗口之上。使用系统级窗口实现悬浮窗的步骤如下:

1. 创建WindowManager.LayoutParams

```java

WindowManager.LayoutParams layoutParams = new WindowManager.LayoutParams();

layoutParams.type = WindowManager.LayoutParams.TYPE_APPLICATION_OVERLAY;

layoutParams.format = PixelFormat.TRANSLUCENT;

layoutParams.flags = WindowManager.LayoutParams.FLAG_NOT_FOCUSABLE | WindowManager.LayoutParams.FLAG_LAYOUT_IN_SCREEN;

layoutParams.width = WindowManager.LayoutParams.WRAP_CONTENT;

layoutParams.height = WindowManager.LayoutParams.WRAP_CONTENT;

layoutParams.gravity = Gravity.START | Gravity.TOP;

```

在创建WindowManager.LayoutParams时,需要注意以下几点:

- layoutParams.type必须设置为WindowManager.LayoutParams.TYPE_APPLICATION_OVERLAY,否则无法显示在其他应用程序窗口之上。

- layoutParams.format需要设置为PixelFormat.TRANSLUCENT,以支持悬浮窗的透明度。

- layoutParams.flags必须设置为WindowManager.LayoutParams.FLAG_NOT_FOCUSABLE | WindowManager.LayoutParams.FLAG_LAYOUT_IN_SCREEN,以确保悬浮窗不会抢占焦点,并且可以显示在屏幕之上。

- layoutParams.width和layoutParams.height可以设置为固定值或者WrapContent,可以根据需要设置。

- layoutParams.gravity是悬浮窗在屏幕上的位置,可以设置为Gravity.START | Gravity.TOP等值。

2. 创建悬浮窗View

```java

View floatingView = LayoutInflater.from(context).inflate(R.layout.floating_view, null);

```

在创建悬浮窗View时,需要注意以下几点:

- View的布局需要适配悬浮窗,可以使用LinearLayout、FrameLayout等布局。

- 需要处理View上的交互事件,如点击、长按等。

3. 添加悬浮窗到WindowManager

```java

WindowManager windowManager = (WindowManager) context.getSystemService(Context.WINDOW_SERVICE);

windowManager.addView(floatingView, layoutParams);

```

在添加悬浮窗到WindowManager时,需要注意以下几点:

- 添加悬浮窗需要获取WindowManager对象。

- 添加悬浮窗时需要传入创建的WindowManager.LayoutParams对象。

- 添加悬浮窗后,需要处理悬浮窗的生命周期,如窗口关闭、窗口位置变化等。

3. 悬浮窗生命周期

悬浮窗的生命周期需要与应用程序的生命周期进行对应,以确保悬浮窗的正常运行。悬浮窗的生命周期如下:

1. 显示窗口

```java

public void showFloatingView(Context context) {

if(!isFloatingViewVisible) {

// 显示悬浮窗

createFloatingView(context);

// 设置悬浮窗View的交互事件

setFloatingViewOnClickListener(context);

// 设置悬浮窗View的生命周期回调

setFloatingViewLifecycleCallback(context);

isFloatingViewVisible = true;

}

}

```

2. 关闭窗口

```java

public void hideFloatingView(Context context) {

if(isFloatingViewVisible) {

// 隐藏悬浮窗

WindowManager windowManager = (WindowManager) context.getSystemService(Context.WINDOW_SERVICE);

windowManager.removeViewImmediate(floatingView);

floatingView = null;

isFloatingViewVisible = false;

}

}

```

3. 处理悬浮窗位置变化

```java

private WindowManager.LayoutParams layoutParams;

View.OnTouchListener floatingViewOnTouchListener = new View.OnTouchListener() {

private float lastX, lastY;

private int initialX, initialY;

@Override

public boolean onTouch(View view, MotionEvent event) {

switch (event.getAction()) {

case MotionEvent.ACTION_DOWN:

// 获取悬浮窗View的初始位置

initialX = layoutParams.x;

initialY = layoutParams.y;

lastX = event.getRawX();

lastY = event.getRawY();

return true;

case MotionEvent.ACTION_MOVE:

// 计算悬浮窗View的移动距离

int deltaX = (int) (event.getRawX() - lastX);

int deltaY = (int) (event.getRawY() - lastY);

// 更新悬浮窗View的位置

layoutParams.x = initialX + deltaX;

layoutParams.y = initialY + deltaY;

WindowManager windowManager = (WindowManager) context.getSystemService(Context.WINDOW_SERVICE);

windowManager.updateViewLayout(floatingView, layoutParams);

return true;

}

return false;

}

};

```

4. 处理悬浮窗生命周期回调

```java

private WindowManager.LayoutParams layoutParams;

WindowManager.BadTokenExceptionCallback floatingViewCallback = new WindowManager.BadTokenExceptionCallback() {

@Override

public void onBadTokenException(WindowManager parent, RuntimeException e) {

parent.removeView(floatingView);

hideFloatingView(context);

}

};

ViewTreeObserver.OnGlobalLayoutListener floatingViewLayoutListener = new ViewTreeObserver.OnGlobalLayoutListener() {

@Override

public void onGlobalLayout() {

if(floatingView == null) {

return;

}

layoutParams.width = floatingView.getWidth();

layoutParams.height = floatingView.getHeight();

WindowManager windowManager = (WindowManager) context.getSystemService(Context.WINDOW_SERVICE);

windowManager.updateViewLayout(floatingView, layoutParams);

}

};

```

需要注意的是,处理悬浮窗的生命周期时,需要获取WindowManager.LayoutParams对象以及悬浮窗View对象,以确保能够正确地处理悬浮窗。

4. 总结

开发一个Android悬浮窗应用需要掌握系统级窗口的使用方法,了解悬浮窗的生命周期以及处理交互事件。通过此文的介绍,相信读者已经对Android悬浮窗开发有了深入了解,并且可以通过系统提供的API实现一个悬浮窗应用。

川公网安备 51019002001185号