Android 项目必备(四十四)-->Android 实现悬浮窗

这篇具有很好参考价值的文章主要介绍了Android 项目必备(四十四)-->Android 实现悬浮窗。希望对大家有所帮助。如果存在错误或未考虑完全的地方,请大家不吝赐教,您也可以点击"举报违法"按钮提交疑问。

前言

悬浮窗是一种比较常见的需求。例如把视频通话界面缩小成一个悬浮窗,然后用户可以在其他界面上处理事情。

本文将讲解悬浮窗实现步骤、原理、实例代码等

实现原理

1. WindowMananger 接口

Android 的界面绘制,都是通过 WindowMananger 的服务来实现的。那么,既然要实现一个能够在自身应用以外的界面上的悬浮窗,我们就要利用 WindowManager 来实现。

(frameworks/base/core/java/android/view/WindowMananger.java)

@SystemService(Context.WINDOW_SERVICE)
public interface WindowManager extends ViewManager {
	...
}

WindowManager 实现了 ViewManager 接口,可以通过获取 WINDOW_SERVICE 系统服务得到。而ViewManager 接口有 addView 方法,我们就是通过这个方法将悬浮窗控件加入到屏幕中去。

2. LayoutParam 设置

这里需要着重说明的是 LayoutParam 里的 type 变量。这个变量是用来指定窗口类型的。

if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.O) {
	layoutParams.type = WindowManager.LayoutParams.TYPE_APPLICATION_OVERLAY;
} else {
    layoutParams.type = WindowManager.LayoutParams.TYPE_PHONE;
}

具体实现

实现一个自动轮播图的悬浮窗。

1. 效果图

android悬浮窗实现,Android -- 项目必备,android,java,开发语言

2. 声明及申请权限

<uses-permission android:name="android.permission.SYSTEM_ALERT_WINDOW" />
@RequiresApi(api = Build.VERSION_CODES.M)
public class MainActivity extends AppCompatActivity {

    @Override
    protected void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        setContentView(R.layout.activity_main);
    }

    @Override
    protected void onActivityResult(int requestCode, int resultCode, Intent data) {
        super.onActivityResult(requestCode, resultCode, data);
        if (requestCode == 1) {
            if (!Settings.canDrawOverlays(this)) {
                Toast.makeText(this, "授权失败", Toast.LENGTH_SHORT).show();
            } else {
                Toast.makeText(this, "授权成功", Toast.LENGTH_SHORT).show();
                startService(new Intent(MainActivity.this, FloatingImageDisplayService.class));
            }
        } 
    }
    
    public void startFloatingImageDisplayService(View view) {
        if (FloatingImageDisplayService.isStarted) {
            return;
        }
        if (!Settings.canDrawOverlays(this)) {
            Toast.makeText(this, "当前无权限,请授权", Toast.LENGTH_SHORT);
            startActivityForResult(new Intent(Settings.ACTION_MANAGE_OVERLAY_PERMISSION, Uri.parse("package:" + getPackageName())), 1);
        } else {
            startService(new Intent(MainActivity.this, FloatingImageDisplayService.class));
        }
    }
}

3. 构建悬浮窗需要的控件

image_display.xml文章来源地址https://www.toymoban.com/news/detail-525236.html

<?xml version="1.0" encoding="utf-8"?>
<LinearLayout
    xmlns:android="http://schemas.android.com/apk/res/android"
    android:layout_width="match_parent"
    android:layout_height="match_parent"
    android:orientation="vertical" >

    <ImageView
        android:id="@+id/image_display_imageview"
        android:layout_width="wrap_content"
        android:layout_height="wrap_content" />

</LinearLayout>

4. 将控件添加到WindowManager

@RequiresApi(api = Build.VERSION_CODES.M)
public class FloatingImageDisplayService extends Service {
    public static boolean isStarted = false;

    private WindowManager windowManager;
    private WindowManager.LayoutParams layoutParams;

    private View displayView;

    private int[] images;
    private int imageIndex = 0;

    private Handler changeImageHandler;

    @Override
    public void onCreate() {
        super.onCreate();
        isStarted = true;
        windowManager = (WindowManager) getSystemService(WINDOW_SERVICE);
        layoutParams = new WindowManager.LayoutParams();
        if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.O) {
            layoutParams.type = WindowManager.LayoutParams.TYPE_APPLICATION_OVERLAY;
        } else {
            layoutParams.type = WindowManager.LayoutParams.TYPE_PHONE;
        }
        layoutParams.format = PixelFormat.RGBA_8888;
        layoutParams.gravity = Gravity.LEFT | Gravity.TOP;
        layoutParams.flags = WindowManager.LayoutParams.FLAG_NOT_TOUCH_MODAL | WindowManager.LayoutParams.FLAG_NOT_FOCUSABLE;
        layoutParams.width = 500;
        layoutParams.height = 500;
        layoutParams.x = 300;
        layoutParams.y = 300;

        images = new int[] {
                R.mipmap.image_01,
                R.mipmap.image_02,
                R.mipmap.image_03,
                R.mipmap.image_04,
                R.mipmap.image_05
        };

        changeImageHandler = new Handler(this.getMainLooper(), changeImageCallback);
    }



    @Override
    public IBinder onBind(Intent intent) {
        return null;
    }

    @Override
    public int onStartCommand(Intent intent, int flags, int startId) {
        showFloatingWindow();
        return super.onStartCommand(intent, flags, startId);
    }


    private void showFloatingWindow() {
        if (Settings.canDrawOverlays(this)) {
            LayoutInflater layoutInflater = LayoutInflater.from(this);
            displayView = layoutInflater.inflate(R.layout.image_display, null);
            displayView.setOnTouchListener(new FloatingOnTouchListener());
            ImageView imageView = displayView.findViewById(R.id.image_display_imageview);
            imageView.setImageResource(images[imageIndex]);
            windowManager.addView(displayView, layoutParams);

            changeImageHandler.sendEmptyMessageDelayed(0, 2000);
        }
    }

    private Handler.Callback changeImageCallback = new Handler.Callback() {
        @Override
        public boolean handleMessage(Message msg) {
            if (msg.what == 0) {
                imageIndex++;
                if (imageIndex >= 5) {
                    imageIndex = 0;
                }
                if (displayView != null) {
                    ((ImageView) displayView.findViewById(R.id.image_display_imageview)).setImageResource(images[imageIndex]);
                }

                changeImageHandler.sendEmptyMessageDelayed(0, 2000);
            }
            return false;
        }
    };

    private class FloatingOnTouchListener implements View.OnTouchListener {
        private int x;
        private int y;

        @Override
        public boolean onTouch(View view, MotionEvent event) {
            switch (event.getAction()) {
                case MotionEvent.ACTION_DOWN:
                    x = (int) event.getRawX();
                    y = (int) event.getRawY();
                    break;
                case MotionEvent.ACTION_MOVE:
                    int nowX = (int) event.getRawX();
                    int nowY = (int) event.getRawY();
                    int movedX = nowX - x;
                    int movedY = nowY - y;
                    x = nowX;
                    y = nowY;
                    layoutParams.x = layoutParams.x + movedX;
                    layoutParams.y = layoutParams.y + movedY;
                    windowManager.updateViewLayout(view, layoutParams);
                    break;
                default:
                    break;
            }
            return false;
        }
    }
}

到了这里,关于Android 项目必备(四十四)-->Android 实现悬浮窗的文章就介绍完了。如果您还想了解更多内容,请在右上角搜索TOY模板网以前的文章或继续浏览下面的相关文章,希望大家以后多多支持TOY模板网!

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处: 如若内容造成侵权/违法违规/事实不符,请点击违法举报进行投诉反馈,一经查实,立即删除!

领支付宝红包 赞助服务器费用

相关文章

  • Android - 使用GSY实现视频播放和画中画悬浮窗

    0. 现完成功能: 悬浮窗区分横屏竖屏两种尺寸 悬浮窗可以在页面上随意拖动 在播放视频时按返回键/Home键/离开当前页时触发开启 悬浮窗显示在退到后台/在应用内/桌面 带播放进度开启悬浮窗,带播放进度回到应用内页面 权限:每次开启前判断有无权限,没权限并且请求过

    2023年04月11日
    浏览(27)
  • 【Android从零单排系列四十八】《Android中自定义activity的实现方法》

    目录 前言 一  activity介绍 二  activity的缺点 三  自定义activity的步骤 四 自定义activity的demo 小伙伴们,在前面的文章中,我们谈到了Android开发中的自定义view的基本概念及方法等,本文我们实际举例自定义一个activity。 Activity(活动)是Android应用程序中的核心组件之一,它代

    2024年02月15日
    浏览(31)
  • 【Android从零单排系列四十九】《Android中自定义Fragment的实现方法》

    目录 前言 一  Fragment介绍 二  Fragment的缺点 三  自定义Fragment的步骤 四 自定义Fragment的demo 小伙伴们,在前面的文章中,我们谈到了Android开发中的自定义view的基本概念及方法等,本文我们实际举例自定义一个Fragment。 Fragment是Android中一种用于构建灵活和可重用界面组件的基

    2024年02月13日
    浏览(30)
  • Android 开发必备知识点及面试题汇总(Android+Java+算法+性能优化+四大组件……),腾讯安卓开发面试

    5.请介绍下 AsyncTask的内部实现,适用的场景是 AsyncTask 内部也是 Handler 机制来完成的,只不过 Android 提供了执行框架来提供线程池来 执行相应地任务,因为线程池的大小问题,所以 AsyncTask 只应该用来执行耗时时间较短的任务, 比如 HTTP 请求,大规模的下载和数据库的更改不

    2024年04月15日
    浏览(44)
  • android 悬浮窗 模拟微信通话返回桌面悬浮

    现有一款IM聊天需求,在通话页面点击缩小视图或者Home键返回桌面,点击悬浮窗回到通话页面这样一个需求。 权限 首先是权限的获取,请注意,在Android 8.0及以上版本中,需要申请悬浮窗权限(SYSTEM_ALERT_WINDOW)才能显示悬浮窗。你可以在应用启动时请求该权限,或者引导用

    2024年02月01日
    浏览(24)
  • Android NestedScrollView悬浮固定顶部

    项目中有页面涉及到多个元素组 需要NestedScrollView包裹来上下滑动 接到需求 一些标题在滑动到顶部时需要置顶 我之前做过关于Android Behavior之ViewPager+Fragment+RecyclerView实现吸顶效果 大概就是这种效果 只不过这次是随意的组件 比如 TextView 或布局组件 RelativeLayout 等 废话不多说来

    2024年01月24日
    浏览(31)
  • Android 应用弹出悬浮窗

    Android开发者经常遇到应用想弹出悬浮窗的操作,而且有可能还想要高层级弹窗,就像ipone的浮标touch一样。android当然也有类似的悬浮图标,比如前些年我们的流量监控提醒。  这里我们忽略UI美学,简单记录一下: 1、基本使用 它的基本使用步骤是不会变的,只是有时候我们

    2024年02月16日
    浏览(26)
  • 【从零开始学习JAVA | 第四十四篇】TCP协议中的握手与挥手

    TCP(传输控制协议)作为计算机网络中的重要协议,扮演着确保数据可靠传输的角色。在TCP的通信过程中,握手与挥手问题是不可忽视的关键环节。握手是指在建立连接时,客户端与服务器相互确认彼此的身份并同步参数,确保双方准备就绪;而挥手则是在终止连接时,双方

    2024年02月11日
    浏览(32)
  • Android 点击悬浮窗后台启动Activity问题及方案

    背景:开启悬浮窗,当app进入后台后,点击悬浮窗进入固定页面 问题:当app在后台运行时,点击悬浮窗,以下代码不能拉起app,经排查,部门手机需要开启后台弹出界面权限 如图  不同的机型这个权限的名称也不相同,要开启此权限,要跳转的界面也不同,所以需要获取不同机型的此页面路

    2024年02月11日
    浏览(33)
  • android 12后WindowManager事件穿透类型悬浮窗无效问题

    笔记: 项目需要将一个悬浮窗WindowManager仅显示view给用户看,不可操作,将触摸事件穿透到后面的窗口。WindowManager.LayoutParams代码如下 在android12以下的机型中测试都没问题,触摸事件可以穿透悬浮窗。但在android 12机型测试时,触摸事件无法穿透悬浮窗了。 查了android开发者官

    2024年02月13日
    浏览(41)

觉得文章有用就打赏一下文章作者

支付宝扫一扫打赏

博客赞助

微信扫一扫打赏

请作者喝杯咖啡吧~博客赞助

支付宝扫一扫领取红包,优惠每天领

二维码1

领取红包

二维码2

领红包