android realizes a floating window that can move freely and monitor click events

  • 2021-10-24 23:55:21
  • OfStack

Recently, because of the need of the project, I realized a free movement, and long press can jump out of a control play, a large floating window.

Okay, let's get started. First of all, let's talk about permissions. The floating window needs to declare one permission in manifest:


<uses-permission android:name="android.permission.SYSTEM_ALERT_WINDOW" />

And then, um, let's talk about the principle of floating window.

In Andriod, all interface elements are implemented through windowmanger, such as Activity, Fragment and so on. Therefore, our floating window will naturally be realized through this.

In this project, we customized two floating windows view. Let's take one of the simpler examples:

We customize a class MyWindowManager that can manage floating windows in a unified way, and be responsible for creating and deleting floating windows


/**
 * Created by shiwe on 2017/3/7.
 *  Floating window management 
 *  Create, remove 
 *  Singleton pattern 
 */

public class MyWindowManager {

 private FloatNormalView normalView;
 private FloatControlView controlView;

 private static MyWindowManager instance;

 private MyWindowManager() {
 }

 public static MyWindowManager getInstance() {
  if (instance == null)
   instance = new MyWindowManager();
  return instance;
 }


 /**
  *  Create a small floating window 
  */
 public void createNormalView(Context context) {
  if (normalView == null)
   normalView = new FloatNormalView(context);
 }

 /**
  *  Remove floating window 
  *
  * @param context
  */
 public void removeNormalView(Context context) {
  if (normalView != null) {
   WindowManager windowManager = (WindowManager) context.getSystemService(Context.WINDOW_SERVICE);
   windowManager.removeView(normalView);
   normalView = null;
  }
 }

 /**
  *  Create a small floating window 
  */
 public void createControlView(Context context) {
  if (controlView == null)
   controlView = new FloatControlView(context);
 }

 /**
  *  Remove floating window 
  *
  * @param context
  */
 public void removeControlView(Context context) {
  if (controlView != null) {
   WindowManager windowManager = (WindowManager) context.getSystemService(Context.WINDOW_SERVICE);
   windowManager.removeView(controlView);
   controlView = null;
  }
 }
}

Then look at our custom view, which inherits from LinearLayout, and we initialize other parameters such as the location of this control in initLayoutParams; In the initEvent method, the monitoring events with finger movement and the monitoring events with long press are defined.


public class FloatNormalView extends LinearLayout {

 private Context context = null;
 private View view = null;
 private ImageView ivShowControlView = null;
 private WindowManager.LayoutParams lp = new WindowManager.LayoutParams();
 private static WindowManager windowManager;

 private float mTouchStartX;
 private float mTouchStartY;
 private float x;
 private float y;
 private boolean initViewPlace = false;
 private MyWindowManager myWindowManager;
 private boolean isControlViewShowing = false;

 public FloatNormalView(Context context) {
  super(context);
  this.context = context;
  myWindowManager = MyWindowManager.getInstance();
  LayoutInflater.from(context).inflate(R.layout.float_normal_view, this);
  view = findViewById(R.id.ll_float_normal);
  ivShowControlView = (ImageView) findViewById(R.id.iv_show_control_view);
  windowManager = (WindowManager) context.getSystemService(Context.WINDOW_SERVICE);
  initLayoutParams();
  initEvent();
 }


 /**
  *  Initialization parameter 
  */
 private void initLayoutParams() {
  // Screen width and height 
  int screenWidth = windowManager.getDefaultDisplay().getWidth();
  int screenHeight = windowManager.getDefaultDisplay().getHeight();

  // Always appears above the application window. 
  lp.type = WindowManager.LayoutParams.TYPE_PHONE;

  // FLAG_NOT_TOUCH_MODAL Do not block event delivery to subsequent windows 
  // FLAG_NOT_FOCUSABLE  When the floating window is small, the application icon behind it changes from non-long press to long press , Do not set this flag If, home There will be problems with the screen drawing of the page 
  lp.flags = WindowManager.LayoutParams.FLAG_NOT_FOCUSABLE
    | WindowManager.LayoutParams.FLAG_NOT_TOUCH_MODAL;

  // Default display position of floating window 
  lp.gravity = Gravity.START | Gravity.TOP;
  // Specify a location 
  lp.x = screenWidth - view.getLayoutParams().width * 2;
  lp.y = screenHeight / 2 + view.getLayoutParams().height * 2;
  // Width and height of floating window 
  lp.width = WindowManager.LayoutParams.WRAP_CONTENT;
  lp.height = WindowManager.LayoutParams.WRAP_CONTENT;
  lp.format = PixelFormat.TRANSPARENT;
  windowManager.addView(this, lp);
 }

 /**
  *  Set the floating window to listen for events 
  */
 private void initEvent() {
  ivShowControlView.setOnLongClickListener(new OnLongClickListener() {
   @Override
   public boolean onLongClick(View view) {
    if (!isControlViewShowing) {
     myWindowManager.createControlView(context);
     isControlViewShowing = true;
    } else {
     myWindowManager.removeControlView(context);
     isControlViewShowing = false;
    }
    return true;
   }
  });
  view.setOnTouchListener(new OnTouchListener() {
   @Override
   public boolean onTouch(View v, MotionEvent event) {
    switch (event.getAction()) {
     case MotionEvent.ACTION_DOWN:
      if (!initViewPlace) {
       initViewPlace = true;
       // Get the initial position 
       mTouchStartX += (event.getRawX() - lp.x);
       mTouchStartY += (event.getRawY() - lp.y);
      } else {
       // Fine-tune the initial position according to the position where the finger left last time and the position where the finger clicked this time 
       mTouchStartX += (event.getRawX() - x);
       mTouchStartY += (event.getRawY() - y);
      }
      break;
     case MotionEvent.ACTION_MOVE:
      //  Gets coordinates relative to the screen, starting at the upper left corner of the screen 
      x = event.getRawX();
      y = event.getRawY();
      updateViewPosition();
      break;

     case MotionEvent.ACTION_UP:
      break;
    }
    return true;
   }
  });
 }

 /**
  *  Update the floating window position 
  */
 private void updateViewPosition() {
  lp.x = (int) (x - mTouchStartX);
  lp.y = (int) (y - mTouchStartY);
  windowManager.updateViewLayout(this, lp);
 }

Finally, you only need to call the createxxx method in mywindowManager in Activity.


public class MainActivity extends AppCompatActivity {

 MyWindowManager myWindowManager;

 @Override
 protected void onCreate(@Nullable Bundle savedInstanceState) {
  super.onCreate(savedInstanceState);
  setContentView(R.layout.activity_main);
  myWindowManager = MyWindowManager.getInstance();
  myWindowManager.createNormalView(this.getApplicationContext());
 }
}

Finally, attach the download address of demo project: android realizes floating window


Related articles: