使用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 = 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);
                        }
                    }
    
                }
            }
        }
    
    }
    看看就行了
  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


免責聲明!

本站轉載的文章為個人學習借鑒使用,本站對版權不負任何法律責任。如果侵犯了您的隱私權益,請聯系本站郵箱yoyou2525@163.com刪除。



 
粵ICP備18138465號   © 2018-2025 CODEPRJ.COM