Android提供的系统服务之--WindowManager(窗口管理服务)

Android提供的系统服务之--WindowManager(窗口管理服务)

                                                          ——转载请注明出处:coder-pig


本节引言:

本节我们来探讨下这个Android系统服务中的WindowManager(窗口管理服务),

他是显示View的最底层,好像我们的Actviity和Dialog,以及Toast的底层实现都用到

这个WindowManager,他是全局的!核心其实就是WindowManager调用addView,

removeView,updateViewLayout这几个方法来显示View;还有WindowManager.LayoutParams

这个API来设置相关的属性!本节我们就写两个关于WindowManger的实用例子吧:

分别是获取屏幕宽高,以及弄一个Android的悬浮框!还有保持屏幕的常亮以及全屏设置

好了,开始本节内容!



本节正文:

1.相关概念图:

Android提供的系统服务之--WindowManager(窗口管理服务)_第1张图片

   


2.使用例子:

①获取手机屏幕宽高:

我们通过调用getDefaultDisplay( )可以获得默认的Display显示对象,接着调用getWidth( )

getHeight( )即可获得屏幕宽高

代码如下:

[java]   view plain  copy  print ?
  1. WindowManager windowManager = (WindowManager) getSystemService(Context.WINDOW_SERVICE);  
  2. setTitle(windowManager.getDefaultDisplay().getWidth() + "*" + windowManager.getDefaultDisplay().getHeight());  

运行截图:





②Android悬浮框的实现:

先来看下效果图吧,这里只是一个简单的按钮,大家可以按自己的需求来自定义~

后面还提供一个类似于QQ悬浮发射小火箭的demo,有需要的可以下载来自己研究研究~

Android提供的系统服务之--WindowManager(窗口管理服务)_第2张图片


实现流程解析:

step 1:我们需要一个后台的Service在后台等待我们的操作,比如完成,View的绘制,移除等~

我们先创建一个空的Service类:MyWindowService继承Service,然后我们需要在

AndroidManifest.xml为这个Service来进行注册!

[html]   view plain  copy  print ?
  1. <service android:name=".MyWindowService"/>  
另外还需要加上下述两个权限:

[html]   view plain  copy  print ?
  1. <uses-permission android:name="android.permission.SYSTEM_ALERT_WINDOW" />  
  2. <uses-permission android:name="android.permission.GET_TASKS" />  



step 2:在我们的MainActivity中设置两个按钮的点击事件,我们还要为intent写入

一个extra,根据这个值,我们在Service进行判断,是开启悬浮框,还是关闭悬浮框

[java]   view plain  copy  print ?
  1. package com.jay.example.windowmanagerdemo1;  
  2.   
  3. import android.app.Activity;  
  4. import android.content.Intent;  
  5. import android.os.Bundle;  
  6. import android.view.View;  
  7. import android.view.View.OnClickListener;  
  8. import android.widget.Button;  
  9. import android.widget.Toast;  
  10.   
  11. public class MainActivity extends Activity {  
  12.   
  13.     private Button btnShow;  
  14.     private Button btnClose;  
  15.       
  16.       
  17.     @Override  
  18.     protected void onCreate(Bundle savedInstanceState) {  
  19.         super.onCreate(savedInstanceState);  
  20.         setContentView(R.layout.activity_main);  
  21.           
  22.         btnShow = (Button) findViewById(R.id.btnShow);  
  23.         btnClose = (Button) findViewById(R.id.btnClose);  
  24.           
  25.         btnShow.setOnClickListener(new OnClickListener() {  
  26.             @Override  
  27.             public void onClick(View v) {  
  28.                 Intent show = new Intent(MainActivity.this, MyWindowService.class);  
  29.                 show.putExtra(MyWindowService.OPERATION, MyWindowService.OPERATION_SHOW);  
  30.                 startService(show);   
  31.                 Toast.makeText(MainActivity.this,"悬浮框已开启~", Toast.LENGTH_SHORT).show();  
  32.             }  
  33.         });  
  34.           
  35.         btnClose.setOnClickListener(new OnClickListener() {  
  36.             @Override  
  37.             public void onClick(View v) {  
  38.                 Intent hide = new Intent(MainActivity.this, MyWindowService.class);  
  39.                 hide.putExtra(MyWindowService.OPERATION, MyWindowService.OPERATION_HIDE);  
  40.                 startService(hide);  
  41.                 Toast.makeText(MainActivity.this,"悬浮框已开启~", Toast.LENGTH_SHORT).show();  
  42.             }  
  43.         });  
  44.     }  
  45. }  


step 3:接下来就需要开始编写我们的Service类了,我们想想这个Service需要干嘛?

①肯定需要一个创建View的方法啦,于是乎,我们定义一个createWindowView( )方法用于创建

悬浮框的View!

[java]   view plain  copy  print ?
  1. // 定义一个创建悬浮框的方法:  
  2.     private void createWindowView() {  
  3.         btnView = new Button(getApplicationContext());  
  4.         btnView.setBackgroundResource(R.drawable.pig);  
  5.         windowManager = (WindowManager) getApplicationContext()  
  6.                 .getSystemService(Context.WINDOW_SERVICE);  
  7.         params = new WindowManager.LayoutParams();  
  8.   
  9.         // 设置Window Type  
  10.         params.type = WindowManager.LayoutParams.TYPE_SYSTEM_ALERT;  
  11.         // 设置悬浮框不可触摸  
  12.         params.flags = WindowManager.LayoutParams.FLAG_NOT_TOUCH_MODAL  
  13.                 | WindowManager.LayoutParams.FLAG_NOT_FOCUSABLE;  
  14.         // 悬浮窗不可触摸,不接受任何事件,同时不影响后面的事件响应  
  15.         params.format = PixelFormat.RGBA_8888;  
  16.         // 设置悬浮框的宽高  
  17.         params.width = 200;  
  18.         params.height = 200;  
  19.         params.gravity = Gravity.LEFT;  
  20.         params.x = 200;  
  21.         params.y = 000;  
  22.         // 设置悬浮框的Touch监听  
  23.         btnView.setOnTouchListener(new OnTouchListener() {  
  24.             //保存悬浮框最后位置的变量  
  25.             int lastX, lastY;  
  26.             int paramX, paramY;  
  27.             @Override  
  28.             public boolean onTouch(View v, MotionEvent event) {  
  29.                 switch (event.getAction()) {  
  30.                 case MotionEvent.ACTION_DOWN:  
  31.                     lastX = (int) event.getRawX();  
  32.                     lastY = (int) event.getRawY();  
  33.                     paramX = params.x;  
  34.                     paramY = params.y;  
  35.                     break;  
  36.                 case MotionEvent.ACTION_MOVE:  
  37.                     int dx = (int) event.getRawX() - lastX;  
  38.                     int dy = (int) event.getRawY() - lastY;  
  39.                     params.x = paramX + dx;  
  40.                     params.y = paramY + dy;  
  41.                     // 更新悬浮窗位置  
  42.                     windowManager.updateViewLayout(btnView, params);  
  43.                     break;  
  44.                 }  
  45.                 return true;  
  46.             }  
  47.         });  
  48.         windowManager.addView(btnView, params);  
  49.         isAdded = true;  
  50.     }  


②这个时候我们只需在OnCreate( )方法中调用上述的createWindowView( )方法即可启动加载悬浮框了

但是,我们发现一点...这玩意貌似关不掉啊,卧槽,好吧,接下来我们就要分析下需求了!当处于手机的普通界面,

即桌面的时候,这玩意才显示,而当我们启动其他App时,这个悬浮框应该消失不见,当我们推出app又回到

桌面,这个悬浮框又要重新出现!那么我们首先需要判断App是否位于桌面,我们通过下面的代码就可以完成这个

判断:

[java]   view plain  copy  print ?
  1. /**  
  2.  * 判断当前界面是否是桌面  
  3.  */    
  4. public boolean isHome(){    
  5.     if(mActivityManager == null) {  
  6.         mActivityManager = (ActivityManager)getSystemService(Context.ACTIVITY_SERVICE);    
  7.     }  
  8.     List rti = mActivityManager.getRunningTasks(1);    
  9.     return homeList.contains(rti.get(0).topActivity.getPackageName());    
  10. }  
  11.   
  12. /**  
  13.  * 获得属于桌面的应用的应用包名称  
  14.  * @return 返回包含所有包名的字符串列表  
  15.  */  
  16. private List getHomes() {  
  17.     List names = new ArrayList();    
  18.     PackageManager packageManager = this.getPackageManager();    
  19.     // 属性    
  20.     Intent intent = new Intent(Intent.ACTION_MAIN);    
  21.     intent.addCategory(Intent.CATEGORY_HOME);    
  22.     List resolveInfo = packageManager.queryIntentActivities(intent,    
  23.             PackageManager.MATCH_DEFAULT_ONLY);    
  24.     for(ResolveInfo ri : resolveInfo) {    
  25.         names.add(ri.activityInfo.packageName);    
  26.     }  
  27.     return names;    
  28. }  

③好了,接下来我们需要每隔一段时间来进行一系列的判断,比如:是否在桌面,是否已加载悬浮框,否则加载;

否则,如果加载了,就将这个悬浮框移除!这里我们使用handler~,因为不能在子线程直接更新UI,所以,你懂的

所以我们自己写一个handler来完成上述的操作:


[java]   view plain  copy  print ?
  1. //定义一个更新界面的Handler  
  2.     private Handler mHandler = new Handler() {  
  3.         @Override  
  4.         public void handleMessage(Message msg) {  
  5.             switch(msg.what) {  
  6.             case HANDLE_CHECK_ACTIVITY:  
  7.                 if(isHome()) {  
  8.                     if(!isAdded) {  
  9.                         windowManager.addView(btnView, params);  
  10.                         isAdded = true;  
  11.                     new Thread(new Runnable() {  
  12.                         public void run() {  
  13.                             for(int i=0;i<10;i++){  
  14.                                 try {  
  15.                                     Thread.sleep(1000);  
  16.                                 } catch (InterruptedException e) {e.printStackTrace();}  
  17.                                 Message m = new Message();  
  18.                                 m.what=2;  
  19.                                 mHandler.sendMessage(m);  
  20.                             }  
  21.                         }  
  22.                     }).start();}  
  23.                 } else {  
  24.                     if(isAdded) {  
  25.                         windowManager.removeView(btnView);  
  26.                         isAdded = false;  
  27.                     }  
  28.                 }  
  29.                 mHandler.sendEmptyMessageDelayed(HANDLE_CHECK_ACTIVITY, 0);  
  30.                 break;  
  31.             }  
  32.         }  
  33.     };  

④最后要做的一件事,就是重写Service的onStartCommand( )方法了,就是做判断,取出Intent中的

数据,判断是需要添加悬浮框,还是要移除悬浮框!


[java]   view plain  copy  print ?
  1. @Override  
  2.     public int onStartCommand(Intent intent, int flags, int startId) {  
  3.         int operation = intent.getIntExtra(OPERATION, OPERATION_SHOW);  
  4.         switch(operation) {  
  5.         case OPERATION_SHOW:  
  6.             mHandler.removeMessages(HANDLE_CHECK_ACTIVITY);  
  7.             mHandler.sendEmptyMessage(HANDLE_CHECK_ACTIVITY);  
  8.             break;  
  9.         case OPERATION_HIDE:  
  10.             mHandler.removeMessages(HANDLE_CHECK_ACTIVITY);  
  11.             break;  
  12.         }  
  13.         return super.onStartCommand(intent, flags, startId);  
  14.     }  


好了,这个程序的实现流程就这是这样,一次看不懂看多几遍就能了解了!




最后还献上WindowManager的两个常用实例吧:

③设置窗口全屏显示:

[java]   view plain  copy  print ?
  1. getWindow().setFlags(WindowManager.LayoutParams.FLAG_FULLSCREEN,  
  2.         WindowManager.LayoutParams.FLAG_FULLSCREEN);  


④保持窗口打开,即屏幕常亮:

[java]   view plain  copy  print ?
  1. public void setKeepScreenOn(Activity activity,boolean keepScreenOn)  
  2. {  
  3.     if(keepScreenOn)  
  4.     {  
  5.         activity.getWindow().addFlags(WindowManager.LayoutParams.FLAG_KEEP_SCREEN_ON);  
  6.     }else{  
  7.         activity.getWindow().clearFlags(WindowManager.LayoutParams.FLAG_KEEP_SCREEN_ON);  
  8.     }  
  9. }  


最后说几句:

好了,关于这个WindowManager就写到这里!

本节实例代码下载:

1.利用WindowManager实现简单的悬浮框:http://pan.baidu.com/s/1pJiHSXP

2.Android模仿QQ小火箭:http://pan.baidu.com/s/1eQeW2um




ps:对于WindowManager.LayoutParams的相关标记可见下述链接,有需要的自己查,笔者就不在这里详述了:

Android官方:http://developer.android.com/reference/android/view/WindowManager.LayoutParams.html

csdn别人写的一篇blog:http://blog.csdn.net/chenyafei617/article/details/6577940

你可能感兴趣的:(Android提供的系统服务之--WindowManager(窗口管理服务))