悬浮窗口的实现主要是用windowManager来实现的,为了简单前面的基础部分就copy过来:http://www.cnblogs.com/mythou/p/3244208.html

1、WindowManager介绍

  全部Android的窗口机制是基于一个叫做WindowManager实现,这个接口可以添加view到屏幕,也可以从屏幕删除view。它面向的对象一端是屏幕,另一端就是View,直接忽视我们以前的Activity或者Dialog之类的元素。其实我们的Activity或者Diolog底层的实现也是经过WindowManager,WindowManager是全局的,整个系统只有一个WindowManager。它是显示View的最底层了。,里面涉及到窗口管理的三个重要方法,分别是

  • addView(); 
  • updateViewLayout();
  • removeView();

  在WindowManager中还有一个重要的静态类LayoutParams。通过它可以设置和获得当前窗口的一些属性。我们先来看看addView()方法,在addView中,会利用LayoutParams获得window的View属性,并为每个window创ViewRoot,ViewRoot是View和WindowManager之间的桥梁,真正把View传递给WindowManager的是通过ViewRoot的setView()方法,ViewRoot实现了View和WindowManager之间的消息传递。在将主窗口添加到WindowManger时,它首先会建立一个代理对象:

wm=(WindowManagerImpl)context.getSystemService(Context.WINDOW_SERVICE)

并且打开会话(IWindowSession),之后Window将通过该会话与WindowManager建立联系。

2、关于android 6.0前后的授权问题。

可参考下:http://blog.csdn.net/self_study/article/details/50186435

本文主要是给一个服务添加悬浮窗口,首先判断是否需要权限:

1、6.0前清单文件添加:<uses-permission android:name="android.permission.SYSTEM_ALERT_WINDOW" />

2、6.0后除清单文件添加上面权限外还得手动为应用添加权限:

  (1)在设置->应用->点开应用->配置应用->在其他应用的上层显示 授予权限

  (2)在代码中判断是否大于6.0版本,并判断是否有权限,若没有就跳到授权设置里。

   (3)、将WindowManager 的类型设置为: params.type = WindowManager.LayoutParams.TYPE_TOAST;则不需要申请权限

3、关于悬浮窗口的设置和实现以及一些属性设置

  (1)、悬浮窗口在某一个activity里显示。

 之前做过一个VR相册的项目,要求点击图片时候显示一张图片,为了不创建新的activity和布局文件,只用一个自定义空间来显示,采用了悬浮窗口,

    relativeLayout  =new  RelativeLayout(this);
    WindowManager.LayoutParams params = new WindowManager.LayoutParams();
        params.width = WindowManager.LayoutParams.MATCH_PARENT;
        params.height = WindowManager.LayoutParams.MATCH_PARENT;
        params.format = PixelFormat.RGBA_8888;//背景透明
        params.gravity = Gravity.LEFT | Gravity.TOP;
        relativeLayout.addView(myBallPictureView);
        wm.addView(relativeLayout, params);    

然而发现了悬浮窗口的2个坑:

    <1> 、在addview()的时候报错:android.view.WindowManager$BadTokenException: Unable to add window -- token null is not for an application,根据报错的原因,是窗口的令牌错误,然后查看我的windowManager的初始化为: 

          wm = (WindowManager)getApplicationContext().getSystemService(Context.WINDOW_SERVICE);

    既然是令牌错误,就发现可能是初始化wm时候发生了错误,后来通过activity和getApplicationContext()的contex是不一样的,一个是整个应用的,一个是此activity的,后来换作activity来获取WM,发现不再报错,且显示很流畅。getApplicationContext()除非添加了

params.type = WindowManager.LayoutParams.TYPE_TOAST;后才不报错,但会一直显示在屏幕上。。其实这里提醒我们能使用activity的context尽量别使用getApplicationContext(),还是有区别的(虽然大部分都时候获取的token其实是一样的)。

    <2>、发现弹出了图像后按back键图像消失不了,为什么消失不了?通过log发现,当我按onBackPress()的时候发现,我覆写的onBackPress()方法更本没有执行,就联想到可能是WindowManager.LayoutParams 的Type属性问题了。后来将Type修改为:

          params.flags= WindowManager.LayoutParams.FLAG_NOT_FOCUSABLE;

    发现可以了。

这里也使得我们在使用悬浮窗口的时候主要的事项:WindowManager.LayoutParams的属性,和context的获取。下面介绍一下WindowManager.LayoutParams的Type和flag2个重要的属性:

  Type是添加窗口的类型:窗口以系统的什么类型的窗口显示:可参考:http://realgodo.iteye.com/blog/1780176。用的较多的就是第二部分提到的TYPE_TOAST。此时会显示在屏幕上,

  flag窗口的焦点属性:   

      WindowManager.LayoutParams.FLAG_NOT_FOCUSABLE ;此时不会固定在屏幕上,按返回键是有效的。

      WindowManager.LayoutParams.FLAG_ALT_FOCUSABLE_IM; 此时窗口固定在屏幕上,返回键是无效的。

 (2) 悬浮窗口在所有屏幕上显示

  这其实上面也提到了,需要设置TYPE_TOAST,或者TYPE_PRIORITY_PHONE    具体参看上面的关于Type的博客。

 (3),应用内显示悬浮窗口

  这里为了方便管理,通常是设置一个service来实现,当应用前台时候调用服务显示窗口,当应用后台时候调用服务显示窗口。

(4)、桌面显示,应用内不现实:这个和上面相反,也可以用服务来实现。

  由于时间紧,直接给出(3)代码,其中有注解:

  Activity:

package com.example.user.floatingview;
import android.content.Intent;
import android.net.Uri;
import android.os.Build;
import android.provider.Settings;
import android.support.v7.app.AppCompatActivity;
import android.os.Bundle;
import android.view.View;
import android.widget.Button;
import android.widget.Toast;

public class MainActivity extends AppCompatActivity implements
        View.OnClickListener {
    private Button btn_show;
    private Button btn_hide;
    private  int floatShow = 0;
    private boolean hasGetPermission = false;
    @Override
    protected void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        setContentView(R.layout.activity_main);
        btn_show = (Button) findViewById(R.id.btn_show);
        btn_hide = (Button) findViewById(R.id.btn_hide);
        btn_show.setOnClickListener(this);
        btn_hide.setOnClickListener(this);
    }
    public void onClick(View v)
    {
        switch (v.getId())
        {
            case R.id.btn_show:
                if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.M){//判断api版本,若低于6.0就不需要动态授权检测权限,将canDrawOverlays版本disable掉
                    hasGetPermission =Settings.canDrawOverlays(this);
                if (!hasGetPermission) {
                    Toast.makeText(this, "当前无权限,请授权!", Toast.LENGTH_SHORT).show();
                    Intent intent = new Intent(Settings.ACTION_MANAGE_OVERLAY_PERMISSION,
                            Uri.parse("package:" + getPackageName()));
                    startActivityForResult(intent, 1024);
                }
                    else {
                    floatShow = 1;
                    Toast.makeText(this, "后台显示浮动窗口", Toast.LENGTH_SHORT).show();
                }
            }
                break;
            case R.id.btn_hide:
                floatShow = -1;
                Toast.makeText(this,"已取消后台显示浮动窗口",Toast.LENGTH_SHORT).show();
                break;
        }
    }

    @Override
    protected void onPause()
    {
        super.onPause();
        if(hasGetPermission) {
            if (floatShow == 1) {
                Intent show = new Intent(this, TopWindowService.class);
                show.putExtra(TopWindowService.OPERATION,
                        TopWindowService.OPERATION_SHOW);
                startService(show);
            } else if (floatShow == -1) {
                Intent hide = new Intent(this, TopWindowService.class);
                hide.putExtra(TopWindowService.OPERATION,
                        TopWindowService.OPERATION_HIDE);
                startService(hide);
            }
        }
    }

    @Override
    protected void onResume() {
        super.onResume();
        if(hasGetPermission&&floatShow == 1) {
            Intent hide = new Intent(this, TopWindowService.class);
            hide.putExtra(TopWindowService.OPERATION,
                    TopWindowService.OPERATION_HIDE);
            startService(hide);
        }
    }

    protected void onActivityResult(int requestCode, int resultCode, Intent data) {
        if (requestCode == 1024&&(Build.VERSION.SDK_INT >= Build.VERSION_CODES.KITKAT)){
            if(!Settings.canDrawOverlays(this)) {
                Toast.makeText(this, "权限授予失败,无法开启悬浮窗", Toast.LENGTH_SHORT).show();
                hasGetPermission = false;
            } else {
                Toast.makeText(this, "权限授予成功!", Toast.LENGTH_SHORT).show();
                floatShow = 1;
            }

        }
    }



}
View Code

相关文章:

  • 2021-08-03
  • 2022-12-23
  • 2022-12-23
  • 2022-12-23
  • 2021-06-15
  • 2021-06-11
  • 2022-03-07
猜你喜欢
  • 2022-12-23
  • 2021-10-20
  • 2022-12-23
  • 2021-10-27
  • 2021-12-12
  • 2021-07-05
  • 2022-12-23
相关资源
相似解决方案