Android提供的系統服務之--WindowManager(窗口管理服務)
來源:程序員人生 發布時間:2015-03-16 10:50:03 閱讀次數:5967次
Android提供的系統服務之--WindowManager(窗口管理服務)
――轉載請注明出處:coder-pig
本節引言:
本節我們來探討下這個Android系統服務中的WindowManager(窗口管理服務),
他是顯示View的最底層,好像我們的Actviity和Dialog,和Toast的底層實現都用到
這個WindowManager,他是全局的!核心其實就是WindowManager調用addView,
removeView,updateViewLayout這幾個方法來顯示View;還有WindowManager.LayoutParams
這個API來設置相干的屬性!本節我們就寫兩個關于WindowManger的實用例子吧:
分別是獲得屏幕寬高,和弄1個Android的懸浮框!還有保持屏幕的常亮和全屏設置
好了,開始本節內容!
本節正文:
1.相干概念圖:

2.使用例子:
①獲得手機屏幕寬高:
我們通過調用getDefaultDisplay( )可以取得默許的Display顯示對象,接著調用getWidth( )
和getHeight( )便可取得屏幕寬高
代碼以下:
WindowManager windowManager = (WindowManager) getSystemService(Context.WINDOW_SERVICE);
setTitle(windowManager.getDefaultDisplay().getWidth() + "*" + windowManager.getDefaultDisplay().getHeight());
運行截圖:

②Android懸浮框的實現:
先來看下效果圖吧,這里只是1個簡單的按鈕,大家可以按自己的需求來自定義~
后面還提供1個類似于QQ懸浮發射小火箭的demo,有需要的可以下載來自己研究研究~

實現流程解析:
step 1:我們需要1個后臺的Service在后臺等待我們的操作,比如完成,View的繪制,移除等~
我們先創建1個空的Service類:MyWindowService繼承Service,然后我們需要在
AndroidManifest.xml為這個Service來進行注冊!
<service android:name=".MyWindowService"/>
另外還需要加上下述兩個權限:
<uses-permission android:name="android.permission.SYSTEM_ALERT_WINDOW" />
<uses-permission android:name="android.permission.GET_TASKS" />
step 2:在我們的MainActivity中設置兩個按鈕的點擊事件,我們還要為intent寫入
1個extra,根據這個值,我們在Service進行判斷,是開啟懸浮框,還是關閉懸浮框
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需要干嗎?
①肯定需要1個創建View的方法啦,因而乎,我們定義1個createWindowView( )方法用于創建
懸浮框的View!
// 定義1個創建懸浮框的方法:
private void createWindowView() {
btnView = new Button(getApplicationContext());
btnView.setBackgroundResource(R.drawable.pig);
windowManager = (WindowManager) getApplicationContext()
.getSystemService(Context.WINDOW_SERVICE);
params = new WindowManager.LayoutParams();
// 設置Window Type
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;
// 設置懸浮框的Touch監聽
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( )方法便可啟動加載懸浮框了
但是,我們發現1點...這玩意貌似關不掉啊,臥槽,好吧,接下來我們就要分析下需求了!當處于手機的普通界面,
即桌面的時候,這玩意才顯示,而當我們啟動其他App時,這個懸浮框應當消失不見,當我們推出app又回到
桌面,這個懸浮框又要重新出現!那末我們首先需要判斷App是不是位于桌面,我們通過下面的代碼就能夠完成這個
判斷:
/**
* 判斷當前界面是不是是桌面
*/
public boolean isHome(){
if(mActivityManager == null) {
mActivityManager = (ActivityManager)getSystemService(Context.ACTIVITY_SERVICE);
}
List<RunningTaskInfo> rti = mActivityManager.getRunningTasks(1);
return homeList.contains(rti.get(0).topActivity.getPackageName());
}
/**
* 取得屬于桌面的利用的利用包名稱
* @return 返回包括所有包名的字符串列表
*/
private List<String> getHomes() {
List<String> names = new ArrayList<String>();
PackageManager packageManager = this.getPackageManager();
// 屬性
Intent intent = new Intent(Intent.ACTION_MAIN);
intent.addCategory(Intent.CATEGORY_HOME);
List<ResolveInfo> resolveInfo = packageManager.queryIntentActivities(intent,
PackageManager.MATCH_DEFAULT_ONLY);
for(ResolveInfo ri : resolveInfo) {
names.add(ri.activityInfo.packageName);
}
return names;
}
③好了,接下來我們需要每隔1段時間來進行1系列的判斷,比如:是不是在桌面,是不是已加載懸浮框,否則加載;
否則,如果加載了,就將這個懸浮框移除!這里我們使用handler~,由于不能在子線程直接更新UI,所以,你懂的
所以我們自己寫1個handler來完成上述的操作:
//定義1個更新界面的Handler
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;
}
}
};
④最后要做的1件事,就是重寫Service的onStartCommand( )方法了,就是做判斷,取出Intent中的
數據,判斷是需要添加懸浮框,還是要移除懸浮框!
@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);
}
好了,這個程序的實現流程就這是這樣,1次看不懂看多幾遍就可以了解了!
最后還獻上WindowManager的兩個經常使用實例吧:
③設置窗口全屏顯示:
getWindow().setFlags(WindowManager.LayoutParams.FLAG_FULLSCREEN,
WindowManager.LayoutParams.FLAG_FULLSCREEN);
④保持窗口打開,即屏幕常亮:
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他人寫的1篇blog:http://blog.csdn.net/chenyafei617/article/details/6577940
~好了,最后祝大家元宵節快樂~
生活不易,碼農辛苦
如果您覺得本網站對您的學習有所幫助,可以手機掃描二維碼進行捐贈