首页 > 代码库 > 使用WindowManager添加View——悬浮窗口的基本原理

使用WindowManager添加View——悬浮窗口的基本原理

Android系统中的“窗口”类型虽然很多,但只有两大类是经常使用的:一是由系统进程管理的,称之为“系统窗口”;第二个就是由应用程序产生的,用于显示UI界面的“应用窗口”。如果大家熟悉WindowManagerService(窗口管理机制WMS)的话,那么一切都很简单。它是一个负责统筹管理所有窗口的一个服务,从始到终一直在运作。之所以扯上WMS,因为它才是大Boss,所有的窗口变化都要通知到它。而WindowManager虽然与它没有之间的关系,但是对它负责,所有信息会经过一定的途径传回到WMS中。额,跑题了,我们说的是WindowManager,它是一个接口类,它可以实现对view的管理,包括增加,更新和删除。

 

一、WindowManager

  1. 获取WindowManager
    wManager = (WindowManager) getApplicationContext().getSystemService(                Context.WINDOW_SERVICE);

    在Activity和Service中都可以直接使用这个方法来获得WindowManager。其getSystemService返回的是一个WindowManagerImpl对象,这是一个存在于本地进程中的一个对象。而事实是WindowManagerImpl继承了WindowManager,而WindowManger继承了ViewManager。

  2. 设置WindowManager.LayoutParams
    LayoutParams里面存放着的是窗口的属性,通过这个变量,可以为窗口赋予各式的属性。也可以改变它的属性值,来进行各种各样的操作,像悬浮窗口的拖动,拉伸等操作。
    详细的属性表在:
    http://www.cnblogs.com/shitianzeng/articles/2814050.html
  3. 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,点击消除悬浮窗。

  1. 首先是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);//移除窗口        }    }}

     

  2. 一个自定义的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 = http://www.mamicode.com/new Rect(x1, y2, cx1,cy1); Rect dst = new Rect(x2, y2,>*/    }    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);                    }                }            }        }    }}
    看看就行了
  3. 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);    }
  4. manifest.xml
    <uses-permission android:name="android.permission.SYSTEM_ALERT_WINDOW" /> <!-- 使用SYSTEM_ALERT_WINDOW时必须要加 -->

     

  5. 效果图
     

三、结束语
学到越深,发现需要学的越多

 

 

========================================

 

作者:cpacm
出处:(http://www.cnblogs.com/cpacm/p/4087690.html)

使用WindowManager添加View——悬浮窗口的基本原理