在一些场合里,我们使用悬浮窗会有很大的便利,比如IOS系统的悬浮窗,360或者其他手机卫士的悬浮窗等等。
本篇博客,我们创造出两个悬浮窗,通过点击小悬浮窗打开或者关闭大悬浮窗。
代码如下:
在这之前,我们需要在manifest中申请权限:
<uses-permission android:name="android.permission.SYSTEM_ALERT_WINDOW" />
1
并且,悬浮窗这个权限我们需要手动在手机找到应用权限管理,允许这个权限才行
小悬浮窗的界面代码float_normal_view.xml
<?xml version="1.0" encoding="utf-8"?> <LinearLayout xmlns:android="http://schemas.android.com/apk/res/android" android:layout_width="wrap_content" android:layout_height="wrap_content" android:id="@+id/ll_float_normal" android:gravity="center" android:orientation="vertical"> <ImageView android:id="@+id/iv_show_control_view" android:layout_gravity="center" android:background="@drawable/float_bg" android:layout_width="65dp" android:orientation="vertical" android:alpha="0.3" android:layout_height="65dp" > </ImageView> </LinearLayout>
大悬浮窗的界面代码float_window_big.xml:
<?xml version="1.0" encoding="utf-8"?> <LinearLayout xmlns:android="http://schemas.android.com/apk/res/android" android:id="@+id/big_window_layout" android:layout_width="200dip" android:layout_height="100dip" android:background="@android:color/holo_green_dark" android:orientation="vertical" > <Button android:id="@+id/close" android:layout_width="100dip" android:layout_height="40dip" android:layout_gravity="center_horizontal" android:layout_marginTop="12dip" android:text="关闭悬浮窗" /> <Button android:id="@+id/back" android:layout_width="100dip" android:layout_height="40dip" android:layout_gravity="center_horizontal" android:text="主页" /> </LinearLayout>
入口activity(MainActivity ):
public class MainActivity extends Activity { MyWindowManager myWindowManager; @Override protected void onCreate(Bundle savedInstanceState) { super.onCreate(savedInstanceState); if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.M) { if(!Settings.canDrawOverlays(getApplicationContext())) { //启动Activity让用户授权 Intent intent = new Intent(Settings.ACTION_MANAGE_OVERLAY_PERMISSION); intent.setData(Uri.parse("package:" + getPackageName())); startActivityForResult(intent,100); } } setContentView(R.layout.activity_main); myWindowManager = MyWindowManager.getInstance(); myWindowManager.createNormalView(this.getApplicationContext()); } }
悬浮窗管理器MyWindowManager:
public class MyWindowManager { private FloatNormalView normalView; private Context mContext; private static MyWindowManager instance; private WindowManager windowManager; private MyWindowManager() { } public static MyWindowManager getInstance() { if (instance == null) instance = new MyWindowManager(); return instance; } private WindowManager getWindowManager(Context context) { if (windowManager == null) windowManager = (WindowManager) context.getSystemService(Context.WINDOW_SERVICE); mContext=context; return windowManager; } /** * 判断小悬浮窗是否存在 * * @return */ public boolean isNormalViewExists() { return normalView != null; } /** * 创建小型悬浮窗 */ public void createNormalView(Context context) { if (normalView == null) { normalView = new FloatNormalView(context); } } /** * 移除悬浮窗 * * @param context */ public void removeNormalView(Context context) { if (normalView != null) { windowManager.removeView(normalView); normalView = null; } } FloatWindowBigView bigWindow; private WindowManager.LayoutParams bigWindowParams; public void createBigWindow(Context context) { WindowManager windowManager = getWindowManager(context); int screenWidth = windowManager.getDefaultDisplay().getWidth(); int screenHeight = windowManager.getDefaultDisplay().getHeight(); if (bigWindow == null) { bigWindow = new FloatWindowBigView(context); if (bigWindowParams == null) { bigWindowParams = new WindowManager.LayoutParams(); bigWindowParams.x = screenWidth / 2 - FloatWindowBigView.viewWidth / 2; bigWindowParams.y = screenHeight / 2 - FloatWindowBigView.viewHeight / 2; if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.O) { bigWindowParams.type =WindowManager.LayoutParams.TYPE_APPLICATION_OVERLAY; } else { bigWindowParams.type = WindowManager.LayoutParams.TYPE_PHONE; } bigWindowParams.format = PixelFormat.RGBA_8888; bigWindowParams.gravity = Gravity.LEFT | Gravity.TOP; bigWindowParams.width = FloatWindowBigView.viewWidth; bigWindowParams.height = FloatWindowBigView.viewHeight; } windowManager.addView(bigWindow, bigWindowParams); } } public void removeBigWindow(Context context) { if (bigWindow != null) { WindowManager windowManager = getWindowManager(context); windowManager.removeView(bigWindow); bigWindow = null; } } }
小悬浮窗FloatNormalView:
public class FloatNormalView extends LinearLayout implements View.OnTouchListener { private final static String TAG="FloatNormalView"; /** * 记录小悬浮窗的宽度 */ public static int viewWidth; /** * 记录小悬浮窗的高度 */ public static int viewHeight; /** * 记录系统状态栏的高度 */ private static int statusBarHeight; /** * 用于更新小悬浮窗的位置 */ private WindowManager windowManager; /** * 小悬浮窗的参数 */ private WindowManager.LayoutParams mParams; /** * 记录当前手指位置在屏幕上的横坐标值 */ private float xInScreen,xInitScreen; /** * 记录当前手指位置在屏幕上的纵坐标值 */ private float yInScreen,yInitScreen; /** * 记录手指按下时在屏幕上的横坐标的值 */ private float xDownInScreen; /** * 记录手指按下时在屏幕上的纵坐标的值 */ private float yDownInScreen; /** * 记录手指按下时在小悬浮窗的View上的横坐标的值 */ private float xInView; /** * 记录手指按下时在小悬浮窗的View上的纵坐标的值 */ private float yInView; public FloatNormalView(Context context) { super(context); windowManager = (WindowManager) context.getSystemService(Context.WINDOW_SERVICE); LayoutInflater.from(context).inflate(R.layout.float_normal_view, this); View view = findViewById(R.id.ll_float_normal); viewWidth = view.getLayoutParams().width; viewHeight = view.getLayoutParams().height; initLayoutParams(); } OnClickListener circleClickListener=new OnClickListener() { @Override public void onClick(View view) { MyWindowManager.getInstance().createBigWindow(getContext()); MyWindowManager.getInstance().removeNormalView(getContext()); } }; /** * 初始化参数 */ private void initLayoutParams() { //屏幕宽高 int screenWidth = windowManager.getDefaultDisplay().getWidth(); int screenHeight = windowManager.getDefaultDisplay().getHeight(); mParams = new WindowManager.LayoutParams(); // FLAG_NOT_TOUCH_MODAL不阻塞事件传递到后面的窗口 // FLAG_NOT_FOCUSABLE 悬浮窗口较小时,后面的应用图标由不可长按变为可长按,不设置这个flag的话,home页的划屏会有问题 mParams.flags = WindowManager.LayoutParams.FLAG_NOT_FOCUSABLE | WindowManager.LayoutParams.FLAG_NOT_TOUCH_MODAL; //悬浮窗默认显示的位置 mParams.gravity = Gravity.START | Gravity.TOP; //指定位置 mParams.x = screenWidth - viewWidth * 2; mParams.y = screenHeight / 2 + viewHeight * 2; //悬浮窗的宽高 mParams.width = WindowManager.LayoutParams.WRAP_CONTENT; mParams.height = WindowManager.LayoutParams.WRAP_CONTENT; mParams.format = PixelFormat.TRANSPARENT; if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.O) { mParams.type =WindowManager.LayoutParams.TYPE_APPLICATION_OVERLAY; } else { mParams.type = WindowManager.LayoutParams.TYPE_PHONE; } mParams.type = WindowManager.LayoutParams.TYPE_APPLICATION_OVERLAY; windowManager.addView(this, mParams); } @Override public boolean onTouchEvent(MotionEvent event) { Log.i(TAG,"hsz--->onTouchEvent:"+event.getAction()); switch (event.getAction()) { case MotionEvent.ACTION_DOWN: // 手指按下时记录必要数据,纵坐标的值都需要减去状态栏高度 xInView =xInitScreen= event.getX(); yInView =yInitScreen= 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; case MotionEvent.ACTION_UP: // 如果手指离开屏幕时,xDownInScreen和xInScreen相等,且yDownInScreen和yInScreen相等,则视为触发了单击事件。 if (xDownInScreen == xInScreen && yDownInScreen == yInScreen) { openOrCloseControlView(); } Float distanceX=event.getX()-xInitScreen; Float distanceY=event.getY()-yInitScreen; if(distanceX==0 && distanceY==0){ MyWindowManager.getInstance().createBigWindow(getContext()); MyWindowManager.getInstance().removeNormalView(getContext()); } break; default: break; } return true; } /** * 将小悬浮窗的参数传入,用于更新小悬浮窗的位置。 * * @param params 小悬浮窗的参数 */ public void setParams(WindowManager.LayoutParams params) { mParams = params; } /** * 更新小悬浮窗在屏幕中的位置。 */ 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; } @Override public boolean onTouch(View v, MotionEvent event) { return false; } }
大悬浮窗FloatWindowBigView:
public class FloatWindowBigView extends LinearLayout { /** * 记录大悬浮窗的宽度 */ public static int viewWidth; /** * 记录大悬浮窗的高度 */ public static int viewHeight; public FloatWindowBigView(final Context context) { super(context); LayoutInflater.from(context).inflate(R.layout.float_window_big, this); View view = findViewById(R.id.big_window_layout); viewWidth = view.getLayoutParams().width; viewHeight = view.getLayoutParams().height; Button close = (Button) findViewById(R.id.close); Button back = (Button) findViewById(R.id.back); close.setOnClickListener(new OnClickListener() { @Override public void onClick(View v) { // 点击关闭悬浮窗的时候 MyWindowManager.getInstance().removeBigWindow(context); MyWindowManager.getInstance().createNormalView(context); } }); back.setOnClickListener(new OnClickListener() { @Override public void onClick(View v) { // 点击主页的时候,移除大悬浮窗,创建小悬浮窗 Intent intent = new Intent(Intent.ACTION_MAIN); intent.setFlags(Intent.FLAG_ACTIVITY_NEW_TASK); intent.addCategory(Intent.CATEGORY_HOME); context.startActivity(intent); MyWindowManager.getInstance().removeBigWindow(context); MyWindowManager.getInstance().createNormalView(context); } }); } }