Implementation of Android floating window (error prone point)

Now many applications use the floating window. For example, when wechat clicks the home button in the video, the video small window will still be displayed on the screen. This function is very useful in many cases. So today, let's implement Android floating window and explore the error prone points when implementing floating window.

1.1 suspended window insertion interface

Before implementing the floating window, we need to know what interface can put a control into the screen. Android interface drawing is realized through windowmananger services. Then, since we want to implement a floating window on the interface other than our own application, we need to use WindowManager to "do things".

(frameworks/base/core/java/android/view/WindowMananger.java)

@SystemService(Context.WINDOW_SERVICE)
public interface WindowManager extends ViewManager {
 ...
}

WindowManager implements the viewmanager interface, which can be obtained through windows_ Service system service is. The viewmanager interface has the addview method, through which we add the floating window control to the screen.

1.2 permission setting and request

The suspended window needs to display controls on other applications. Obviously, this requires some permissions.

When API level > = 23, the permission system needs to be declared in the androidmanifest.xml file_ ALERT_ Windows can draw controls on other applications.

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

In addition to this permission, we also need to set the floating window permission for this application in the system settings. This permission needs to start settings.action in the application_ MANAGE_ OVERLAY_ Permission to let users set permissions manually.

startActivityForResult(new Intent(Settings.ACTION_MANAGE_OVERLAY_PERMISSION,Uri.parse("package:" + getPackageName())),REQUEST_CODE);

1.3 layoutparam settings

The addview method of WindowManager has two parameters, one is the control object to be added, and the other is the windowmanager.layoutparam object.

What needs to be emphasized here is the type variable in layoutparam. This variable is used to specify the window type. When setting this variable, you need to pay attention to a pit, that is, you need to adapt different versions of Android systems.

if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.O) {
 layoutParams.type = WindowManager.LayoutParams.TYPE_APPLICATION_OVERLAY;
} else {
 layoutParams.type = WindowManager.LayoutParams.TYPE_PHONE;
}

Before Android 8.0, the floating window setting can be type_ Phone, which is a non application window used to provide user interaction. Android 8.0 has modified the system and API behavior, including the use of system_ ALERT_ Applications with windows permission can no longer use the following window types to display reminder windows above other applications and windows:

If you need to display the reminder window above other applications and windows, this must be type_ APPLICATION_ New type of overlay. If you still use type in Android version 8.0 or above_ The following exception message will appear in the phone type suspension window:

android.view.WindowManager$BadTokenException: Unable to add window android.view.ViewRootImpl$W@f8ec928 -- permission denied for window type 2002

Let's explain the specific implementation of the floating window.

Complete source code address: https://github.com/dongzhong/TestForFloatingWindow

In order to separate the suspended window from the activity so that the suspended window can still run normally when the application is in the background, service is used to start the suspended window and serve as the logic support behind it. Before starting the service, you need to judge whether the floating window is allowed to be opened.

(MainActivity.java)

public void startFloatingService(View view) {
 ...
 if (!Settings.canDrawOverlays(this)) {
 Toast.makeText(this,"当前无权限,请授权",Toast.LENGTH_SHORT);
 startActivityForResult(new Intent(Settings.ACTION_MANAGE_OVERLAY_PERMISSION,0);
 } else {
 startService(new Intent(MainActivity.this,FloatingService.class));
 }
}
@Override
protected void onActivityResult(int requestCode,int resultCode,Intent data) {
 if (requestCode == 0) {
 if (!Settings.canDrawOverlays(this)) {
  Toast.makeText(this,"授权失败",Toast.LENGTH_SHORT).show();
 } else {
  Toast.makeText(this,"授权成功",Toast.LENGTH_SHORT).show();
  startService(new Intent(MainActivity.this,FloatingService.class));
 }
 }
}

The floating window control can be any subclass type of view. Here is an example of the simplest button.

(FloatingService.java)

@Override
public int onStartCommand(Intent intent,int flags,int startId) {
 showFloatingWindow();
 return super.onStartCommand(intent,flags,startId);
}
private void showFloatingWindow() {
 if (Settings.canDrawOverlays(this)) {
 // 获取WindowManager服务
 WindowManager windowManager = (WindowManager) getSystemService(WINDOW_SERVICE);
 // 新建悬浮窗控件
 Button button = new Button(getApplicationContext());
 button.setText("Floating Window");
 button.setBackgroundColor(Color.BLUE);
 // 设置LayoutParam
 WindowManager.LayoutParams layoutParams = new WindowManager.LayoutParams();
 if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.O) {
  layoutParams.type = WindowManager.LayoutParams.TYPE_APPLICATION_OVERLAY;
 } else {
  layoutParams.type = WindowManager.LayoutParams.TYPE_PHONE;
 }
 layoutParams.format = PixelFormat.RGBA_8888;
 layoutParams.width = 500;
 layoutParams.height = 100;
 layoutParams.x = 300;
 layoutParams.y = 300;
 // 将悬浮窗控件添加到WindowManager
 windowManager.addView(button,layoutParams);
 }
}

All right, done!

Yes, I'm right. The simplest suspension window is realized. Isn't it simple? Let's see the effect.

Of course, the effect of this floating window is only displayed, which is far from the real desired effect. However, the basic principle has been realized, and the rest is to add a little function on it.

3.1 drag function

The first function you want to add is to drag the floating window. Because the display position of the suspended window may block the information we want to see behind us. It would be best if we could drag the suspended window away. In Android, the handling of touch events is one of the most basic operations, which can be directly applied to the code.

(FloatingService.java)

private void showFloatingWindow() {
 ...
 button.setOnTouchListener(new FloatingOnTouchListener());
 ...
}
private class FloatingOnTouchListener implements View.OnTouchListener {
 private int x;
 private int y;
 @Override
 public boolean onTouch(View view,MotionEvent event) {
 switch (event.getAction()) {
  case MotionEvent.ACTION_DOWN:
  x = (int) event.getRawX();
  y = (int) event.getRawY();
  break;
  case MotionEvent.ACTION_MOVE:
  int NowX = (int) event.getRawX();
  int NowY = (int) event.getRawY();
  int movedX = NowX - x;
  int movedY = NowY - y;
  x = NowX;
  y = NowY;
  layoutParams.x = layoutParams.x + movedX;
  layoutParams.y = layoutParams.y + movedY;
  // 更新悬浮窗控件布局
  windowManager.updateViewLayout(view,layoutParams);
  break;
  default:
  break;
 }
 return false;
 }
}

It should be noted here that the method of updating the floating window control layout at the code comment. Only when this method is called will the position of the floating window change. Let's see the effect.

3.2 automatic picture playback

Let's make some small changes to the suspended window to demonstrate the slightly complex interface.

In the floating window interface here, we no longer simply use a button control, but add an ImageView in a LinearLayout. The layout file is as follows.

(image_display.xml)

<?xml version="1.0" encoding="utf-8"?>
<LinearLayout
 xmlns:android="http://schemas.android.com/apk/res/android"
 android:layout_width="match_parent"
 android:layout_height="match_parent"
 android:orientation="vertical" >

 <ImageView
 android:id="@+id/image_display_imageview"
 android:layout_width="wrap_content"
 android:layout_height="wrap_content" />

</LinearLayout>

Make some changes where you created the suspended window layout.

(FloatingService.java)

private void showFloatingWindow() {
 ...
 LayoutInflater layoutInflater = LayoutInflater.from(this);
 displayView = layoutInflater.inflate(R.layout.image_display,null);
 displayView.setOnTouchListener(new FloatingOnTouchListener());

 ImageView imageView = displayView.findViewById(R.id.image_display_imageview);
 imageView.setImageResource(images[imageIndex]);

 windowManager.addView(displayView,layoutParams);
 ...
}

We also want to switch pictures every two seconds, so let's make a mechanism to switch pictures regularly.

(FloatingService.java)

@Override
public void onCreate() {
 ...
 changeImageHandler = new Handler(this.getMainLooper(),changeImageCallback);
}
private Handler.Callback changeImageCallback = new Handler.Callback() {
 @Override
 public boolean handleMessage(Message msg) {
 if (msg.what == 0) {
  imageIndex++;
  if (imageIndex >= 5) {
  imageIndex = 0;
  }
  if (displayView != null) {
  ((ImageView) displayView.findViewById(R.id.image_display_imageview)).setImageResource(images[imageIndex]);
  }
  changeImageHandler.sendEmptyMessageDelayed(0,2000);
 }
 return false;
 }
};
private void showFloatingWindow() {
 ...
 windowManager.addView(displayView,layoutParams);
 changeImageHandler.sendEmptyMessageDelayed(0,2000);
}

Let's take a look at the effect of automatic picture playback in the floating window.

3.3 video window

Let's take a look at the most commonly used function of floating window: Video small window. For example, when wechat exits the interface during the video process, it will display the video in the form of a small window. Here, I'll play a network video with mediaplay and surfaceview to simulate the effect. The implementation is basically the same as that of the above picture player, except that the control and corresponding playback logic are changed. The layout file is similar to the picture player above, but replaces ImageView with surfaceview. Create a floating window control.

(FloatingService.java)

private void showFloatingWindow() {
 ...
 MediaPlayer mediaPlayer = new MediaPlayer();
 mediaPlayer.setAudioStreamType(AudioManager.STREAM_MUSIC);
 SurfaceView surfaceView = displayView.findViewById(R.id.video_display_surfaceview);
 final SurfaceHolder surfaceHolder = surfaceView.getHolder();
 surfaceHolder.addCallback(new SurfaceHolder.Callback() {
 @Override
 public void surfaceCreated(SurfaceHolder holder) {
  mediaPlayer.setDisplay(surfaceHolder);
 }
 ...
 );
 mediaPlayer.setOnPreparedListener(new MediaPlayer.OnPreparedListener() {
 @Override
 public void onPrepared(MediaPlayer mp) {
  mediaPlayer.start();
 }
 });
 try {
 mediaPlayer.setDataSource(this,Uri.parse("https://raw.githubusercontent.com/dongzhong/ImageAndVideoStore/master/Bruno%20Mars%20-%20Treasure.mp4"));
 mediaPlayer.prepareAsync();
 }
 catch (IOException e) {
 Toast.makeText(this,"无法打开视频源",Toast.LENGTH_LONG).show();
 }
 windowManager.addView(displayView,layoutParams);
}

Well, let's play a Mars coquettish "treasure".

The above is the implementation of Android floating window, as well as some small and simple applications.

The following steps can be summarized:

1. Declare and apply for permission 2. Build the controls required for the floating window 3. Add the controls to 'WindowManager' 4. Update the layout of 'WindowManager' if necessary

One of the pitfalls that should be noted is the version adaptation of layoutparams.type.

Demo source address: https://github.com/dongzhong/TestForFloatingWindow

The above is the implementation of Android floating window introduced by Xiaobian. I hope it will help you. If you have any questions, please leave me a message, and Xiaobian will reply to you in time. Thank you very much for your support to our website! If you think this article is helpful to you, welcome to reprint, please indicate the source, thank you!

The content of this article comes from the network collection of netizens. It is used as a learning reference. The copyright belongs to the original author.
THE END
分享
二维码
< <上一篇
下一篇>>