转自:http://blog.csdn.net/shinay/article/details/7783276
下面是创建悬浮窗的方法:
- private boolean isAdded = false; // 是否已增加悬浮窗
- private static WindowManager wm;
- private static WindowManager.LayoutParams params;
- private Button btn_floatView;
- /**
- * 创建悬浮窗
- */
- private void createFloatView() {
- btn_floatView = new Button(getApplicationContext());
- btn_floatView.setText("悬浮窗");
- wm = (WindowManager) getApplicationContext()
- .getSystemService(Context.WINDOW_SERVICE);
- params = new WindowManager.LayoutParams();
- // 设置window type
- params.type = WindowManager.LayoutParams.TYPE_SYSTEM_ALERT;
- /*
- * 如果设置为params.type = WindowManager.LayoutParams.TYPE_PHONE;
- * 那么优先级会降低一些, 即拉下通知栏不可见
- */
- params.format = PixelFormat.RGBA_8888; // 设置图片格式,效果为背景透明
- // 设置Window flag
- params.flags = WindowManager.LayoutParams.FLAG_NOT_TOUCH_MODAL
- | WindowManager.LayoutParams.FLAG_NOT_FOCUSABLE;
- /*
- * 下面的flags属性的效果形同“锁定”。
- * 悬浮窗不可触摸,不接受任何事件,同时不影响后面的事件响应。
- wmParams.flags=LayoutParams.FLAG_NOT_TOUCH_MODAL
- | LayoutParams.FLAG_NOT_FOCUSABLE
- | LayoutParams.FLAG_NOT_TOUCHABLE;
- */
- // 设置悬浮窗的长得宽
- params.width = 100;
- params.height = 100;
- // 设置悬浮窗的Touch监听
- btn_floatView.setOnTouchListener(new OnTouchListener() {
- int lastX, lastY;
- int paramX, paramY;
- 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;
- // 更新悬浮窗位置
- wm.updateViewLayout(btn_floatView, params);
- break;
- }
- return true;
- }
- });
- wm.addView(btn_floatView, params);
- isAdded = true;
- }
做完这步,基本上就可以在桌面显示一个悬浮窗并且可以自由拖动了。
如果想要控制它在桌面显示,而进入到别的应用程序时隐藏它的话,就需要用一个后台运行的Service来实现了。
首先需要先获取到手机上的桌面程序的包名(桌面程序指的是按下HOME键所列出的程序,如go桌面等):
- /**
- * 获得属于桌面的应用的应用包名称
- * @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;
- }
接着是判断当前运行的Activity是否为桌面应用程序,这里需要用到ActivityManager:
- /**
- * 判断当前界面是否是桌面
- */
- 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());
- }
有了上面两个方法,就可以实现这个功能了。只不过我们需要定时去判断,例如可以用一个Handler每一秒去检查一次:
- private Handler mHandler = new Handler() {
- @Override
- public void handleMessage(Message msg) {
- switch(msg.what) {
- case HANDLE_CHECK_ACTIVITY:
- if(isHome()) {
- if(!isAdded) {
- wm.addView(btn_floatView, params);
- isAdded = true;
- }
- } else {
- if(isAdded) {
- wm.removeView(btn_floatView);
- isAdded = false;
- }
- }
- mHandler.sendEmptyMessageDelayed(HANDLE_CHECK_ACTIVITY, 1000);
- break;
- }
- }
- };
在我的Demo中,悬浮窗都是通过Service来控制的,那么我的启动与隐藏就都扔给Service处理就OK。
- public void onClick(View v) {
- switch(v.getId()) {
- case R.id.btn_show:
- Intent show = new Intent(this, FloatingWindowService.class);
- show.putExtra(FloatingWindowService.OPERATION, FloatingWindowService.OPERATION_SHOW);
- startService(show);
- break;
- case R.id.btn_hide:
- Intent hide = new Intent(this, FloatingWindowService.class);
- hide.putExtra(FloatingWindowService.OPERATION, FloatingWindowService.OPERATION_HIDE);
- startService(hide);
- break;
- }
- }
在Service里的onStart方法中,只需要根据传过来的操作参数,对handler检查进行操作即可。
- @Override
- public void onStart(Intent intent, int startId) {
- super.onStart(intent, 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;
- }
- }
另外:需要增加以下权限!!
- <uses-permission android:name="android.permission.SYSTEM_ALERT_WINDOW"/>
- <uses-permission android:name="android.permission.GET_TASKS"/>
下面是创建悬浮窗的方法:
- private boolean isAdded = false; // 是否已增加悬浮窗
- private static WindowManager wm;
- private static WindowManager.LayoutParams params;
- private Button btn_floatView;
- /**
- * 创建悬浮窗
- */
- private void createFloatView() {
- btn_floatView = new Button(getApplicationContext());
- btn_floatView.setText("悬浮窗");
- wm = (WindowManager) getApplicationContext()
- .getSystemService(Context.WINDOW_SERVICE);
- params = new WindowManager.LayoutParams();
- // 设置window type
- params.type = WindowManager.LayoutParams.TYPE_SYSTEM_ALERT;
- /*
- * 如果设置为params.type = WindowManager.LayoutParams.TYPE_PHONE;
- * 那么优先级会降低一些, 即拉下通知栏不可见
- */
- params.format = PixelFormat.RGBA_8888; // 设置图片格式,效果为背景透明
- // 设置Window flag
- params.flags = WindowManager.LayoutParams.FLAG_NOT_TOUCH_MODAL
- | WindowManager.LayoutParams.FLAG_NOT_FOCUSABLE;
- /*
- * 下面的flags属性的效果形同“锁定”。
- * 悬浮窗不可触摸,不接受任何事件,同时不影响后面的事件响应。
- wmParams.flags=LayoutParams.FLAG_NOT_TOUCH_MODAL
- | LayoutParams.FLAG_NOT_FOCUSABLE
- | LayoutParams.FLAG_NOT_TOUCHABLE;
- */
- // 设置悬浮窗的长得宽
- params.width = 100;
- params.height = 100;
- // 设置悬浮窗的Touch监听
- btn_floatView.setOnTouchListener(new OnTouchListener() {
- int lastX, lastY;
- int paramX, paramY;
- 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;
- // 更新悬浮窗位置
- wm.updateViewLayout(btn_floatView, params);
- break;
- }
- return true;
- }
- });
- wm.addView(btn_floatView, params);
- isAdded = true;
- }
做完这步,基本上就可以在桌面显示一个悬浮窗并且可以自由拖动了。
如果想要控制它在桌面显示,而进入到别的应用程序时隐藏它的话,就需要用一个后台运行的Service来实现了。
首先需要先获取到手机上的桌面程序的包名(桌面程序指的是按下HOME键所列出的程序,如go桌面等):
- /**
- * 获得属于桌面的应用的应用包名称
- * @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;
- }
接着是判断当前运行的Activity是否为桌面应用程序,这里需要用到ActivityManager:
- /**
- * 判断当前界面是否是桌面
- */
- 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());
- }
有了上面两个方法,就可以实现这个功能了。只不过我们需要定时去判断,例如可以用一个Handler每一秒去检查一次:
- private Handler mHandler = new Handler() {
- @Override
- public void handleMessage(Message msg) {
- switch(msg.what) {
- case HANDLE_CHECK_ACTIVITY:
- if(isHome()) {
- if(!isAdded) {
- wm.addView(btn_floatView, params);
- isAdded = true;
- }
- } else {
- if(isAdded) {
- wm.removeView(btn_floatView);
- isAdded = false;
- }
- }
- mHandler.sendEmptyMessageDelayed(HANDLE_CHECK_ACTIVITY, 1000);
- break;
- }
- }
- };
在我的Demo中,悬浮窗都是通过Service来控制的,那么我的启动与隐藏就都扔给Service处理就OK。
- public void onClick(View v) {
- switch(v.getId()) {
- case R.id.btn_show:
- Intent show = new Intent(this, FloatingWindowService.class);
- show.putExtra(FloatingWindowService.OPERATION, FloatingWindowService.OPERATION_SHOW);
- startService(show);
- break;
- case R.id.btn_hide:
- Intent hide = new Intent(this, FloatingWindowService.class);
- hide.putExtra(FloatingWindowService.OPERATION, FloatingWindowService.OPERATION_HIDE);
- startService(hide);
- break;
- }
- }
在Service里的onStart方法中,只需要根据传过来的操作参数,对handler检查进行操作即可。
- @Override
- public void onStart(Intent intent, int startId) {
- super.onStart(intent, 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;
- }
- }
另外:需要增加以下权限!!
- <uses-permission android:name="android.permission.SYSTEM_ALERT_WINDOW"/>
- <uses-permission android:name="android.permission.GET_TASKS"/>