Android桌面悬浮窗

这是一篇Android悬浮窗的介绍,能够实现例如360,QQ管家那样的悬浮窗效果。后台运行了一个服务,用于控制在运行非桌面app情况下隐藏悬浮窗。



下面先上Demo截图:





上图就是所实现的简单悬浮窗示例,当然可以根据项目需要改变其相应布局。
给出Demo的源代码地址:http://download.csdn.net/detail/shinay/4450976


下面是创建悬浮窗的方法:

	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"/>





最后给出源代码地址: http://download.csdn.net/detail/shinay/4450976

欢迎讨论,共同进步。




  • 5
    点赞
  • 64
    收藏
    觉得还不错? 一键收藏
  • 33
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论 33
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值