You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

如何在React Native中实现带实时日期更新的PIP(画中画)模式?

React Native 实现带实时日期更新的画中画(PIP)模式

如果第三方库不好用,直接基于原生模块实现是更可靠的方案,以下是针对Android的完整实现步骤(iOS方案附后):

Android 端实现

1. 配置 AndroidManifest.xml

在你的主 Activity 标签中添加 PIP 支持属性,并声明权限:

<activity
  android:name=".MainActivity"
  android:supportsPictureInPicture="true"
  android:resizeableActivity="true"
  android:configChanges="screenSize|smallestScreenSize|screenLayout|orientation">

  <!-- Android 12+ 需声明权限 -->
  <uses-permission android:name="android.permission.USE_PICTURE_IN_PICTURE" />
</activity>

2. 编写原生 PIP 模块

创建 Kotlin 模块,实现进入 PIP 的方法,并在 PIP 模式下每秒发送日期更新事件:

Kotlin 示例(PipModule.kt)

package com.yourappname

import android.app.PictureInPictureParams
import android.os.Build
import android.os.Handler
import android.os.Looper
import android.util.Rational
import com.facebook.react.bridge.ReactApplicationContext
import com.facebook.react.bridge.ReactContextBaseJavaModule
import com.facebook.react.bridge.ReactMethod
import com.facebook.react.modules.core.DeviceEventManagerModule
import java.text.SimpleDateFormat
import java.util.*

class PipModule(reactContext: ReactApplicationContext) : ReactContextBaseJavaModule(reactContext) {
    private val handler = Handler(Looper.getMainLooper())
    private val dateFormat = SimpleDateFormat("yyyy-MM-dd HH:mm:ss", Locale.getDefault())
    private val updateRunnable = object : Runnable {
        override fun run() {
            val currentDate = dateFormat.format(Date())
            // 发送事件到 JS 层
            reactApplicationContext
                .getJSModule(DeviceEventManagerModule.RCTDeviceEventEmitter::class.java)
                .emit("onPipDateUpdate", currentDate)
            handler.postDelayed(this, 1000)
        }
    }

    override fun getName() = "PipModule"

    @ReactMethod
    fun enterPip() {
        currentActivity?.let { activity ->
            if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.O) {
                val params = PictureInPictureParams.Builder()
                    .setAspectRatio(Rational(16, 9)) // 自定义宽高比
                    .build()
                activity.enterPictureInPictureMode(params)
            }
        }
    }

    override fun onActivityPaused() {
        super.onActivityPaused()
        // 检查是否处于 PIP 模式,启动定时器
        currentActivity?.let {
            if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.N && it.isInPictureInPictureMode) {
                handler.post(updateRunnable)
            }
        }
    }

    override fun onActivityResumed() {
        super.onActivityResumed()
        // 退出 PIP 时停止定时器
        handler.removeCallbacks(updateRunnable)
    }
}

注册模块(Package.kt)

package com.yourappname

import com.facebook.react.ReactPackage
import com.facebook.react.bridge.NativeModule
import com.facebook.react.bridge.ReactApplicationContext
import com.facebook.react.uimanager.ViewManager

class PipPackage : ReactPackage {
    override fun createNativeModules(reactContext: ReactApplicationContext): List<NativeModule> {
        return listOf(PipModule(reactContext))
    }

    override fun createViewManagers(reactContext: ReactApplicationContext): List<ViewManager<*, *>> {
        return emptyList()
    }
}

然后在 MainApplication.kt 中注册这个 Package:

override fun getPackages(): List<ReactPackage> {
    return PackageList(this).packages.apply {
        add(PipPackage())
    }
}

React Native JS 层实现

1. 监听日期更新并渲染UI

import React, { useState, useEffect } from 'react';
import { View, Text, Button, NativeEventEmitter, NativeModules } from 'react-native';

const { PipModule } = NativeModules;
const pipEmitter = new NativeEventEmitter(PipModule);

const PipScreen = () => {
    const [currentDate, setCurrentDate] = useState(new Date().toLocaleString());

    useEffect(() => {
        // 监听原生发送的日期更新事件
        const subscription = pipEmitter.addListener('onPipDateUpdate', (date) => {
            setCurrentDate(date);
        });

        return () => subscription.remove();
    }, []);

    const enterPipMode = async () => {
        try {
            await PipModule.enterPip();
        } catch (err) {
            console.error('进入PIP模式失败:', err);
        }
    };

    return (
        <View style={{ flex: 1, justifyContent: 'center', alignItems: 'center' }}>
            <Text style={{ fontSize: 20, marginBottom: 20 }}>{currentDate}</Text>
            <Button title="进入画中画模式" onPress={enterPipMode} />
        </View>
    );
};

export default PipScreen;

iOS 端补充方案

iOS 的 PIP 原生支持主要针对视频播放,但如果要显示自定义UI,需要将组件渲染到 AVPlayerLayer 中,或者使用 iOS 15+ 的 UIWindowScene PIP 能力:

  1. 配置 Info.plist 添加 UISupportsPictureInPicture 为 YES
  2. 使用 AVPictureInPictureController 承载自定义视图,通过定时器更新日期并刷新图层

不过相对 Android 来说,iOS 自定义 PIP UI 的复杂度更高,建议优先保证 Android 端实现,再针对 iOS 做适配。


内容的提问来源于stack exchange,提问作者Suraj Deshmane

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.08.09 18:45:31