To realize the suspended ball, we need to understand
- Winodw and WindowManager
- Serivce
Version: Android 10
Android studio version: 2020.3.1
Targetversion: 30 (26 +)
Gradle version during development; com.android.tools.build:gradle:7.0.0
Application permission: ACTION_MANAGE_OVERLAY_PERMISSION
Effect achieved:
- Short press
- It can be pressed for a long time (when you just click, the short press click event will not be triggered), and it can be pressed for two times
- Without clicking for 10 seconds, it will become a suspension ball with 80% transparency and will not block the text behind the suspension ball
Suspended ball Preview: (private chat with me if you want to lose the attention of the picture)
Start implementation
1. Type of use
First of all, it should be clear that Activity cannot complete a floating ball suspended on the desktop. We must use service. It will not be repeated here. Service is very similar to Activity, but the front is mainly used for some background services. In cooperation with WindowManager, we can complete the effect of floating ball (application shutdown, service shutdown).
2. Authority
At least pop-up permission is required. If the floating ball needs to add other functions, add permissions as needed
<uses-permission android:name="android.permission.SYSTEM_ALERT_WINDOW" />
3. Prepare picture resources
Need to pay attention to private chat
4. Write code
1. Get screen parameters
public void getSizeOfScreen(){ WindowManager windowManager=(WindowManager) this.getSystemService(Context.WINDOW_SERVICE); SCREEN_HEIGHT=windowManager.getDefaultDisplay().getHeight(); SCREEN_WIDTH=windowManager.getDefaultDisplay().getWidth(); }
Screen parameters are very important here to prevent the floating ball from being displayed outside the screen
2.initWindowManager
@RequiresApi(api = Build.VERSION_CODES.O) @SuppressLint("ClickableViewAccessibility") public void initWindowManager() { windowManager = (WindowManager) getApplicationContext() .getSystemService(Context.WINDOW_SERVICE); //Here is the view to obtain the binding of WindowManager. The following and the following echo, which is equivalent to binding the two together. Only after binding can we use Manger to control the levitation ball // floatView = new View(getApplicationContext()); params = new WindowManager.LayoutParams(); // Set Window Type if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.O) { params.type = WindowManager.LayoutParams.TYPE_APPLICATION_OVERLAY; } else { params.type = WindowManager.LayoutParams.TYPE_SYSTEM_ALERT; } // Set the suspension box to be untouchable params.flags = WindowManager.LayoutParams.FLAG_NOT_TOUCH_MODAL | WindowManager.LayoutParams.FLAG_NOT_FOCUSABLE; // The suspended window cannot be touched, does not accept any events, and does not affect the subsequent event response params.format = PixelFormat.RGBA_8888; // Automatically set the appropriate width and height of the suspension frame setSizeOfBall(); params.gravity = Gravity.LEFT; params.x = 200; params.y = 000; }
The if else statement in the code is very important. In some outdated blogs on the Internet, if there is no such code, the permission type error will be reported
Note: parm represents the position of the suspended ball
If you want to control your own floating ball, you must know the View coordinate system of Android mobile phone screen
3. Code
public void Drag() { // Set the Touch monitor of the floating frame if (IS_SHOW) { floatView.setOnTouchListener(new View.OnTouchListener() { //The variable that holds the last position of the hover box int lastX, lastY; int paramX, paramY; int old_dx = 0, old_dy = 0; //Idea: when only touching, moving the floating ball will trigger the ontouch listener (if the hand is released, the floating ball will automatically stick to the edge). If it is touched and not stuck to the edge, if it is suspended in the non edge part //You can call the screenshot function to open the screenshot @Override public boolean onTouch(View v, MotionEvent event) { switch (event.getAction()) { case MotionEvent.ACTION_DOWN: lastX = (int) event.getRawX(); lastY = (int) event.getRawY(); paramX = params.x; paramY = params.y; isMove=true;//Mobile sign setDrawable(); downTime=System.currentTimeMillis(); Log.i(TAG, "onTouch: ACTION_DOWN"); break; case MotionEvent.ACTION_MOVE: int dx = (int) event.getRawX() - lastX; int dy = (int) event.getRawY() - lastY; params.x = paramX + dx; params.y = paramY + dy; //Determine whether it is a long press isLongPress(event,lastX,lastY); windowManager.updateViewLayout(floatView, params); Log.i(TAG, "onTouch:ACTION_MOVE"); old_dy = dy; old_dx = dx; IsTransparent=false; handler.removeMessages(flag3); break; // After canceling the click, the current position is obtained, and the horizontal direction becomes 0, case MotionEvent.ACTION_UP: Log.i(TAG, "onTouch: ACTION_CANCEL" ); //Here is a simple automatic welt function if (params.x>SCREEN_WIDTH/2){ params.x=SCREEN_WIDTH; windowManager.updateViewLayout(floatView,params); } else{ params.x=0; windowManager.updateViewLayout(floatView,params); } isOnceLongPress=false; isMove=false; isTwiceLongPress=false; Transparent();//Set the suspended ball picture to 80 transparent break; } v.performClick(); return true; // onTouchListener's onTouch method has higher priority than onTouchEvent and will be triggered first // If the onTouch method returns false, onTouchEvent will be triggered. Otherwise, the onTouchEvent method will not be called // The implementation of built-in events such as click events is based on onTouchEvent. If onTouch returns true, these events will not be triggered // The order is: onTouch – > ontouchevent - > onclick //Data address https://blog.csdn.net/DreamingOfDreams/article/details/83592238 } }); windowManager.addView(floatView, params); Log.i(TAG, "Drag: The suspension ball has addView"); } }
A listener is added to the suspension ball to monitor its click and change its position with the click
The Handler is used in the figure to solve a user-defined gesture and two long press events
Here, we will not explain in detail what a Handler is. In short, its function is to send signals in the code, and then the Handler executes the corresponding code after receiving the signal, and has the functions of delaying transmission and canceling transmission.
I used the following functions in the demo
- Delayed sending: it can solve the event that the transparency of the suspended ball changes 10 seconds after non clicking
- Cancel sending: if there is a click event within 10 seconds, cancel sending the signal to change the transparency
Note: the delayed transmission signal will arrive, and the corresponding code will be triggered to cancel the transmission before transmission
handler.sendEmptyMessageDelayed(flag1,1000);
@SuppressLint("HandlerLeak") public void initHandler(){ handler=new Handler(){ @Override public void handleMessage(@NonNull Message msg) { switch (msg.what){ case flag1: isOnceLongPress=true; handler.removeMessages(flag1);//Prevent message overflow from causing this event to occur twice Log.d(TAG, "handleMessage: Long press event 1" ); break; case flag2: isTwiceLongPress=true; handler.removeMessages(flag2); Log.d(TAG, "handleMessage: Long press event 2" ); break; case flag3: IsTransparent=true; handler.removeMessages(flag3); Log.d(TAG, "handleMessage: Transparent events" ); floatView.setBackgroundResource(R.drawable.ic_floatball_transparent80); break; } } }; }
Set the picture for the floating ball: set the picture if it is transparent, and create it if it is not displayed
public void setDrawable() { if (IsTransparent){ floatView.setBackgroundResource(R.drawable.ic_float_ball); return; } if (!IS_SHOW) { floatView = new View(getApplicationContext()); floatView.setBackgroundResource(R.drawable.ic_float_ball); // floatView.setBackgroundColor(Color.TRANSPARENT); IS_SHOW = true; Log.d(TAG, "setDrawable: Suspended ball picture has been loaded"); } }