一、前言:
我手机从来不装这些东西,不过,有次看到同事的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应用图标在状态栏上显示,以及显示不同的图标》一起给出。