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