在 Android 中实现浮窗(悬浮窗)并添加吸边效果

在 Android 中实现浮窗(悬浮窗)并添加吸边效果,可以使用 WindowManager 来管理浮窗视图,并通过触摸事件来实现吸边效果。以下是一个示例,展示如何创建一个浮窗并实现吸边效果。

1. 添加权限

首先,确保您的应用具有显示悬浮窗的权限。在 AndroidManifest.xml 文件中添加以下权限:

ini 复制代码
<uses-permission android:name="android.permission.SYSTEM_ALERT_WINDOW"/>

2. 请求悬浮窗权限

在 Android 6.0(API 级别 23)及更高版本中,您需要在运行时请求悬浮窗权限:

java 复制代码
import android.content.Intent;

import android.net.Uri;

import android.os.Build;

import android.os.Bundle;

import android.provider.Settings;

import androidx.annotation.Nullable;

import androidx.appcompat.app.AppCompatActivity;

public class MainActivity extends AppCompatActivity {

    private static final int REQUEST_CODE_OVERLAY_PERMISSION = 1001;

    @Override

    protected void onCreate(@Nullable Bundle savedInstanceState) {

        super.onCreate(savedInstanceState);

        if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.M) {

            if (!Settings.canDrawOverlays(this)) {

                Intent intent = new Intent(Settings.ACTION_MANAGE_OVERLAY_PERMISSION,

                        Uri.parse("package:" + getPackageName()));

                startActivityForResult(intent, REQUEST_CODE_OVERLAY_PERMISSION);

            } else {

                // 已获得权限,显示浮窗

                showFloatingWindow();

            }

        } else {

            // 低于 Android 6.0,直接显示浮窗

            showFloatingWindow();

        }

    }

    @Override

    protected void onActivityResult(int requestCode, int resultCode, @Nullable Intent data) {

        super.onActivityResult(requestCode, resultCode, data);

        if (requestCode == REQUEST_CODE_OVERLAY_PERMISSION) {

            if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.M) {

                if (Settings.canDrawOverlays(this)) {

                    // 已获得权限,显示浮窗

                    showFloatingWindow();

                }

            }

        }

    }

    private void showFloatingWindow() {

        // 显示浮窗的代码

    }

}

3. 创建浮窗并实现吸边效果

以下是一个完整的示例,展示如何创建一个浮窗并实现吸边效果:

java 复制代码
import android.content.Context;

import android.graphics.PixelFormat;

import android.os.Build;

import android.os.Bundle;

import android.view.Gravity;

import android.view.MotionEvent;

import android.view.View;

import android.view.WindowManager;

import android.widget.ImageView;

import androidx.annotation.Nullable;

import androidx.appcompat.app.AppCompatActivity;

public class MainActivity extends AppCompatActivity {

    private WindowManager windowManager;

    private WindowManager.LayoutParams layoutParams;

    private ImageView floatingView;

    @Override

    protected void onCreate(@Nullable Bundle savedInstanceState) {

        super.onCreate(savedInstanceState);

        if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.M) {

            if (!Settings.canDrawOverlays(this)) {

                Intent intent = new Intent(Settings.ACTION_MANAGE_OVERLAY_PERMISSION,

                        Uri.parse("package:" + getPackageName()));

                startActivityForResult(intent, 1001);

            } else {

                showFloatingWindow();

            }

        } else {

            showFloatingWindow();

        }

    }

    @Override

    protected void onActivityResult(int requestCode, int resultCode, @Nullable Intent data) {

        super.onActivityResult(requestCode, resultCode, data);

        if (requestCode == 1001) {

            if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.M) {

                if (Settings.canDrawOverlays(this)) {

                    showFloatingWindow();

                }

            }

        }

    }

    private void showFloatingWindow() {

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

        floatingView = new ImageView(this);

        floatingView.setImageResource(R.drawable.ic_launcher_foreground); // 替换为您的图标

        layoutParams = 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,

                PixelFormat.TRANSLUCENT

        );

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

        layoutParams.x = 0;

        layoutParams.y = 100;

        windowManager.addView(floatingView, layoutParams);

        floatingView.setOnTouchListener(new View.OnTouchListener() {

            private int initialX;

            private int initialY;

            private float initialTouchX;

            private float initialTouchY;

            @Override

            public boolean onTouch(View v, MotionEvent event) {

                switch (event.getAction()) {

                    case MotionEvent.ACTION_DOWN:

                        initialX = layoutParams.x;

                        initialY = layoutParams.y;

                        initialTouchX = event.getRawX();

                        initialTouchY = event.getRawY();

                        return true;

                    case MotionEvent.ACTION_MOVE:

                        layoutParams.x = initialX + (int) (event.getRawX() - initialTouchX);

                        layoutParams.y = initialY + (int) (event.getRawY() - initialTouchY);

                        windowManager.updateViewLayout(floatingView, layoutParams);

                        return true;

                    case MotionEvent.ACTION_UP:

                        int screenWidth = getResources().getDisplayMetrics().widthPixels;

                        if (layoutParams.x < screenWidth / 2) {

                            layoutParams.x = 0;

                        } else {

                            layoutParams.x = screenWidth;

                        }

                        windowManager.updateViewLayout(floatingView, layoutParams);

                        return true;

                }

                return false;

            }

        });

    }

    @Override

    protected void onDestroy() {

        super.onDestroy();

        if (floatingView != null) {

            windowManager.removeView(floatingView);

        }

    }

}

解释

  1. 请求悬浮窗权限 :在 onCreate 方法中检查并请求悬浮窗权限。
  2. 创建浮窗 :使用 WindowManager 创建浮窗,并设置 WindowManager.LayoutParams
  3. 实现吸边效果 :通过 setOnTouchListener 方法监听浮窗的触摸事件,在 ACTION_UP 事件中实现吸边效果。

注意事项

  • 权限管理:确保在 Android 6.0 及更高版本中正确请求悬浮窗权限。
  • 悬浮窗类型 :在 Android 8.0 及更高版本中,使用 WindowManager.LayoutParams.TYPE_APPLICATION_OVERLAY 类型。
  • 资源管理 :在 onDestroy 方法中移除浮窗视图,避免内存泄漏。
相关推荐
Grackers27 分钟前
Android Perfetto 系列 5:Android App 基于 Choreographer 的渲染流程
android
踩着两条虫28 分钟前
AI驱动的Vue3应用开发平台深入探究(十):物料系统之内置组件库
android·前端·vue.js·人工智能·低代码·系统架构·rxjava
sam.li33 分钟前
JADX MCP 原理与使用部署
android·逆向·jadx
冬奇Lab1 小时前
Android 15音频子系统(五):AudioPolicyService策略管理深度解析
android·音视频开发·源码阅读
亚历克斯神1 小时前
Flutter for OpenHarmony: Flutter 三方库 mutex 为鸿蒙异步任务提供可靠的临界资源互斥锁(并发安全基石)
android·数据库·安全·flutter·华为·harmonyos
dalancon3 小时前
SurfaceControl 的事务提交给 SurfaceFlinger,以及 SurfaceFlinger 如何将这些数据设置到对应 Layer 的完整流程
android
dalancon3 小时前
SurfaceFlinger Layer 到 HWC 通信流程详解
android
cccccc语言我来了3 小时前
Linux(9)操作系统
android·java·linux
yige454 小时前
【MySQL】MySQL内置函数--日期函数字符串函数数学函数其他相关函数
android·mysql·adb
洞见前行4 小时前
AI 当逆向工程师:Claude Code 自主分析 APK 和 so 文件,解决 Unity 插件化启动崩溃
android·人工智能