Android悬浮窗实现详解

一、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/n/302706.html

(0)
打赏 微信扫一扫 微信扫一扫 支付宝扫一扫 支付宝扫一扫
小蓝小蓝
上一篇 2024-12-30 18:13
下一篇 2024-12-31 11:48

相关推荐

  • Android ViewPager和ScrollView滑动冲突问题

    Android开发中,ViewPager和ScrollView是两个常用的控件。但是当它们同时使用时,可能会发生滑动冲突的问题。本文将从多个方面介绍解决Android ViewPa…

    编程 2025-04-28
  • Android如何点击其他区域收起软键盘

    在Android应用中,当输入框获取焦点弹出软键盘后,我们希望能够点击其他区域使软键盘消失,以提升用户体验。本篇文章将说明如何实现这一功能。 一、获取焦点并显示软键盘 在Andro…

    编程 2025-04-28
  • Android Studio HUD 实现指南

    本文将会以实例来详细阐述如何在 Android Studio 中使用 HUD 功能实现菊花等待指示器的效果。 一、引入依赖库 首先,我们需要在 build.gradle 文件中引入…

    编程 2025-04-27
  • Android和Vue3混合开发方案

    本文将介绍如何将Android和Vue3结合起来进行混合开发,以及其中的优势和注意事项。 一、环境搭建 在进行混合开发之前,需要搭建好相应的开发环境。首先需要安装 Android …

    编程 2025-04-27
  • Android Java Utils 可以如何提高你的开发效率

    Android Java Utils 是一款提供了一系列方便实用的工具类的 Java 库,可以帮助开发者更加高效地进行 Android 开发,提高开发效率。本文将从以下几个方面对 …

    编程 2025-04-27
  • 神经网络代码详解

    神经网络作为一种人工智能技术,被广泛应用于语音识别、图像识别、自然语言处理等领域。而神经网络的模型编写,离不开代码。本文将从多个方面详细阐述神经网络模型编写的代码技术。 一、神经网…

    编程 2025-04-25
  • Linux sync详解

    一、sync概述 sync是Linux中一个非常重要的命令,它可以将文件系统缓存中的内容,强制写入磁盘中。在执行sync之前,所有的文件系统更新将不会立即写入磁盘,而是先缓存在内存…

    编程 2025-04-25
  • nginx与apache应用开发详解

    一、概述 nginx和apache都是常见的web服务器。nginx是一个高性能的反向代理web服务器,将负载均衡和缓存集成在了一起,可以动静分离。apache是一个可扩展的web…

    编程 2025-04-25
  • Linux修改文件名命令详解

    在Linux系统中,修改文件名是一个很常见的操作。Linux提供了多种方式来修改文件名,这篇文章将介绍Linux修改文件名的详细操作。 一、mv命令 mv命令是Linux下的常用命…

    编程 2025-04-25
  • git config user.name的详解

    一、为什么要使用git config user.name? git是一个非常流行的分布式版本控制系统,很多程序员都会用到它。在使用git commit提交代码时,需要记录commi…

    编程 2025-04-25

发表回复

登录后才能评论