现在的位置: 首页 > 综合 > 正文

Android类似360,QQ管家那样的悬浮窗

2017年12月15日 ⁄ 综合 ⁄ 共 4637字 ⁄ 字号 评论关闭

一、前言:

        我手机从来不装这些东西,不过,有次看到同事的android手机上,有个QQ管家在桌面上浮着,同事拖动管家时,管家就变成一只鸟,桌面下方还有个弹弓,桌面顶部有只乌鸦,把管家也就是鸟拖动到弹弓那,然后,松手,鸟就飞出去。这个过程是动画过程,做的事,实际上是清楚内存。

二:原理:

        其实,没什么原理,用到的就是WindowManager以及WindowManager.LayoutParams,对这个LayoutParams做文章,当设置为属性后,然后,创建一个View,将这个View添加到WindowManager中就行。

package com.chris.floats.window;

import android.os.Bundle;
import android.util.DisplayMetrics;
import android.view.Gravity;
import android.view.WindowManager;
import android.app.Activity;
import android.content.Context;

public class MainActivity extends Activity {

	private static WindowManager mWindowMgr = null;
	private WindowManager.LayoutParams mWindowMgrParams = null;
	private static FloatsWindowView mFloatsWindowView = null;
	
	@Override
	protected void onCreate(Bundle savedInstanceState) {
		super.onCreate(savedInstanceState);
		setContentView(R.layout.activity_main);
	}

	/*
	 * 显示应用主界面时,去除悬浮层
	 */
	@Override
	public void onWindowFocusChanged(boolean hasFocus) {
		if(hasFocus){
			if(mFloatsWindowView != null){
				mWindowMgr.removeView(mFloatsWindowView);
				mFloatsWindowView = null;
			}
		}else{
			getWindowLayout();
		}
	}

	private void initParams(){
		DisplayMetrics dm = getResources().getDisplayMetrics();
		mWindowMgrParams.x = dm.widthPixels - 136;
		mWindowMgrParams.y = 300;
		mWindowMgrParams.width = 136;
		mWindowMgrParams.height = 136;
	}

	private void getWindowLayout(){
		if(mFloatsWindowView == null){
			mWindowMgr = (WindowManager)getBaseContext().getSystemService(Context.WINDOW_SERVICE);
			mWindowMgrParams = new WindowManager.LayoutParams();
			
			/*
			 *  2003 在指悬浮在所有界面之上
			 *  (4.0+系统中,在下拉菜单下面,而在2.3中,在上拉菜单之上)
			 */
			mWindowMgrParams.type = 2003;
			mWindowMgrParams.format = 1;
			
			/*
			 * 代码实际是wmParams.flags |= FLAG_NOT_FOCUSABLE;
			 * 40的由来是wmParams的默认属性(32)+ FLAG_NOT_FOCUSABLE(8)
			 */
			mWindowMgrParams.flags = 40;
			mWindowMgrParams.gravity = Gravity.LEFT | Gravity.TOP;
			initParams();
			
			mFloatsWindowView = new FloatsWindowView(this);
			mWindowMgr.addView(mFloatsWindowView, mWindowMgrParams);
		}
	}
}

上面代码,主要在getWindowLayout函数中,最后两行就是创建一个View,并加入到WindowManager中。

继承View的悬浮View:

package com.chris.floats.window;

import android.content.Context;
import android.content.Intent;
import android.graphics.drawable.AnimationDrawable;
import android.util.AttributeSet;
import android.util.DisplayMetrics;
import android.view.Gravity;
import android.view.MotionEvent;
import android.view.View;
import android.view.ViewTreeObserver.OnPreDrawListener;
import android.view.WindowManager;

public class FloatsWindowView extends View {

	private Context mContext = null;
	private WindowManager mWindowMgr = null;
	private WindowManager.LayoutParams mWindowMgrParams = null;
	private AnimationDrawable mAnimationDrawable = null;
	
	private int iPosX = 0;
	private int iPosY = 0;
	private int iLastPosX = 0;
	private int iLastPosY = 0;
	private boolean bMoved = false;
	   
	public FloatsWindowView(Context context) {
		this(context, null, 0);
	}
	public FloatsWindowView(Context context, AttributeSet attrs) {
		this(context, attrs, 0);
	}
	public FloatsWindowView(Context context, AttributeSet attrs, int defStyle) {
		super(context, attrs, defStyle);
		
		mContext = context;
		mWindowMgr = (WindowManager)getContext().getApplicationContext().getSystemService("window");
		mWindowMgrParams = new WindowManager.LayoutParams();
		initParams();
		
		mAnimationDrawable = new AnimationDrawable();
		for(int i = 0; i < 4; i++){
			int id = getResources().getIdentifier("a"+ i, "drawable", mContext.getPackageName());
			mAnimationDrawable.addFrame(getResources().getDrawable(id), 100);
		}
		mAnimationDrawable.setOneShot(false);
		this.setBackgroundDrawable(mAnimationDrawable);
		
		OnPreDrawListener listener = new OnPreDrawListener(){
			@Override
			public boolean onPreDraw() {
				mAnimationDrawable.start();
				return true;
			}
		};
		this.getViewTreeObserver().addOnPreDrawListener(listener);
	}
	
	private void initParams(){
		DisplayMetrics dm = getResources().getDisplayMetrics();
		mWindowMgrParams.x = dm.widthPixels - 136;
		mWindowMgrParams.y = 300;
		mWindowMgrParams.width = 136;
		mWindowMgrParams.height = 136;
	}
	
	@Override
	public boolean onTouchEvent(MotionEvent event) {
		
		switch(event.getAction()){
		case MotionEvent.ACTION_DOWN:
			iPosX = (int)event.getX();
			iPosY = (int)event.getY();
			bMoved = false;
			break;
			
		case MotionEvent.ACTION_MOVE:
			bMoved = true;
			iLastPosX = (int)event.getX();
			iLastPosY = (int)event.getY();
			updatePostion(iLastPosX - iPosX, iLastPosY - iPosY);
			break;
			
		case MotionEvent.ACTION_UP:
			if(!bMoved){
				Intent it=new Intent(mContext, MainActivity.class);
				mContext.startActivity(it);
			}
			break;
			
		default:
			break;
		}
		return true;
	}

	private void updatePostion(int x, int y){
		mWindowMgrParams.type = 2003;
		mWindowMgrParams.format = 1;
		mWindowMgrParams.flags = 40;
		mWindowMgrParams.gravity = Gravity.LEFT | Gravity.TOP;
		mWindowMgrParams.x += x;
		mWindowMgrParams.y += y;
		mWindowMgr.updateViewLayout(this, mWindowMgrParams);
	}
}

之所以将updatePosition中的参数与Activity中设置一样,是为了确保在MOVE时,造成相对位置的不一样,而导致闪砾,大家要是不理解,可以实验下。

三、小结:

这篇文章实现了简单的悬浮窗口动画效果,如果要想做成像360,QQ管家那样,还需要一些其它的操作:

1. 比如启动一个后台服务来监控系统信息;

2. ACTION_DOWN时,修改悬浮窗口上的图片;

3. ACTION_MOVE时窗口跟随;

4. ACTION_UP时,创建一个线程,来完成释放后,向上运动的动画过程等;

 

本篇源码包,会将在下篇文章《Android应用图标在状态栏上显示,以及显示不同的图标》一起给出。

抱歉!评论已关闭.