使用WindowManager添加View——悬浮窗口的基本原理
Android系统中的“窗口”类型虽然很多,但只有两大类是经常使用的:一是由系统进程管理的,称之为“系统窗口”;第二个就是由应用程序产生的,用于显示UI界面的“应用窗口”。如果大家熟悉WindowManagerService(窗口管理机制WMS)的话,那么一切都很简单。它是一个负责统筹管理所有窗口的一个服务,从始到终一直在运作。之所以扯上WMS,因为它才是大Boss,所有的窗口变化都要通知到它。而WindowManager虽然与它没有之间的关系,但是对它负责,所有信息会经过一定的途径传回到WMS中。额,跑题了,我们说的是WindowManager,它是一个接口类,它可以实现对view的管理,包括增加,更新和删除。
一、WindowManager
- 获取WindowManager
wManager = (WindowManager) getApplicationContext().getSystemService( Context.WINDOW_SERVICE);
在Activity和Service中都可以直接使用这个方法来获得WindowManager。其getSystemService返回的是一个WindowManagerImpl对象,这是一个存在于本地进程中的一个对象。而事实是WindowManagerImpl继承了WindowManager,而WindowManger继承了ViewManager。
- 设置WindowManager.LayoutParams
LayoutParams里面存放着的是窗口的属性,通过这个变量,可以为窗口赋予各式的属性。也可以改变它的属性值,来进行各种各样的操作,像悬浮窗口的拖动,拉伸等操作。
详细的属性表在:
http://www.cnblogs.com/shitianzeng/articles/2814050.html - WindowManager的操作
(1)窗口添加public void addView(View view, ViewGroup.LayoutParams params);
(2)窗口更新
public void updateViewLayout(View view, ViewGroup.LayoutParams params);
(3)窗口删除
public void removeView(View view);
以上的三个方法都存在于ViewManager中。
二、悬浮窗实例
例子设计:利用service打开悬浮窗,其中放着一个自定义的View,点击消除悬浮窗。
- 首先是Service,它是由Activity打开。先获得WindowManager,再配置属性
public class WindowService extends Service implements OnClickListener { private WindowManager wManager;// 窗口管理者 private WindowManager.LayoutParams mParams;// 窗口的属性 private MyView myView; private boolean flag = true; @Override public IBinder onBind(Intent intent) { // TODO Auto-generated method stub return null; } @Override public void onCreate() { // TODO Auto-generated method stub wManager = (WindowManager) getApplicationContext().getSystemService( Context.WINDOW_SERVICE); mParams = new WindowManager.LayoutParams(); mParams.type = WindowManager.LayoutParams.TYPE_SYSTEM_ALERT;// 系统提示window mParams.format = PixelFormat.TRANSLUCENT;// 支持透明 //mParams.format = PixelFormat.RGBA_8888; mParams.flags |= WindowManager.LayoutParams.FLAG_NOT_FOCUSABLE;// 焦点 mParams.width = 490;//窗口的宽和高 mParams.height = 160; mParams.x = 0;//窗口位置的偏移量 mParams.y = 0; //mParams.alpha = 0.1f;//窗口的透明度 myView = new MyView(this); myView.setOnClickListener(this); super.onCreate(); } @Override public int onStartCommand(Intent intent, int flags, int startId) { // TODO Auto-generated method stub if (flag) { flag = false; wManager.addView(myView, mParams);//添加窗口 } return super.onStartCommand(intent, flags, startId); } @Override public void onDestroy() { // TODO Auto-generated method stub if (myView.getParent() != null) wManager.removeView(myView);//移除窗口 super.onDestroy(); } @Override public void onClick(View v) { // TODO Auto-generated method stub if (v.equals(myView)) { flag = true; if (myView.getParent() != null) wManager.removeView(myView);//移除窗口 } } }
- 一个自定义的view
很早以前写的surfaceView,里面包括文字图片的两个显示动画
@SuppressLint("WrongCall") public class MyView extends SurfaceView implements SurfaceHolder.Callback { private static int span = 5; private MyThread wtf; private Paint paint; int sleepSpan = 150; // 动画的时延ms Bitmap logo_s, logo_l; // logo图片引用 int width_s; // 图片大小 int height_s; float currentX_s; // 图片位置 float currentY_s; float currentX_l; // 图片位置 float currentY_l; private Rect src; private RectF dst; private int currentAlpha = 0; public MyView(Context context) { super(context); this.getHolder().addCallback(this);// 设置生命周期回调接口的实现者 paint = new Paint();// 创建画笔 paint.setAntiAlias(true);// 打开抗锯齿 } @Override public void surfaceCreated(SurfaceHolder holder) { logo_s = BitmapFactory .decodeResource(getResources(), R.drawable.logo_s); logo_l = BitmapFactory .decodeResource(getResources(), R.drawable.logo_l); // TODO Auto-generated method stub width_s = logo_s.getWidth(); height_s = logo_s.getHeight(); src = new Rect(0, 0, 0, height_s); // 大图片的位置 currentX_s = 5; currentY_s = 5; dst = new RectF(currentX_s, currentY_s, currentX_s, currentY_s + height_s); currentX_l = currentX_s + width_s - logo_l.getWidth(); // 小图片的位置 currentY_l = currentY_s + height_s; currentAlpha = 0; wtf = new MyThread(); wtf.start(); } @Override public void surfaceChanged(SurfaceHolder holder, int format, int width, int height) { // TODO Auto-generated method stub } @Override public void surfaceDestroyed(SurfaceHolder holder) { // TODO Auto-generated method stub } @Override public void onDraw(Canvas canvas) { // TODO Auto-generated method stub // 绘制黑填充矩形清背景 super.onDraw(canvas); paint.setAlpha(120);// 设置不透明度为255 paint.setColor(Color.BLACK);// 设置画笔颜色 canvas.drawColor(Color.BLACK); // 进行平面贴图 if (logo_s == null || logo_l == null) return; src.right += span; dst.right += span; canvas.drawBitmap(logo_s, src, dst, paint); paint.setAlpha(currentAlpha); canvas.drawBitmap(logo_l, currentX_l, currentY_l, paint); // canvas.drawBitmap(bitmap, src, dst, paint); /* * Rect src = new Rect(x1, y2, cx1,cy1); Rect dst = new Rect(x2, y2, * cx2, cy2); 第一个矩形,是你想截取的bitmap里面的哪一段。 第二个矩形,是你想显示在屏幕上的什么位置。 * 两个矩形可以不一样大小,在绘制的时候,会自动拉伸。 */ } class MyThread extends Thread { public void run() { SurfaceHolder mholder = MyView.this.getHolder();// 获取回调接口 // 绘制tatans try { sleep(500); } catch (InterruptedException e1) { // TODO Auto-generated catch block e1.printStackTrace(); } for (int i = 0; i <= width_s / span; i++) { Canvas canvas = mholder.lockCanvas();// 获取画布 try { synchronized (mholder) // 同步 { onDraw(canvas);// 进行 } sleep(20); } catch (Exception e) { // TODO Auto-generated catch block e.printStackTrace(); } finally { if (canvas != null) { mholder.unlockCanvasAndPost(canvas); } } } // 绘制天坦智慧 for (int i = 0; i <= 25; i++) { currentAlpha = i * 10; Canvas canvas = mholder.lockCanvas();// 获取画布 try { synchronized (mholder) // 同步 { onDraw(canvas);// 进行 } sleep(25); } catch (Exception e) { // TODO Auto-generated catch block e.printStackTrace(); } finally { if (canvas != null) { mholder.unlockCanvasAndPost(canvas); } } } } } }
- Activity中,点击打开service
@Override public boolean onTouchEvent(MotionEvent event) { // TODO Auto-generated method stub Log.d("CPACM","onTouchEvent"); Intent intent = new Intent(); intent.setClass(this, WindowService.class); startService(intent); this.finish(); return super.onTouchEvent(event); }
- manifest.xml
<uses-permission android:name="android.permission.SYSTEM_ALERT_WINDOW" /> <!-- 使用SYSTEM_ALERT_WINDOW时必须要加 -->
- 效果图
三、结束语
学到越深,发现需要学的越多!
========================================
作者:cpacm
出处:(http://www.cnblogs.com/cpacm/p/4087690.html)