Android提供的系统服务之--WindowManager(窗口管理服务)
——转载请注明出处:coder-pig
本节引言:
本节我们来探讨下这个Android系统服务中的WindowManager(窗口管理服务),
他是显示View的最底层,好像我们的Actviity和Dialog,以及Toast的底层实现都用到
这个WindowManager,他是全局的!核心其实就是WindowManager调用addView,
removeView,updateViewLayout这几个方法来显示View;还有WindowManager.LayoutParams
这个API来设置相关的属性!本节我们就写两个关于WindowManger的实用例子吧:
分别是获取屏幕宽高,以及弄一个Android的悬浮框!还有保持屏幕的常亮以及全屏设置
好了,开始本节内容!
本节正文:
1.相关概念图:
2.使用例子:
①获取手机屏幕宽高:
我们通过调用getDefaultDisplay( )可以获得默认的Display显示对象,接着调用getWidth( )
和getHeight( )即可获得屏幕宽高
代码如下:
[java]
view plain
copy
print ?
- WindowManager windowManager = (WindowManager) getSystemService(Context.WINDOW_SERVICE);
- setTitle(windowManager.getDefaultDisplay().getWidth() + "*" + windowManager.getDefaultDisplay().getHeight());
运行截图:
②Android悬浮框的实现:
先来看下效果图吧,这里只是一个简单的按钮,大家可以按自己的需求来自定义~
后面还提供一个类似于QQ悬浮发射小火箭的demo,有需要的可以下载来自己研究研究~
实现流程解析:
step 1:我们需要一个后台的Service在后台等待我们的操作,比如完成,View的绘制,移除等~
我们先创建一个空的Service类:MyWindowService继承Service,然后我们需要在
AndroidManifest.xml为这个Service来进行注册!
[html]
view plain
copy
print ?
- <service android:name=".MyWindowService"/>
另外还需要加上下述两个权限:
[html]
view plain
copy
print ?
- <uses-permission android:name="android.permission.SYSTEM_ALERT_WINDOW" />
- <uses-permission android:name="android.permission.GET_TASKS" />
step 2:在我们的MainActivity中设置两个按钮的点击事件,我们还要为intent写入
一个extra,根据这个值,我们在Service进行判断,是开启悬浮框,还是关闭悬浮框
[java]
view plain
copy
print ?
- package com.jay.example.windowmanagerdemo1;
-
- import android.app.Activity;
- import android.content.Intent;
- import android.os.Bundle;
- import android.view.View;
- import android.view.View.OnClickListener;
- import android.widget.Button;
- import android.widget.Toast;
-
- public class MainActivity extends Activity {
-
- private Button btnShow;
- private Button btnClose;
-
-
- @Override
- protected void onCreate(Bundle savedInstanceState) {
- super.onCreate(savedInstanceState);
- setContentView(R.layout.activity_main);
-
- btnShow = (Button) findViewById(R.id.btnShow);
- btnClose = (Button) findViewById(R.id.btnClose);
-
- btnShow.setOnClickListener(new OnClickListener() {
- @Override
- public void onClick(View v) {
- Intent show = new Intent(MainActivity.this, MyWindowService.class);
- show.putExtra(MyWindowService.OPERATION, MyWindowService.OPERATION_SHOW);
- startService(show);
- Toast.makeText(MainActivity.this,"悬浮框已开启~", Toast.LENGTH_SHORT).show();
- }
- });
-
- btnClose.setOnClickListener(new OnClickListener() {
- @Override
- public void onClick(View v) {
- Intent hide = new Intent(MainActivity.this, MyWindowService.class);
- hide.putExtra(MyWindowService.OPERATION, MyWindowService.OPERATION_HIDE);
- startService(hide);
- Toast.makeText(MainActivity.this,"悬浮框已开启~", Toast.LENGTH_SHORT).show();
- }
- });
- }
- }
step 3:接下来就需要开始编写我们的Service类了,我们想想这个Service需要干嘛?
①肯定需要一个创建View的方法啦,于是乎,我们定义一个createWindowView( )方法用于创建
悬浮框的View!
[java]
view plain
copy
print ?
-
- private void createWindowView() {
- btnView = new Button(getApplicationContext());
- btnView.setBackgroundResource(R.drawable.pig);
- windowManager = (WindowManager) getApplicationContext()
- .getSystemService(Context.WINDOW_SERVICE);
- params = new WindowManager.LayoutParams();
-
-
- params.type = WindowManager.LayoutParams.TYPE_SYSTEM_ALERT;
-
- params.flags = WindowManager.LayoutParams.FLAG_NOT_TOUCH_MODAL
- | WindowManager.LayoutParams.FLAG_NOT_FOCUSABLE;
-
- params.format = PixelFormat.RGBA_8888;
-
- params.width = 200;
- params.height = 200;
- params.gravity = Gravity.LEFT;
- params.x = 200;
- params.y = 000;
-
- btnView.setOnTouchListener(new OnTouchListener() {
-
- int lastX, lastY;
- int paramX, paramY;
- @Override
- public boolean onTouch(View v, MotionEvent event) {
- switch (event.getAction()) {
- case MotionEvent.ACTION_DOWN:
- lastX = (int) event.getRawX();
- lastY = (int) event.getRawY();
- paramX = params.x;
- paramY = params.y;
- break;
- case MotionEvent.ACTION_MOVE:
- int dx = (int) event.getRawX() - lastX;
- int dy = (int) event.getRawY() - lastY;
- params.x = paramX + dx;
- params.y = paramY + dy;
-
- windowManager.updateViewLayout(btnView, params);
- break;
- }
- return true;
- }
- });
- windowManager.addView(btnView, params);
- isAdded = true;
- }
②这个时候我们只需在OnCreate( )方法中调用上述的createWindowView( )方法即可启动加载悬浮框了
但是,我们发现一点...这玩意貌似关不掉啊,卧槽,好吧,接下来我们就要分析下需求了!当处于手机的普通界面,
即桌面的时候,这玩意才显示,而当我们启动其他App时,这个悬浮框应该消失不见,当我们推出app又回到
桌面,这个悬浮框又要重新出现!那么我们首先需要判断App是否位于桌面,我们通过下面的代码就可以完成这个
判断:
[java]
view plain
copy
print ?
-
-
-
- public boolean isHome(){
- if(mActivityManager == null) {
- mActivityManager = (ActivityManager)getSystemService(Context.ACTIVITY_SERVICE);
- }
- List rti = mActivityManager.getRunningTasks(1);
- return homeList.contains(rti.get(0).topActivity.getPackageName());
- }
-
-
-
-
-
- private List getHomes() {
- List names = new ArrayList();
- PackageManager packageManager = this.getPackageManager();
-
- Intent intent = new Intent(Intent.ACTION_MAIN);
- intent.addCategory(Intent.CATEGORY_HOME);
- List resolveInfo = packageManager.queryIntentActivities(intent,
- PackageManager.MATCH_DEFAULT_ONLY);
- for(ResolveInfo ri : resolveInfo) {
- names.add(ri.activityInfo.packageName);
- }
- return names;
- }
③好了,接下来我们需要每隔一段时间来进行一系列的判断,比如:是否在桌面,是否已加载悬浮框,否则加载;
否则,如果加载了,就将这个悬浮框移除!这里我们使用handler~,因为不能在子线程直接更新UI,所以,你懂的
所以我们自己写一个handler来完成上述的操作:
[java]
view plain
copy
print ?
-
- private Handler mHandler = new Handler() {
- @Override
- public void handleMessage(Message msg) {
- switch(msg.what) {
- case HANDLE_CHECK_ACTIVITY:
- if(isHome()) {
- if(!isAdded) {
- windowManager.addView(btnView, params);
- isAdded = true;
- new Thread(new Runnable() {
- public void run() {
- for(int i=0;i<10;i++){
- try {
- Thread.sleep(1000);
- } catch (InterruptedException e) {e.printStackTrace();}
- Message m = new Message();
- m.what=2;
- mHandler.sendMessage(m);
- }
- }
- }).start();}
- } else {
- if(isAdded) {
- windowManager.removeView(btnView);
- isAdded = false;
- }
- }
- mHandler.sendEmptyMessageDelayed(HANDLE_CHECK_ACTIVITY, 0);
- break;
- }
- }
- };
④最后要做的一件事,就是重写Service的onStartCommand( )方法了,就是做判断,取出Intent中的
数据,判断是需要添加悬浮框,还是要移除悬浮框!
[java]
view plain
copy
print ?
- @Override
- public int onStartCommand(Intent intent, int flags, int startId) {
- int operation = intent.getIntExtra(OPERATION, OPERATION_SHOW);
- switch(operation) {
- case OPERATION_SHOW:
- mHandler.removeMessages(HANDLE_CHECK_ACTIVITY);
- mHandler.sendEmptyMessage(HANDLE_CHECK_ACTIVITY);
- break;
- case OPERATION_HIDE:
- mHandler.removeMessages(HANDLE_CHECK_ACTIVITY);
- break;
- }
- return super.onStartCommand(intent, flags, startId);
- }
好了,这个程序的实现流程就这是这样,一次看不懂看多几遍就能了解了!
最后还献上WindowManager的两个常用实例吧:
③设置窗口全屏显示:
[java]
view plain
copy
print ?
- getWindow().setFlags(WindowManager.LayoutParams.FLAG_FULLSCREEN,
- WindowManager.LayoutParams.FLAG_FULLSCREEN);
④保持窗口打开,即屏幕常亮:
[java]
view plain
copy
print ?
- public void setKeepScreenOn(Activity activity,boolean keepScreenOn)
- {
- if(keepScreenOn)
- {
- activity.getWindow().addFlags(WindowManager.LayoutParams.FLAG_KEEP_SCREEN_ON);
- }else{
- activity.getWindow().clearFlags(WindowManager.LayoutParams.FLAG_KEEP_SCREEN_ON);
- }
- }
最后说几句:
好了,关于这个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