• 使用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,里面包括文字图片的两个显示动画
    3. @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);
      }
      }

      }
      }
      }

      }

      看看就行了


      复制代码
      @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);
                          }
                      }
      
                  }
              }
          }
      
      }
      复制代码
    4. 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);
          }
      复制代码
    5. manifest.xml
      <uses-permission android:name="android.permission.SYSTEM_ALERT_WINDOW" /> <!-- 使用SYSTEM_ALERT_WINDOW时必须要加 -->
    6. 效果图
        

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

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

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

  • 相关阅读:
    如何选择RabbitMQ的消息保存方式?
    一次Flannel和Docker网络不通定位问题
    flannel
    vsftp设置不同用户登录ftp的根目录不同
    nginx反向代理服务器获取不到端口的问题的解决办法
    RabbitMQ 内存控制 硬盘控制
    -bash: fork: Cannot allocate memory
    redis info
    nginx第三方模块---nginx-sticky-module的使用(基于cookie的会话保持)
    Haproxy的三种保持客户端会话保持方式
  • 原文地址:https://www.cnblogs.com/Alex80/p/12989975.html
Copyright © 2020-2023  润新知