android windowmanger实现桌面悬浮窗功能-kb88凯时官网登录

来自:网络
时间:2023-07-25
阅读:
目录

如果想实现一个在桌面显示的悬浮窗,用dialogpopupwindowtoast等已经不能实现了,他们基本都是在activity之上显示的,如果想实现在桌面显示的悬浮窗效果,需要用到windowmanager来实现了。

效果图

android windowmanger实现桌面悬浮窗功能

使用windowmanager实现

  • 添加一个悬浮窗:
        sys_view = new smallwindowview(mcontext);
        sys_view.settext("50%");
        sys_view.setontouchlistener(this);
        windowmanager = (windowmanager) mcontext.getsystemservice(context.window_service);
        int screenwidth = 0, screenheight = 0;
        if (windowmanager != null) {
            //获取屏幕的宽和高
            point point = new point();
            windowmanager.getdefaultdisplay().getsize(point);
            screenwidth = point.x;
            screenheight = point.y;
            layoutparams = new windowmanager.layoutparams();
//            layoutparams.width = windowmanager.layoutparams.wrap_content;
//            layoutparams.height = windowmanager.layoutparams.wrap_content;
            layoutparams.width = 200;
            layoutparams.height = 200;
            //设置type
            if (build.version.sdk_int >= build.version_codes.o) {
                //26及以上必须使用type_application_overlay   @deprecated type_phone
                layoutparams.type = windowmanager.layoutparams.type_application_overlay;
            } else {
                layoutparams.type = windowmanager.layoutparams.type_phone;
            }
            //设置flags
            layoutparams.flags = windowmanager.layoutparams.flag_not_touch_modal
                    | windowmanager.layoutparams.flag_not_focusable | windowmanager.layoutparams.flag_show_when_locked;
            layoutparams.gravity = gravity.start | gravity.top;
            //背景设置成透明
            layoutparams.format = pixelformat.transparent;
            layoutparams.x = screenwidth;
            layoutparams.y = screenheight / 2;
            //将view添加到屏幕上
            windowmanager.addview(sys_view, layoutparams);
        }
  • 更新悬浮窗位置:
windowmanager.updateviewlayout(sys_view, layoutparams);
  • 关闭悬浮窗:
windowmanager.removeview(sys_view);

通过上面的代码就可以实现一个桌面悬浮窗功能了。

注意:在6.0以上,需要在manifest.xml中声明 权限并且在开启悬浮窗时动态判断权限,如果没有此权限需要跳到设置页面去设置,看下官方文档的说明:

分析

1、添加悬浮窗: 通过context.getsystemservice(context.window_service)获得一个windowmanager(以下简称vm), vm是外界访问window的入口,activitydialogtoast等其视图都是依附在window之上的,windowview的直接管理者,vm继承自viewmanager,其添加、刷新、删除方法也是来自viewmanager:

public interface viewmanager
{   public void addview(view view, viewgroup.layoutparams params);
    public void updateviewlayout(view view, viewgroup.layoutparams params);
    public void removeview(view view);
}

vm有一个静态内部类windowmanager.layoutparamswindow的各个属性在这个内部类中设置:

  • layoutparams.type 如果targetsdkversion<26 ,那么可以直接使用layoutparams.type_phone或者layoutparams.type_system_alert,在targetsdkversion>=26时,type_phonetype_system_alert都已经废弃了,需要使用type_application_overlay来标识type
  • layoutparams.flags flags表示window的属性,通过flags可以控制window的显示特性,常用的几个特性: layoutparams.flag_not_touch_modal : 使用了此标识,可以将点击事件传递到悬浮窗以外的区域,反之其他区域的window将接收不到事件。 layoutparams.flag_not_focusable : 表示悬浮窗window不需要获取焦点,也不需要获取各种输入事件,事件会直接传递给下层的具有焦点的window layoutparams.flag_show_when_locked : 此模式可以让window显示在锁屏的界面上
  • layoutparams.format 悬浮窗window的背景格式,一般设置成pixelformat.transparent透明即可
  • layoutparams.x & layoutparams.y 悬浮窗window在屏幕上的坐标值,可以根据x&y的值来刷新window在屏幕上的位置
  • layoutparams.width & layoutparams.height 悬浮窗window的宽度和高度

2、更新悬浮窗位置: 在viewontouchevent中或ontouch中更新layoutparams.xlayoutparams.y的值并通过windowmanager.updateviewlayout()重新设置悬浮窗window在屏幕中的位置,如下:

    @override
    public boolean ontouch(view v, motionevent event) {
        int minscreenx = (int) event.getrawx();
        int minscreeny = (int) event.getrawy();
        switch (event.getaction()) {
            case motionevent.action_down:
                mlastx = (int) event.getrawx();
                mlasty = (int) event.getrawy();
                break;
            case motionevent.action_move:
                layoutparams.x  = minscreenx - mlastx;
                layoutparams.y  = minscreeny - mlasty;
                mlastx = minscreenx;
                mlasty = minscreeny;
                windowmanager.updateviewlayout(sys_view, layoutparams);
                break;
            case motionevent.action_up:
                break;
        }
        return true;
    }

3、删除悬浮窗: 删除比较简单,直接调用windowmanager.removeview(view)viewwindow中删除即可。

问题

6.0以上使用时,需要动态申请该悬浮窗权限,如下:

//判断有没有悬浮窗权限,没有去申请
if(!settings.candrawoverlays(context)){
     intent intent = new intent(settings.action_manage_overlay_permission,
                uri.parse("package:"   context.getpackagename()));
     context.startactivityforresult(intent, request_code);
}
    @override
    protected void onactivityresult(int requestcode, int resultcode, intent data) {
        switch (requestcode) {
            case request_code:
                if (build.version.sdk_int < build.version_codes.m) return;
                if (!windowutil.canoverdraw(this)) {
                    toast("悬浮窗权限未开启,请在设置中手动打开");
                    return;
                }
                windowcontroller.getinstance().showthumbwindow();
                break;
        }
    }

通过settings.candrawoverlays(context)判断是否有悬浮窗权限,如果没有,跳转到设置页面去设置,并在onactivityresult ()中得到申请结果,看似很完美,但在实际测试中,发现在8.0以上的手机上有问题,即使在设置中同意了权限,8.0的手机settings.candrawoverlays(context)总是返回false,不过在关闭页面重新调用此方法时,又返回的true,感觉是有一定的延迟,google了一下,发现别人同样遇到了这个问题,貌似已经给google提交了bug单,可以看此博客: http://paskov.vmsoft-bg.com/settings-candrawoverlays-allays-returns-false-on-android-o/ ,不过博客中的解决方法用我的8.0手机(huawei mate10)依然不起作用,暂时还没深入研究,有解决此问题的还希望不吝赐教。

以上例子的源码地址:https://github.com/crazyqiang

参考

【1】

返回顶部
顶部
网站地图