(Android)桌面悬浮窗

本文介绍了Android桌面悬浮窗的实现过程,包括所需权限、布局文件设置、悬浮窗样式定制、WindowManager的使用以及如何在服务中动态创建和移除悬浮窗。详细步骤包括判断当前界面是否为桌面、创建自定义布局、设置悬浮窗初始位置等。
摘要由CSDN通过智能技术生成

悬浮窗一直都觉得是个高大上的东西,但是当你剖析之后,就会发现其实也挺简单的,就是判断当前界面是否是桌面,然后开启一个Service
悬浮窗涉及到WindowManager,通过调用其中的几个方法:addView(添加悬浮窗)、removeView(移除悬浮窗)、updateViewLayout(更新悬浮窗);
首先就是申请权限:

<uses-permission android:name="android.permission.SYSTEM_ALERT_WINDOW" /> <!-- 使用SYSTEM_ALERT_WINDOW时必须要加 -->
<uses-permission android:name="android.permission.GET_TASKS"/>

然后就是布局文件:开启悬浮窗按钮

<RelativeLayout xmlns:android="http://schemas.android.com/apk/res/android"
    xmlns:tools="http://schemas.android.com/tools"
    android:layout_width="fill_parent"
    android:layout_height="fill_parent"
    tools:context=".MainActivity" >
    <Button
        android:id="@+id/start_float_window"
        android:layout_width="fill_parent"
        android:layout_height="wrap_content"
        android:text="start"/>
</RelativeLayout>

悬浮窗样式:样式可以根据自己需求设置的

<LinearLayout  xmlns:android="http://schemas.android.com/apk/res/android"
    android:id="@+id/small_window_layout"
   android:layout_width="60dip"
   android:layout_height="25dip"
   android:background="@mipmap/bg_small">
    <TextView 
        android:id="@+id/percent"
        android:layout_width="fill_parent"
        android:layout_height="fill_parent"
        android:gravity="center"
        android:textColor="#ffffff"/>
</LinearLayout>

对悬浮窗做一个自定义的动态布局:

public class FloatWindowView extends LinearLayout {
   public static int viewWidth;记录悬浮窗的宽度
   public static int viewHeight;记录悬浮窗的高度
    private static int statusBarHeight;记录系统状态栏的高度
   private WindowManager windowManager; 用于更新悬浮窗的位置
   private float xInScreen;记录当前手指位置在屏幕上的横坐标值
   private float yInScreen;记录当前手指位置在屏幕上的纵坐标值
   private float xDownInScreen;记录手指按下时在屏幕上的横坐标的值
   private float yDownInScreen;记录手指按下时在屏幕上的纵坐标的值
   private float xInView;//记录手指按下时在小悬浮窗的View上的横坐标的值
   private float yInView;//记录手指按下时在小悬浮窗的View上的纵坐标的值
   public FloatWindowView(Context context) {
      super(context);
      windowManager = (WindowManager) context.getSystemService(Context.WINDOW_SERVICE);
      LayoutInflater.from(context).inflate(R.layout.float_window, this);
      View view = findViewById(R.id.window_layout);
      viewWidth = view.getLayoutParams().width;
      viewHeight = view.getLayoutParams().height;
      //这里可以设置悬浮窗里面展示的内容
   }
   @Override
   public boolean onTouchEvent(MotionEvent event) {
      switch (event.getAction()) {
      case MotionEvent.ACTION_DOWN:
         // 手指按下时记录必要数据,纵坐标的值都需要减去状态栏高度
         xInView = event.getX();
         yInView = event.getY();
         xDownInScreen = event.getRawX();
         yDownInScreen = event.getRawY() - getStatusBarHeight();
         xInScreen = event.getRawX();
         yInScreen = event.getRawY() - getStatusBarHeight();
         break;
      case MotionEvent.ACTION_MOVE:
         xInScreen = event.getRawX();
         yInScreen = event.getRawY() - getStatusBarHeight();
         // 手指移动的时候更新小悬浮窗的位置
         updateViewPosition();
         break;
      default:
         break;
      }
      return true;
   }
   /**
    * 更新小悬浮窗在屏幕中的位置。
    */
   private void updateViewPosition() {
      mParams.x = (int) (xInScreen - xInView);
      mParams.y = (int) (yInScreen - yInView);
      windowManager.updateViewLayout(this, mParams);
   }
   /**
    * 用于获取状态栏的高度。
    * @return 返回状态栏高度的像素值。
    */
   private int getStatusBarHeight() {
      if (statusBarHeight == 0) {
         try {
            Class<?> c = Class.forName("com.android.internal.R$dimen");
            Object o = c.newInstance();
            Field field = c.getField("status_bar_height");
            int x = (Integer) field.get(o);
            statusBarHeight = getResources().getDimensionPixelSize(x);
         } catch (Exception e) {
            e.printStackTrace();
         }
      }
      return statusBarHeight;
   }
}

创建一个WindowManager:
创建悬浮窗,并设置悬浮窗显示的初始位置

public static void createSmallWindow(Context context) {
   WindowManager windowManager = getWindowManager(context);
   int screenWidth = windowManager.getDefaultDisplay().getWidth();
   int screenHeight = windowManager.getDefaultDisplay().getHeight();
   if (smallWindow == null) {
      smallWindow = new FloatWindowView(context);
      if (smallWindowParams == null) {
         smallWindowParams = new LayoutParams();
         smallWindowParams.type = LayoutParams.TYPE_PHONE;
         smallWindowParams.format = PixelFormat.RGBA_8888;
         smallWindowParams.flags = LayoutParams.FLAG_NOT_TOUCH_MODAL
               | LayoutParams.FLAG_NOT_FOCUSABLE;
         smallWindowParams.gravity = Gravity.LEFT | Gravity.TOP;
         smallWindowParams.width = FloatWindowView.viewWidth;
         smallWindowParams.height = FloatWindowView.viewHeight;
         smallWindowParams.x = screenWidth;
         smallWindowParams.y = screenHeight / 2;
      }
      smallWindow.setParams(smallWindowParams);
      windowManager.addView(smallWindow, smallWindowParams);
   }
}
public static void removeWindow(Context context) {
   if (smallWindow != null) {
      WindowManager windowManager = getWindowManager(context);
      windowManager.removeView(smallWindow);
      smallWindow = null;
   }
}
public static void updateUsedPercent(Context context) {
   if (smallWindow != null) {
    //通过findviewbyid,重新设置悬浮窗的数据显示
   }
}
public static boolean isWindowShowing() {
   return smallWindow != null;//判断桌面是否有悬浮窗
}

别忘了把这个写上:

/**
 * 如果WindowManager还未创建,则创建一个新的WindowManager返回。否则返回当前已创建的WindowManager。
 * @param context    必须为应用程序的Context.
 * @return WindowManager的实例,用于控制在屏幕上添加或移除悬浮窗。
 */
private static WindowManager getWindowManager(Context context) {
   if (mWindowManager == null) {
      mWindowManager = (WindowManager) context.getSystemService(Context.WINDOW_SERVICE);
   }
   return mWindowManager;
}
/**
 * 如果ActivityManager还未创建,则创建一个新的ActivityManager返回。否则返回当前已创建的ActivityManager。
 * @param context  可传入应用程序上下文。
 * @return ActivityManager的实例,用于获取手机可用内存。
 */
private static ActivityManager getActivityManager(Context context) {
   if (mActivityManager == null) {
      mActivityManager = (ActivityManager) context.getSystemService(Context.ACTIVITY_SERVICE);
   }
   return mActivityManager;
}

新建一个悬浮窗的后台服务,并继承Service:
1、判断当前界面是否是桌面,只有在桌面的时候才会开启悬浮窗:

/**
 * 判断当前界面是否是桌面
 */
private boolean isHome() {
   ActivityManager mActivityManager = (ActivityManager) getSystemService(Context.ACTIVITY_SERVICE);
   List<RunningTaskInfo> rti = mActivityManager.getRunningTasks(1);
   return getHomes().contains(rti.get(0).topActivity.getPackageName());
}
/**
 * 获得属于桌面的应用的应用包名称
 * @return 返回包含所有包名的字符串列表
 */
private List<String> getHomes() {
   List<String> names = new ArrayList<String>();
   PackageManager packageManager = this.getPackageManager();
   Intent intent = new Intent(Intent.ACTION_MAIN);
   intent.addCategory(Intent.CATEGORY_HOME);
   List<ResolveInfo> resolveInfo = packageManager.queryIntentActivities(intent,
         PackageManager.MATCH_DEFAULT_ONLY);
   for (ResolveInfo ri : resolveInfo) {
      names.add(ri.activityInfo.packageName);
   }
   return names;
}

2、开启一个线程Handler去创建和移除悬浮窗
在线程中执行判断:

// 当前界面是桌面,且没有悬浮窗显示,则创建悬浮窗。
if (isHome() && !MyWindowManager.isWindowShowing()) {
   handler.post(new Runnable() {
      @Override
      public void run() {
         MyWindowManager.createWindow(getApplicationContext());
      }
   });
}
// 当前界面不是桌面,且有悬浮窗显示,则移除悬浮窗。
else if (!isHome() && MyWindowManager.isWindowShowing()) {
   handler.post(new Runnable() {
      @Override
      public void run() {
         MyWindowManager.removeSmallWindow(getApplicationContext());
         MyWindowManager.removeBigWindow(getApplicationContext());
      }
   });
}

然后在服务中开启线程去执行,
由于代码偏多,所以只展示了一部分,说的不够全面,还望理解,不过大致意思就是这样的
完整资源:http://download.csdn.net/detail/qq_36159785/9884890

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值