Floating window playing effect of Android imitating Youku video

  • 2021-12-04 19:57:07
  • OfStack

Previously, I met the demand to make the video play in the floating window like Youku video, and to realize seamless switching between the floating window and the main playing page. The self-encapsulated ijkplayer was used in the project
This requirement means that you can't create new video controls in the floating window, so you need to reuse the video controls of the main page in the floating window to achieve seamless connection.

The home page faces the parent view of the corresponding video control


<FrameLayout
      android:id="@+id/vw_live"
      android:layout_width="match_parent"
      android:layout_height="match_parent"
      android:layout_centerInParent="true"/>

Use FrameLayout as the ParentView for adding video controls, and add the newly created player control to the inside of the parent control through addview method


vw_live = new IjkVideoView(this);

video_frame = findViewById(R.id.vw_live);
video_frame.addView(vw_live);

Startup mode of main playback interface

The startup mode of activity of the main playback interface cannot be the default, because we must ensure that the main playback interface retreats to the background when the floating window is displayed, but the whole application cannot retreats to the background, so the startup mode of activity is changed to singleInstance

android:launchMode="singleInstance"

Back in the background, we pass the moveTaskToBack (true) method;

moveTaskToBack(true);

The playback interface can be retreated to the background without the whole application returning to the background

Request for permission

To use the floating window, you need to apply for permission


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

if (!Settings.canDrawOverlays(this)) {
      Toast.makeText(this, " There is no permission at present, please authorize ", Toast.LENGTH_SHORT);
      startActivityForResult(new Intent(Settings.ACTION_MANAGE_OVERLAY_PERMISSION, Uri.parse("package:" + getPackageName())), 2);
    } 

Floating window


 @SuppressLint("ClickableViewAccessibility")
  public void showFloatingWindowView(IjkVideoView view) {
    //  Floating window display view 
    LayoutInflater layoutInflater = LayoutInflater.from(activity);
    mShowView = layoutInflater.inflate(R.layout.video_floating_window_layout, null);;
    //  Get the System Window Management Service 
    mWindowManager = (WindowManager) activity.getSystemService(Context.WINDOW_SERVICE);
    //  Parameter setting and return of floating window 
    mFloatParams = getParams();
    //floatingWindow Internal control instance 
    init(view);
    //  Set Window Touch Move Events 
    mShowView.setOnTouchListener(new FloatViewMoveListener());

    //  Floating window generation 
    mWindowManager.addView(mShowView, mFloatParams);
  }
  private void init(IjkVideoView viewGroup){
    videoLayout = mShowView.findViewById(R.id.floating_video);
    videoLayout.removeAllViews();
    if (viewGroup != null){
      ijkVideoView = viewGroup;
      videoLayout.addView(ijkVideoView,new ViewGroup.LayoutParams(ViewGroup.LayoutParams.MATCH_PARENT
          ,ViewGroup.LayoutParams.MATCH_PARENT));
    }

    mBtnCloseFloatingWindow = mShowView.findViewById(R.id.close_floating_view);
    mBtnCloseFloatingWindow.setOnClickListener(new View.OnClickListener() {
      @Override
      public void onClick(View v) {

      }
    });
    mBtnBackFloatingWindow = (ImageView)mShowView.findViewById(R.id.back_floating_view);
    mBtnBackFloatingWindow.setOnClickListener(new View.OnClickListener() {
      @Override
      public void onClick(View v) {

      }
    });
  }

  private WindowManager.LayoutParams getParams() {
    WindowManager.LayoutParams layoutParams = new WindowManager.LayoutParams();
    // Set the floating window type 
    if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.O) {
      layoutParams.type = WindowManager.LayoutParams.TYPE_APPLICATION_OVERLAY;
    } else {
      layoutParams.type = WindowManager.LayoutParams.TYPE_SYSTEM_ALERT;
    }
    // Setting Floating Window Properties 
    layoutParams.flags = WindowManager.LayoutParams.FLAG_NOT_FOCUSABLE
        | WindowManager.LayoutParams.FLAG_NOT_TOUCH_MODAL
        | WindowManager.LayoutParams.FLAG_LAYOUT_IN_SCREEN
        | WindowManager.LayoutParams.FLAG_LAYOUT_INSET_DECOR
        | WindowManager.LayoutParams.FLAG_WATCH_OUTSIDE_TOUCH;
    // Set the floating window transparent 
    layoutParams.format = PixelFormat.TRANSLUCENT;
    // Set the length and width data of the floating window 
    layoutParams.width = 500;
    layoutParams.height = 340;
    // Set the display position of the floating window 
    layoutParams.gravity = Gravity.START | Gravity.TOP;
    layoutParams.x = 100;
    layoutParams.y = 100;
    return layoutParams;
  }

xml of floating window can get the desired effect through customization


<FrameLayout
  xmlns:android="http://schemas.android.com/apk/res/android"
  android:id="@+id/floating_video_layout"
  android:layout_width="match_parent"
  android:layout_height="match_parent">
  <FrameLayout
    android:id="@+id/floating_video"
    android:layout_width="match_parent"
    android:layout_height="match_parent"/>


  <ImageView
    android:id="@+id/close_floating_view"
    android:layout_width="50dp"
    android:layout_height="50dp"
    android:layout_gravity="end"
    android:padding="10dp"
    android:src="@android:drawable/ic_menu_close_clear_cancel" />

  <ImageView
    android:id="@+id/back_floating_view"
    android:layout_width="50dp"
    android:layout_height="50dp"
    android:padding="10dp"
    android:src="@android:drawable/ic_menu_revert" />
</FrameLayout>

The sliding of the floating window can be realized by custom click monitoring


/**
   *  Floating window movement / Click to listen 
   */
  private class FloatViewMoveListener implements View.OnTouchListener {

    // The coordinates of starting touch, the coordinates of moving (relative to the coordinates of the upper left corner of the screen) 
    private int mTouchStartX;
    private int mTouchStartY;
    // Start coordinates and end coordinates (relative to the coordinates of its own control) 
    private int mStartX, mStartY;
    // Judge whether the floating window moves, and make a mark here to prevent the click event triggered by letting go after moving 
    private boolean isMove;

    @Override
    public boolean onTouch(View view, MotionEvent motionEvent) {
      int action = motionEvent.getAction();
      int x = (int) motionEvent.getX();
      int y = (int) motionEvent.getY();
      switch (action) {
        case MotionEvent.ACTION_DOWN:
          isMove = false;
          mTouchStartX = (int) motionEvent.getRawX();
          mTouchStartY = (int) motionEvent.getRawY();
          mStartX = x;
          mStartY = y;
          break;
        case MotionEvent.ACTION_MOVE:
          int mTouchCurrentX = (int) motionEvent.getRawX();
          int mTouchCurrentY = (int) motionEvent.getRawY();
          mFloatParams.x += mTouchCurrentX - mTouchStartX;
          mFloatParams.y += mTouchCurrentY - mTouchStartY;
          mWindowManager.updateViewLayout(mShowView, mFloatParams);
          mTouchStartX = mTouchCurrentX;
          mTouchStartY = mTouchCurrentY;
          float deltaX = x - mStartX;
          float deltaY = y - mStartY;
          if (Math.abs(deltaX) >= 5 || Math.abs(deltaY) >= 5) {
            isMove = true;
          }
          break;
        case MotionEvent.ACTION_UP:
          break;
        default:
          break;
      }
      // If it is a mobile event, it will not trigger OnClick Events to prevent movement 1 Let go to form a click event 
      return isMove;
    }
  }

The disappearance of floating window, videoLayout. removeAllViews () is called here to empty the parent View of the multiplexed video control. When returning to the main playback activity, calling addview method will not report the error of child view has Parent, you have to call removeView ()


public void dismiss() {
    if (mWindowManager != null && mShowView != null) {
      videoLayout.removeAllViews();
      if (mShowView.getParent() != null){
        mWindowManager.removeView(mShowView);
      }
    }
  }

Start the suspension window


 public videoFloatingWindow(Context context){
    super(context);
    this.activity = context;
  }

Calls to floating windows

Use hasBind to record whether the floating window is called


vw_live = new IjkVideoView(this);
0

Attention

1. Since the main interface activity uses singleInstance startup mode, flag should be added when returning to the main interface activity from the floating window


vw_live = new IjkVideoView(this);
1

2. When the activity of the main interface returns to the background and re-enters the main interface, note that the onCreate method is no longer called, but the onNewIntent is called, so rewrite the onNewIntent method and re-enter the main interface, and the floating window disappears


vw_live = new IjkVideoView(this);
2

Summarize


Related articles: