Mobile Development 14 min read

Comparative Study and Implementation of Small‑Window Video Playback on Android

The article compares Android small‑window video playback techniques—SurfaceView, GLSurfaceView and TextureView—detailing their animation limitations, providing source‑code examples for embedded and floating implementations, and showing that TextureView combined with a singleton player offers the smoothest, most reliable small‑window experience.

Tencent Music Tech Team
Tencent Music Tech Team
Tencent Music Tech Team
Comparative Study and Implementation of Small‑Window Video Playback on Android

This article presents an in‑depth study of small‑window video playback on Android. It compares several implementation schemes— SurfaceView , GLSurfaceView and TextureView —and provides a detailed source‑code analysis to achieve smooth moving, scaling, rotation and transparency effects.

After reviewing the previous article on the principles of small‑window playback, the author explains how SurfaceView creates a separate window and renders content on an independent surface. Because the surface is not part of the host view hierarchy, animations may produce black edges, lack of rotation synchronization, and transparency glitches. On Android N and later, SurfaceView synchronizes its surface during animations, eliminating black edges, but it still does not support transparent animations. TextureView , being a regular view, handles moving, scaling, rotation and alpha animations without these issues, though it consumes more GPU resources.

The article then demonstrates the conclusions with concrete examples using MediaPlayer to play video via SurfaceView and TextureView . The source code for the examples is provided at the end of the article.

1. Example Demonstration

Animations on an Android L device show that SurfaceView suffers from black edges during move/scale, does not follow rotation, and hides the view during alpha animation, whereas TextureView behaves like a normal view.

On an Android N device, SurfaceView no longer shows black edges during move/scale, but still fails to rotate and does not support transparent animations. TextureView continues to work correctly.

Sliding tests on Android N reveal black edges for SurfaceView while TextureView remains clean.

2. Seamless Playback During Interaction

Switching between full‑screen and small‑window modes normally requires recreating the player, causing a pause. By using a singleton player that renders to both the large and small windows, the transition becomes smooth and the video continues playing without interruption.

3. Implementation of Small‑Window Playback

3.1 Embedding the Video Playback View into the Layout

The layout embeds a TextureView inside a custom DragVideoView that handles gesture‑based scaling. A separate View displays video description. The XML snippet is:

<com.iamlarry.floatwindowdemo.drag.DragVideoView
    android:id="@+id/drag_view"
    android:layout_width="match_parent"
    android:layout_height="match_parent">
    <TextureView
        android:id="@+id/player"
        android:layout_width="match_parent"
        android:layout_height="wrap_content"
        android:background="#ff000000" />
    <View
        android:id="@+id/desc"
        android:layout_width="match_parent"
        android:layout_height="match_parent"
        android:background="@color/colorAccent" />
</com.iamlarry.floatwindowdemo.drag.DragVideoView>

The DragVideoView measures Player and Desc in onMeasure and positions them in onLayout based on the current drag offset:

@Override
protected void onMeasure(int widthMeasureSpec, int heightMeasureSpec) {
    measurePlayer(widthMeasureSpec, heightMeasureSpec);
    measureDesc(widthMeasureSpec, heightMeasureSpec);
}

@Override
protected void onLayout(boolean changed, int l, int t, int r, int b) {
    if (mDragDirect != HORIZONTAL) {
        mLeft = getWidth() - getPaddingRight() - getPaddingLeft() - mPlayer.getMeasuredWidth();
        mDesc.layout(mLeft, mTop + mPlayer.getMeasuredHeight(),
                     mLeft + mDesc.getMeasuredWidth(), mTop + mDesc.getMeasuredHeight());
    }
    mPlayer.layout(mLeft, mTop, mLeft + mPlayer.getMeasuredWidth(), mTop + mPlayer.getMeasuredHeight());
}

Touch events are delegated to a DragHelper . The vertical slide method calculates the target position and animates the player:

@Override
public boolean onInterceptTouchEvent(MotionEvent event) {
    return mDragHelper.shouldInterceptTouchEvent(event);
}

private boolean slideVerticalTo(float slideOffset) {
    int topBound = mMinTop;
    int y = (int) (topBound + slideOffset * mVerticalRange);
    if (mDragHelper.smoothSlideViewTo(mPlayer,
            mIsMinimum ? (int) (mPlayerMaxWidth * (1 - PLAYER_RATIO)) : getPaddingLeft(), y)) {
        ViewCompat.postInvalidateOnAnimation(this);
        return true;
    }
    return false;
}

3.2 Adding the Playback View via WindowManager

Using WindowManagerService , an application can add a floating window that appears above other apps. The relevant source excerpt shows the addWindow method handling different window types.

public class WindowManagerService extends IWindowManager.Stub {
    public int addWindow(Session session, IWindow client, …) {
        if (attrs.type == TYPE_INPUT_METHOD) {
            // …
        } else if (attrs.type == TYPE_INPUT_METHOD_DIALOG) {
            // …
        } else {
            if (attrs.type == TYPE_WALLPAPER) {
                adjustWallpaperWindowsLocked();
            } else if ((attrs.flags & FLAG_SHOW_WALLPAPER) != 0) {
                adjustWallpaperWindowsLocked();
            }
        }
    }
}

Typical layout parameters for a floating video view:

WindowManager.LayoutParams layoutParams = new WindowManager.LayoutParams(
        WindowManager.LayoutParams.WRAP_CONTENT,
        WindowManager.LayoutParams.WRAP_CONTENT,
        0, 0,
        PixelFormat.TRANSPARENT);
layoutParams.flags = WindowManager.LayoutParams.FLAG_NOT_TOUCH_MODAL;
layoutParams.type = WindowManager.LayoutParams.TYPE_PHONE;
layoutParams.gravity = Gravity.CENTER;
WindowManager windowManager = getWindowManager();
windowManager.addView(floatingButton, layoutParams);

This approach allows playback both inside and outside the host app, but requires the SYSTEM_ALERT_WINDOW permission and provides poorer interaction smoothness.

3.3 Android 8.0 Picture‑in‑Picture (PiP)

PiP offers a simple way to shrink video into a floating window on Android 8.0+, but it does not support devices prior to 8.0.

3.4 Activity Dialog Mode

An Activity can be displayed as a dialog window (a PhoneWindow ) that floats above other Activities. The relationship between Activity , Window and View is illustrated, and the source shows how setContentView inflates the layout into a DecorView :

// Activity#setContentView
public void setContentView(@LayoutRes int layoutResID) {
    getWindow().setContentView(layoutResID);
    initWindowDecorActionBar();
}

// PhoneWindow#setContentView
@Override
public void setContentView(int layoutResID) {
    if (mContentParent == null) {
        installDecor(); // initializes DecorView
    } else if (!hasFeature(FEATURE_CONTENT_TRANSITIONS)) {
        mContentParent.removeAllViews();
    }
    if (hasFeature(FEATURE_CONTENT_TRANSITIONS)) {
        final Scene newScene = Scene.getSceneForLayout(mContentParent, layoutResID, getContext());
        transitionTo(newScene);
    } else {
        mLayoutInflater.inflate(layoutResID, mContentParent);
    }
}

By modifying the WindowManager.LayoutParams of the dialog‑style Activity, its position and size can be changed, enabling a floating video window. Example code:

Window window = getWindow();
WindowManager.LayoutParams lp = window.getAttributes();
lp.y = (int) y;
lp.x = (int) (y * lastX / lastY);
lp.width = Config.getWidth() - lp.x;
lp.height = Config.getHeight() - lp.y;
window.setAttributes(lp);

// Window.setAttributes implementation
public void setAttributes(WindowManager.LayoutParams a) {
    mWindowAttributes.copyFrom(a);
    dispatchWindowAttributesChanged(mWindowAttributes);
}

public void onWindowAttributesChanged(WindowManager.LayoutParams params) {
    if (mParent == null && decor != null && decor.getParent() != null) {
        getWindowManager().updateViewLayout(decor, params);
    }
}

This method provides simple implementation but may introduce black edges during drag.

4. Conclusion

Experimental results show that SurfaceView exhibits black edges during move/scale, does not rotate with the content, and fails transparent animations. On Android N+, move/scale/rotate are synchronized, eliminating black edges, but transparency remains unsupported. TextureView handles all these animations correctly and is therefore more suitable for small‑window video playback.

Using a singleton player enables seamless transition between full‑screen and small‑window modes, delivering a smooth user experience.

Among the four schemes discussed, the WindowManager‑based floating view and the embedded view inside the app layout are the most appropriate solutions for implementing small‑window video playback.

5. Demo

Source code repository: https://github.com/FightingLarry/FloatWindowDemo

AndroidSurfaceViewvideo playbackPicture-in-PictureTextureViewWindowManager
Tencent Music Tech Team
Written by

Tencent Music Tech Team

Public account of Tencent Music's development team, focusing on technology sharing and communication.

0 followers
Reader feedback

How this landed with the community

login Sign in to like

Rate this article

Was this worth your time?

Sign in to rate
Discussion

0 Comments

Thoughtful readers leave field notes, pushback, and hard-won operational detail here.