• Android中可自由移动悬浮窗口的实现


    大家对悬浮窗概念不会陌生,相信每台电脑桌面的右上角都会有这么一个东西,它总是出现在所有页面的顶端(Top Show)。但在Android平台中如何实现这样的效果呢?先来看一看效果图。

    FloatView

    看见在Google搜索框上面的那个Icon图片了嘛。下面我就来详细介绍一下在Android平台下悬浮窗口的实现,并让它能够随手指的触摸而移动。

    一、实现原理及移动思路

    调用WindowManager,并设置WindowManager.LayoutParams的相关属性,通过WindowManager的addView方法创建View,这样产生出来的View根据WindowManager.LayoutParams属性不同,效果也就不同了。比如创建系统顶级窗口,实现悬浮窗口效果!然后通过覆写悬浮View中onTouchEvent方法来改变windowMananager.LayoutParams中x和y的值来实现自由移动悬浮窗口。

    二、示例代码

    先来看一看悬浮View的代码,这里用一个ImageView作为演示

     1 public class MyFloatView extends ImageView {
     2 
     3     private float mTouchStartX;
     4 
     5     private float mTouchStartY;
     6 
     7     private float x;
     8 
     9     private float y;
    10 
    11      
    12 
    13     private WindowManager wm=(WindowManager)getContext().getApplicationContext().getSystemService("window");
    14 
    15     //此wmParams变量为获取的全局变量,用以保存悬浮窗口的属性
    16 09
    17     private WindowManager.LayoutParams wmParams = ((MyApplication)getContext().getApplicationContext()).getMywmParams();
    18 10
    19  
    20 11
    21     public MyFloatView(Context context) {
    22 12
    23         super(context);    
    24 13
    25         // TODO Auto-generated constructor stub
    26 14
    27     }
    28 15
    29      
    30 16
    31      @Override
    32 17
    33      public boolean onTouchEvent(MotionEvent event) {
    34 18
    35          //获取相对屏幕的坐标,即以屏幕左上角为原点     
    36 19
    37          x = event.getRawX();  
    38 20
    39          y = event.getRawY()-25;   //25是系统状态栏的高度
    40 21
    41          Log.i("currP", "currX"+x+"====currY"+y);
    42 22
    43          switch (event.getAction()) {
    44 23
    45             case MotionEvent.ACTION_DOWN:    //捕获手指触摸按下动作
    46 24
    47                 //获取相对View的坐标,即以此View左上角为原点
    48 25
    49                 mTouchStartX =  event.getX(); 
    50 26
    51                     mTouchStartY =  event.getY();               
    52 27
    53                 Log.i("startP","startX"+mTouchStartX+"====startY"+mTouchStartY);
    54 28
    55                 break;
    56 29
    57  
    58 30
    59             case MotionEvent.ACTION_MOVE:   //捕获手指触摸移动动作           
    60 31
    61                 updateViewPosition();
    62 32
    63                 break;
    64 33
    65  
    66 34
    67             case MotionEvent.ACTION_UP:    //捕获手指触摸离开动作
    68 35
    69                 updateViewPosition();
    70 36
    71                 mTouchStartX=mTouchStartY=0;
    72 37
    73                 break;
    74 38
    75             }
    76 39
    77             return true;
    78 40
    79         }
    80 41
    81       
    82 42
    83      private void updateViewPosition(){
    84 43
    85         //更新浮动窗口位置参数
    86 44
    87         wmParams.x=(int)( x-mTouchStartX);
    88 45
    89         wmParams.y=(int) (y-mTouchStartY);
    90 46
    91         wm.updateViewLayout(this, wmParams);  //刷新显示
    92 47
    93      }
    94 48
    95  
    96 49
    97 }

    上面的wmParams变量(即WindowManager.LayoutParams)的存储采用了extends Application的方式来创建全局变量,示例代码如下:

     1 public class MyApplication extends Application {
     2 02
     3      
     4 03
     5     /**
     6 04
     7      * 创建全局变量
     8 05
     9      * 全局变量一般都比较倾向于创建一个单独的数据类文件,并使用static静态变量
    10 06
    11      *
    12 07
    13      * 这里使用了在Application中添加数据的方法实现全局变量
    14 08
    15      * 注意在AndroidManifest.xml中的Application节点添加android:name=".MyApplication"属性
    16 09
    17      *
    18 10
    19      */
    20 11
    21     private WindowManager.LayoutParams wmParams=newWindowManager.LayoutParams();
    22 12
    23  
    24 13
    25     public WindowManager.LayoutParams getMywmParams(){
    26 14
    27         return wmParams;
    28 15
    29     }
    30 16
    31 }

    再来看一看Activity中的代码:

      1 public class MyFloatViewActivity extends Activity {
      2 02
      3     /** Called when the activity is first created. */
      4 03
      5      
      6 04
      7     private WindowManager wm=null;
      8 05
      9     private WindowManager.LayoutParams wmParams=null;
     10 06
     11      
     12 07
     13     private MyFloatView myFV=null;
     14 08
     15  
     16 09
     17      
     18 10
     19     @Override
     20 11
     21     public void onCreate(Bundle savedInstanceState) {
     22 12
     23         super.onCreate(savedInstanceState);
     24 13
     25         setContentView(R.layout.main);
     26 14
     27         //创建悬浮窗口
     28 15
     29         createView();
     30 16
     31      
     32 17
     33     }
     34 18
     35      
     36 19
     37    
     38 20
     39      
     40 21
     41     private void createView(){
     42 22
     43         myFV=new MyFloatView(getApplicationContext());
     44 23
     45         myFV.setImageResource(R.drawable.icon);  //这里简单的用自带的Icom来做演示
     46 24
     47         //获取WindowManager
     48 25
     49         wm=(WindowManager)getApplicationContext().getSystemService("window");
     50 26
     51         //设置LayoutParams(全局变量)相关参数
     52 27
     53         wmParams = ((MyApplication)getApplication()).getMywmParams();
     54 28
     55  
     56 29
     57          /**
     58 30
     59          *以下都是WindowManager.LayoutParams的相关属性
     60 31
     61          * 具体用途可参考SDK文档
     62 32
     63          */
     64 33
     65         wmParams.type=LayoutParams.TYPE_PHONE;   //设置window type
     66 34
     67         wmParams.format=PixelFormat.RGBA_8888;   //设置图片格式,效果为背景透明
     68 35
     69  
     70 36
     71         //设置Window flag
     72 37
     73         wmParams.flags=LayoutParams.FLAG_NOT_TOUCH_MODAL
     74 38
     75                               | LayoutParams.FLAG_NOT_FOCUSABLE;
     76 39
     77         /*
     78 40
     79          * 下面的flags属性的效果形同“锁定”。
     80 41
     81          * 悬浮窗不可触摸,不接受任何事件,同时不影响后面的事件响应。
     82 42
     83          wmParams.flags=LayoutParams.FLAG_NOT_TOUCH_MODAL
     84 43
     85                                | LayoutParams.FLAG_NOT_FOCUSABLE
     86 44
     87                                | LayoutParams.FLAG_NOT_TOUCHABLE;
     88 45
     89         */
     90 46
     91          
     92 47
     93          
     94 48
     95         wmParams.gravity=Gravity.LEFT|Gravity.TOP;   //调整悬浮窗口至左上角,便于调整坐标
     96 49
     97         //以屏幕左上角为原点,设置x、y初始值
     98 50
     99         wmParams.x=0;
    100 51
    101         wmParams.y=0;
    102 52
    103          
    104 53
    105         //设置悬浮窗口长宽数据
    106 54
    107         wmParams.width=40;
    108 55
    109         wmParams.height=40;
    110 56
    111      
    112 57
    113         //显示myFloatView图像
    114 58
    115         wm.addView(myFV, wmParams);
    116 59
    117   
    118 60
    119     }
    120 61
    121      
    122 62
    123     @Override
    124 63
    125     public void onDestroy(){
    126 64
    127         super.onDestroy();
    128 65
    129         //在程序退出(Activity销毁)时销毁悬浮窗口
    130 66
    131         wm.removeView(myFV);
    132 67
    133     }   
    134 68
    135 }

    最后,别忘了在AndroidManifest.xml中添加权限:

    1 <uses-permission android:name="android.permission.SYSTEM_ALERT_WINDOW">
    2 2
    3 </uses-permission>

    这样一个可以置顶显示、悬浮、且可自由移动的窗口就完工了。运行一下,然后按Home键返回桌面试试看(不能点击返回键,演示程序这里设置了销毁窗体)

    三、一些说明

    WindowManager的方法很简单,基本用到的就三个addView,removeView,updateViewLayout。

    而WindowManager.LayoutParams的属性就多了,非常丰富,这个也是关键所在。

    这里例举两个window type:

     1 /**
     2 02
     3  * Window type: phone.  These are non-application windows providing
     4 03
     5  * user interaction with the phone (in particular incoming calls).
     6 04
     7  * These windows are normally placed above all applications, but behind
     8 05
     9  * the status bar.
    10 06
    11  */
    12 07
    13 public static final int TYPE_PHONE              = FIRST_SYSTEM_WINDOW+2;
    14 08
    15  
    16 09
    17 /**
    18 10
    19  * Window type: system window, such as low power alert. These windows
    20 11
    21  * are always on top of application windows.
    22 12
    23  */
    24 13
    25 public static final int TYPE_SYSTEM_ALERT       = FIRST_SYSTEM_WINDOW+3;

    可以看出TYPE_SYSTEM_ALERT的显示层次比TYPE_PHONE还要高,有兴趣的可以试一试显示效果哦!

    另外关键的window flag:

     1 /** Window flag: this window won't ever get focus. */
     2 02
     3 public static final int FLAG_NOT_FOCUSABLE      = 0x00000008;
     4 03
     5  
     6 04
     7 /** Window flag: this window can never receive touch events. */
     8 05
     9 public static final int FLAG_NOT_TOUCHABLE      = 0x00000010;
    10 06
    11  
    12 07
    13 /** Window flag: Even when this window is focusable (its
    14 08
    15  * {@link #FLAG_NOT_FOCUSABLE is not set), allow any pointer events
    16 09
    17  * outside of the window to be sent to the windows behind it.  Otherwise
    18 10
    19  * it will consume all pointer events itself, regardless of whether they
    20 11
    21  * are inside of the window. */
    22 12
    23 public static final int FLAG_NOT_TOUCH_MODAL    = 0x00000020;

    详细的可以看一下这里

    最后,关于Android平台下的悬浮窗口,有人说很不友好,有人很困惑哪里会用到。事实上,在一些软件里面,悬浮窗口的设计给它们带来了很大的优势,比如流量监控,比如歌词显示。

    给出源码包下载

    转自:http://blog.csdn.net/fancylovejava/article/details/17891977

  • 相关阅读:
    php 本周的起始时间
    获取微信公众号素材模板id
    复活贴
    Net 4.0 Parallel 编程(四) Task(上)
    Windows Phone 实用开发技巧120合集(电子书+源代码)
    Net 4.0 Parallel 编程(九)Task中的数据共享(下)
    收藏别人的小类库
    将DLL程序集加入GAC后的一系列问题汇总,及解决方法
    Net 4.0 Parallel 编程(七)Task中的数据共享(上)
    内存做硬盘,开启readyboost加速,全面提升系统性能!
  • 原文地址:https://www.cnblogs.com/zl1991/p/5126433.html
Copyright © 2020-2023  润新知