zoukankan      html  css  js  c++  java
  • Android悬浮窗实现 使用WindowManager

    Android悬浮窗实现 使用WindowManager

    WindowManager介绍

      通过Context.getSystemService(Context.WINDOW_SERVICE)可以获得 WindowManager对象。

      每一个WindowManager对象都和一个特定的 Display绑定。

      想要获取一个不同的display的WindowManager,可以用 createDisplayContext(Display)来获取那个display的 Context,之后再使用:

      Context.getSystemService(Context.WINDOW_SERVICE)来获取WindowManager。

      使用WindowManager可以在其他应用最上层,甚至手机桌面最上层显示窗口。

      调用的是WindowManager继承自基类的addView方法和removeView方法来显示和隐藏窗口。具体见后面的实例。

      另:API 17推出了Presentation,它将自动获取display的Context和WindowManager,可以方便地在另一个display上显示窗口。

    WindowManager实现悬浮窗例子

    声明权限

      首先在manifest中添加如下权限:

    1 <!-- 显示顶层浮窗 -->
    2 <uses-permission android:name="android.permission.SYSTEM_ALERT_WINDOW" />

    注意:在MIUI上需要在设置中打开本应用的”显示悬浮窗”开关,并且重启应用,否则悬浮窗只能显示在本应用界面内,不能显示在手机桌面上。

    服务获取和基本参数设置

    1  // 获取应用的Context
    2         mContext = context.getApplicationContext();
    3         // 获取WindowManager
    4         mWindowManager = (WindowManager) mContext
    5                 .getSystemService(Context.WINDOW_SERVICE);

    参数设置:

     1 final WindowManager.LayoutParams params = new WindowManager.LayoutParams();
     2 
     3         // 类型
     4         params.type = WindowManager.LayoutParams.TYPE_SYSTEM_ALERT;
     5 
     6         // WindowManager.LayoutParams.TYPE_SYSTEM_ALERT
     7 
     8         // 设置flag
     9 
    10         int flags = WindowManager.LayoutParams.FLAG_ALT_FOCUSABLE_IM;
    11         // | WindowManager.LayoutParams.FLAG_NOT_FOCUSABLE;
    12         // 如果设置了WindowManager.LayoutParams.FLAG_NOT_FOCUSABLE,弹出的View收不到Back键的事件
    13         params.flags = flags;
    14         // 不设置这个弹出框的透明遮罩显示为黑色
    15         params.format = PixelFormat.TRANSLUCENT;
    16         // FLAG_NOT_TOUCH_MODAL不阻塞事件传递到后面的窗口
    17         // 设置 FLAG_NOT_FOCUSABLE 悬浮窗口较小时,后面的应用图标由不可长按变为可长按
    18         // 不设置这个flag的话,home页的划屏会有问题
    19 
    20         params.width = LayoutParams.MATCH_PARENT;
    21         params.height = LayoutParams.MATCH_PARENT;
    22 
    23         params.gravity = Gravity.CENTER;

    点击和按键事件

      除了View中的各个控件的点击事件之外,弹窗View的消失控制需要一些处理。

      点击弹窗外部可隐藏弹窗的效果,首先,悬浮窗是全屏的,只不过最外层的是透明或者半透明的:

      布局如下:

     1 <?xml version="1.0" encoding="utf-8"?>
     2 <LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
     3     android:layout_width="match_parent"
     4     android:layout_height="match_parent"
     5     android:layout_gravity="center"
     6     android:background="@color/darken_background"
     7     android:gravity="center"
     8     android:orientation="vertical" >
     9 
    10     <RelativeLayout
    11         android:id="@+id/popup_window"
    12         android:layout_width="@dimen/dialog_window_width"
    13         android:layout_height="@dimen/dialog_window_height"
    14         android:background="@color/white"
    15         android:orientation="vertical" >
    16 
    17         <TextView
    18             android:id="@+id/title"
    19             android:layout_width="match_parent"
    20             android:layout_height="@dimen/dialog_title_height"
    21             android:gravity="center"
    22             android:text="@string/default_title"
    23             android:textColor="@color/dialog_title_text_color"
    24             android:textSize="@dimen/dialog_title_text_size" />
    25 
    26         <View
    27             android:id="@+id/title_divider"
    28             android:layout_width="match_parent"
    29             android:layout_height="2dp"
    30             android:layout_below="@id/title"
    31             android:background="@drawable/dialog_title_divider" />
    32 
    33         <TextView
    34             android:id="@+id/content"
    35             android:layout_width="match_parent"
    36             android:layout_height="wrap_content"
    37             android:layout_below="@id/title_divider"
    38             android:gravity="center"
    39             android:padding="@dimen/dialog_content_padding_side"
    40             android:text="@string/default_content"
    41             android:textColor="@color/dialog_content_text_color"
    42             android:textSize="@dimen/dialog_content_text_size" />
    43 
    44         <LinearLayout
    45             android:layout_width="match_parent"
    46             android:layout_height="wrap_content"
    47             android:layout_alignParentBottom="true"
    48             android:orientation="horizontal"
    49             android:paddingBottom="@dimen/dialog_content_padding_bottom"
    50             android:paddingLeft="@dimen/dialog_content_padding_side"
    51             android:paddingRight="@dimen/dialog_content_padding_side" >
    52 
    53             <Button
    54                 android:id="@+id/negativeBtn"
    55                 android:layout_width="wrap_content"
    56                 android:layout_height="wrap_content"
    57                 android:layout_weight="1"
    58                 android:background="@drawable/promote_window_negative_btn_selector"
    59                 android:focusable="true"
    60                 android:padding="@dimen/dialog_button_padding"
    61                 android:text="@string/default_btn_cancel"
    62                 android:textColor="@color/dialog_negative_btn_text_color"
    63                 android:textSize="@dimen/dialog_button_text_size" />
    64 
    65             <Button
    66                 android:id="@+id/positiveBtn"
    67                 android:layout_width="wrap_content"
    68                 android:layout_height="wrap_content"
    69                 android:layout_marginLeft="18dp"
    70                 android:layout_weight="1"
    71                 android:background="@drawable/promote_window_positive_btn_selector"
    72                 android:focusable="true"
    73                 android:padding="@dimen/dialog_button_padding"
    74                 android:text="@string/default_btn_ok"
    75                 android:textColor="@color/dialog_positive_btn_text_color"
    76                 android:textSize="@dimen/dialog_button_text_size" />
    77         </LinearLayout>
    78     </RelativeLayout>
    79 
    80 </LinearLayout>
    81 
    82 popupwindow.xml

    点击外部可消除设置:

     1 // 点击窗口外部区域可消除
     2         // 这点的实现主要将悬浮窗设置为全屏大小,外层有个透明背景,中间一部分视为内容区域
     3         // 所以点击内容区域外部视为点击悬浮窗外部
     4         final View popupWindowView = view.findViewById(R.id.popup_window);// 非透明的内容区域
     5 
     6         view.setOnTouchListener(new OnTouchListener() {
     7 
     8             @Override
     9             public boolean onTouch(View v, MotionEvent event) {
    10 
    11                 LogUtil.i(LOG_TAG, "onTouch");
    12                 int x = (int) event.getX();
    13                 int y = (int) event.getY();
    14                 Rect rect = new Rect();
    15                 popupWindowView.getGlobalVisibleRect(rect);
    16                 if (!rect.contains(x, y)) {
    17                     WindowUtils.hidePopupWindow();
    18                 }
    19 
    20                 LogUtil.i(LOG_TAG, "onTouch : " + x + ", " + y + ", rect: "
    21                         + rect);
    22                 return false;
    23             }
    24         });

    点击Back键可隐藏弹窗:

      注意Flag不能设置WindowManager.LayoutParams.FLAG_NOT_FOCUSABLE。

     1 // 点击back键可消除
     2         view.setOnKeyListener(new OnKeyListener() {
     3 
     4             @Override
     5             public boolean onKey(View v, int keyCode, KeyEvent event) {
     6                 switch (keyCode) {
     7                 case KeyEvent.KEYCODE_BACK:
     8                     WindowUtils.hidePopupWindow();
     9                     return true;
    10                 default:
    11                     return false;
    12                 }
    13             }
    14         });

    完整效果

      完整代码:

     1 package com.example.hellowindow;
     2 
     3 import android.app.Activity;
     4 import android.os.Bundle;
     5 import android.os.Handler;
     6 import android.view.View;
     7 import android.view.View.OnClickListener;
     8 import android.widget.Button;
     9 
    10 public class MainActivity extends Activity {
    11 
    12     private Handler mHandler = null;
    13 
    14     @Override
    15     protected void onCreate(Bundle savedInstanceState) {
    16         super.onCreate(savedInstanceState);
    17         setContentView(R.layout.activity_main);
    18 
    19         mHandler = new Handler();
    20 
    21         Button button = (Button) findViewById(R.id.button);
    22         button.setOnClickListener(new OnClickListener() {
    23 
    24             @Override
    25             public void onClick(View v) {
    26 
    27                 mHandler.postDelayed(new Runnable() {
    28 
    29                     @Override
    30                     public void run() {
    31                         WindowUtils.showPopupWindow(MainActivity.this);
    32 
    33                     }
    34                 }, 1000 * 3);
    35 
    36             }
    37         });
    38     }
    39 }
    40 
    41 MainActivity
      1 package com.example.hellowindow;
      2 
      3 import android.content.Context;
      4 import android.graphics.PixelFormat;
      5 import android.graphics.Rect;
      6 import android.view.Gravity;
      7 import android.view.KeyEvent;
      8 import android.view.LayoutInflater;
      9 import android.view.MotionEvent;
     10 import android.view.View;
     11 import android.view.View.OnKeyListener;
     12 import android.view.View.OnTouchListener;
     13 import android.view.WindowManager;
     14 import android.view.View.OnClickListener;
     15 import android.view.WindowManager.LayoutParams;
     16 import android.widget.Button;
     17 
     18 /**
     19  * 弹窗辅助类
     20  *
     21  * @ClassName WindowUtils
     22  *
     23  *
     24  */
     25 public class WindowUtils {
     26 
     27     private static final String LOG_TAG = "WindowUtils";
     28     private static View mView = null;
     29     private static WindowManager mWindowManager = null;
     30     private static Context mContext = null;
     31 
     32     public static Boolean isShown = false;
     33 
     34     /**
     35      * 显示弹出框
     36      *
     37      * @param context
     38      * @param view
     39      */
     40     public static void showPopupWindow(final Context context) {
     41         if (isShown) {
     42             LogUtil.i(LOG_TAG, "return cause already shown");
     43             return;
     44         }
     45 
     46         isShown = true;
     47         LogUtil.i(LOG_TAG, "showPopupWindow");
     48 
     49         // 获取应用的Context
     50         mContext = context.getApplicationContext();
     51         // 获取WindowManager
     52         mWindowManager = (WindowManager) mContext
     53                 .getSystemService(Context.WINDOW_SERVICE);
     54 
     55         mView = setUpView(context);
     56 
     57         final WindowManager.LayoutParams params = new WindowManager.LayoutParams();
     58 
     59         // 类型
     60         params.type = WindowManager.LayoutParams.TYPE_SYSTEM_ALERT;
     61 
     62         // WindowManager.LayoutParams.TYPE_SYSTEM_ALERT
     63 
     64         // 设置flag
     65 
     66         int flags = WindowManager.LayoutParams.FLAG_ALT_FOCUSABLE_IM;
     67         // | WindowManager.LayoutParams.FLAG_NOT_FOCUSABLE;
     68         // 如果设置了WindowManager.LayoutParams.FLAG_NOT_FOCUSABLE,弹出的View收不到Back键的事件
     69         params.flags = flags;
     70         // 不设置这个弹出框的透明遮罩显示为黑色
     71         params.format = PixelFormat.TRANSLUCENT;
     72         // FLAG_NOT_TOUCH_MODAL不阻塞事件传递到后面的窗口
     73         // 设置 FLAG_NOT_FOCUSABLE 悬浮窗口较小时,后面的应用图标由不可长按变为可长按
     74         // 不设置这个flag的话,home页的划屏会有问题
     75 
     76         params.width = LayoutParams.MATCH_PARENT;
     77         params.height = LayoutParams.MATCH_PARENT;
     78 
     79         params.gravity = Gravity.CENTER;
     80 
     81         mWindowManager.addView(mView, params);
     82 
     83         LogUtil.i(LOG_TAG, "add view");
     84 
     85     }
     86 
     87     /**
     88      * 隐藏弹出框
     89      */
     90     public static void hidePopupWindow() {
     91         LogUtil.i(LOG_TAG, "hide " + isShown + ", " + mView);
     92         if (isShown && null != mView) {
     93             LogUtil.i(LOG_TAG, "hidePopupWindow");
     94             mWindowManager.removeView(mView);
     95             isShown = false;
     96         }
     97 
     98     }
     99 
    100     private static View setUpView(final Context context) {
    101 
    102         LogUtil.i(LOG_TAG, "setUp view");
    103 
    104         View view = LayoutInflater.from(context).inflate(R.layout.popupwindow,
    105                 null);
    106         Button positiveBtn = (Button) view.findViewById(R.id.positiveBtn);
    107         positiveBtn.setOnClickListener(new OnClickListener() {
    108 
    109             @Override
    110             public void onClick(View v) {
    111 
    112                 LogUtil.i(LOG_TAG, "ok on click");
    113                 // 打开安装包
    114                 // 隐藏弹窗
    115                 WindowUtils.hidePopupWindow();
    116 
    117             }
    118         });
    119 
    120         Button negativeBtn = (Button) view.findViewById(R.id.negativeBtn);
    121         negativeBtn.setOnClickListener(new OnClickListener() {
    122 
    123             @Override
    124             public void onClick(View v) {
    125                 LogUtil.i(LOG_TAG, "cancel on click");
    126                 WindowUtils.hidePopupWindow();
    127 
    128             }
    129         });
    130 
    131         // 点击窗口外部区域可消除
    132         // 这点的实现主要将悬浮窗设置为全屏大小,外层有个透明背景,中间一部分视为内容区域
    133         // 所以点击内容区域外部视为点击悬浮窗外部
    134         final View popupWindowView = view.findViewById(R.id.popup_window);// 非透明的内容区域
    135 
    136         view.setOnTouchListener(new OnTouchListener() {
    137 
    138             @Override
    139             public boolean onTouch(View v, MotionEvent event) {
    140 
    141                 LogUtil.i(LOG_TAG, "onTouch");
    142                 int x = (int) event.getX();
    143                 int y = (int) event.getY();
    144                 Rect rect = new Rect();
    145                 popupWindowView.getGlobalVisibleRect(rect);
    146                 if (!rect.contains(x, y)) {
    147                     WindowUtils.hidePopupWindow();
    148                 }
    149 
    150                 LogUtil.i(LOG_TAG, "onTouch : " + x + ", " + y + ", rect: "
    151                         + rect);
    152                 return false;
    153             }
    154         });
    155 
    156         // 点击back键可消除
    157         view.setOnKeyListener(new OnKeyListener() {
    158 
    159             @Override
    160             public boolean onKey(View v, int keyCode, KeyEvent event) {
    161                 switch (keyCode) {
    162                 case KeyEvent.KEYCODE_BACK:
    163                     WindowUtils.hidePopupWindow();
    164                     return true;
    165                 default:
    166                     return false;
    167                 }
    168             }
    169         });
    170 
    171         return view;
    172 
    173     }
    174 }
    175 
    176 WindowUtils


    参考资料

      WindowManager:

      http://developer.android.com/reference/android/view/WindowManager.html

      参考实例:

      http://blog.csdn.net/deng0zhaotai/article/details/16827719

      http://www.xsmile.net/?p=538

      http://blog.csdn.net/guolin_blog/article/details/8689140

      简单说明:

      Android之Window、WindowManager与窗口管理:

      http://blog.csdn.net/xieqibao/article/details/6567814

      Android系统服务-WindowManager:

      http://blog.csdn.net/chenyafei617/article/details/6577940

      进一步的学习:

      老罗的Android之旅:

      Android Activity的窗口对象Window的创建过程分析:

      http://blog.csdn.net/luoshengyang/article/details/8223770

      窗口管理服务WindowManagerService的简要介绍和学习计划:

      http://blog.csdn.net/luoshengyang/article/details/8462738

      Android核心分析之窗口管理:

      http://blog.csdn.net/maxleng/article/details/5557758

  • 相关阅读:
    数组,一维,二维,多维
    类函数:string、math
    while和for的内嵌
    循环语言(for)
    选择语言之switch case
    程序语言
    语言、数据和运算符
    原理之一,进制转换
    HTML第一部分
    结构体共用变量 递归
  • 原文地址:https://www.cnblogs.com/zl1991/p/5122388.html
Copyright © 2011-2022 走看看