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

Xamarin Forms Android实现VideoView退出全屏模式的方案咨询

解决XamarinMediaManager(0.8.11)Android平台退出全屏问题

针对你遇到的VideoView无法响应TapGestureRecognizer、无法退出全屏的问题,结合0.8.11这个老版本的特性,我给你几个可行的解决思路:

一、核心问题原因

你遇到的TapGestureRecognizer失效,是因为Android平台下MediaManager的VideoView底层是原生SurfaceView,Xamarin.Forms的手势识别器无法穿透原生控件触发事件,所以得通过自定义Android渲染器来处理原生点击和全屏切换逻辑。

二、具体实现方案(自定义渲染器)

这个方案能完美实现“点击视频切换全屏/退出全屏”,同时保留你原来的按钮触发逻辑:

1. 共享项目创建自定义VideoView控件

先封装一个带全屏状态和切换事件的自定义控件:

using MediaManager.Forms;
using Xamarin.Forms;

namespace HlsViewer.Controls
{
    public class CustomVideoView : VideoView
    {
        // 绑定全屏状态属性
        public static readonly BindableProperty IsFullScreenProperty =
            BindableProperty.Create(nameof(IsFullScreen), typeof(bool), typeof(CustomVideoView), false);

        public bool IsFullScreen
        {
            get => (bool)GetValue(IsFullScreenProperty);
            set => SetValue(IsFullScreenProperty, value);
        }

        // 定义全屏切换请求事件
        public event EventHandler ToggleFullScreenRequested;

        public void OnToggleFullScreen()
        {
            ToggleFullScreenRequested?.Invoke(this, EventArgs.Empty);
        }
    }
}

2. Android项目编写自定义渲染器

在Android项目中创建渲染器,监听原生控件的点击事件,并处理全屏切换的原生逻辑:

using Android.App;
using Android.Content;
using Android.Views;
using HlsViewer.Controls;
using HlsViewer.Droid.Renderers;
using MediaManager.Forms;
using MediaManager.Forms.Android;
using Xamarin.Forms;
using Xamarin.Forms.Platform.Android;

[assembly: ExportRenderer(typeof(CustomVideoView), typeof(CustomVideoViewRenderer))]
namespace HlsViewer.Droid.Renderers
{
    public class CustomVideoViewRenderer : VideoViewRenderer
    {
        private CustomVideoView _formsVideoView;
        private bool _isFullScreen;
        private ViewGroup _originalParent;
        private WindowManagerLayoutParams _originalLayoutParams;

        public CustomVideoViewRenderer(Context context) : base(context)
        {
        }

        protected override void OnElementChanged(ElementChangedEventArgs<VideoView> e)
        {
            base.OnElementChanged(e);

            if (e.NewElement != null)
            {
                _formsVideoView = (CustomVideoView)e.NewElement;
                // 监听原生VideoView的点击事件
                Control.Click += Control_Click;
                // 绑定全屏切换事件
                _formsVideoView.ToggleFullScreenRequested += FormsVideoView_ToggleFullScreenRequested;
            }

            if (e.OldElement != null)
            {
                Control.Click -= Control_Click;
                _formsVideoView.ToggleFullScreenRequested -= FormsVideoView_ToggleFullScreenRequested;
            }
        }

        // 原生点击触发全屏切换
        private void Control_Click(object sender, EventArgs e)
        {
            _formsVideoView.OnToggleFullScreen();
        }

        // 处理全屏切换逻辑
        private void FormsVideoView_ToggleFullScreenRequested(object sender, EventArgs e)
        {
            ToggleFullScreen(!_isFullScreen);
        }

        private void ToggleFullScreen(bool enable)
        {
            _isFullScreen = enable;
            _formsVideoView.IsFullScreen = enable;

            var activity = Context as Activity;
            if (activity == null) return;

            if (enable)
            {
                // 保存原布局状态
                _originalParent = (ViewGroup)Control.Parent;
                _originalLayoutParams = Control.LayoutParameters;

                // 进入全屏:隐藏系统UI,将VideoView挂载到Window根布局
                var window = activity.Window;
                window.AddFlags(WindowManagerFlags.Fullscreen | WindowManagerFlags.LayoutNoLimits);
                window.DecorView.SystemUiVisibility = (StatusBarVisibility)(SystemUiFlags.HideNavigation | SystemUiFlags.Fullscreen | SystemUiFlags.ImmersiveSticky);

                var decorView = window.DecorView as ViewGroup;
                decorView.AddView(Control, new ViewGroup.LayoutParams(ViewGroup.LayoutParams.MatchParent, ViewGroup.LayoutParams.MatchParent));
            }
            else
            {
                // 退出全屏:恢复系统UI,将VideoView放回原布局
                var window = activity.Window;
                window.ClearFlags(WindowManagerFlags.Fullscreen | WindowManagerFlags.LayoutNoLimits);
                window.DecorView.SystemUiVisibility = StatusBarVisibility.Visible;

                var decorView = window.DecorView as ViewGroup;
                decorView.RemoveView(Control);
                _originalParent.AddView(Control, _originalLayoutParams);
            }
        }
    }
}

3. 修改XAML和后台代码

  • 替换XAML中的mm:VideoView为自定义控件:
<ContentPage xmlns="http://xamarin.com/schemas/2014/forms" 
             xmlns:x="http://schemas.microsoft.com/winfx/2009/xaml" 
             xmlns:d="http://xamarin.com/schemas/2014/forms/design" 
             xmlns:mc="http://schemas.openxmlformats.org/markup-compatibility/2006" 
             xmlns:mm="clr-namespace:MediaManager.Forms;assembly=MediaManager.Forms"
             xmlns:controls="clr-namespace:HlsViewer.Controls"
             mc:Ignorable="d" 
             x:Class="HlsViewer.Views.AboutPage" 
             xmlns:vm="clr-namespace:HlsViewer.ViewModels" 
             Appearing="ContentPage_Appearing" 
             Title="{Binding Title}">
 <!-- 其余代码不变 -->
 <controls:CustomVideoView VerticalOptions="FillAndExpand" HorizontalOptions="FillAndExpand" x:Name="mmVideoView" />
 <!-- 其余代码不变 -->
</ContentPage>
  • 后台代码绑定事件,同时保留按钮触发逻辑:
private void ContentPage_Appearing(object sender, EventArgs e) {
 base.OnAppearing();
 Device.BeginInvokeOnMainThread(async () => {
 // 监听全屏切换事件,可在此处理UI显示/隐藏(比如按钮)
 mmVideoView.ToggleFullScreenRequested += MmVideoView_ToggleFullScreenRequested;

 var item = await CrossMediaManager.Current.Extractor.CreateMediaItem("http://qthttp.apple.com.edgesuite.net/1010qwoeiuryfg/sl.m3u8");
 item.MediaType = MediaManager.Library.MediaType.Hls;
 await CrossMediaManager.Current.Play(item);
 });
}

private void MmVideoView_ToggleFullScreenRequested(object sender, EventArgs e)
{
    // 全屏状态切换时,控制按钮显示/隐藏
    yourButton.IsVisible = !mmVideoView.IsFullScreen;
}

// 原按钮点击事件,手动触发全屏切换
private void Button_Clicked(object sender, EventArgs e)
{
    mmVideoView.OnToggleFullScreen();
}

// 可选:监听返回键退出全屏
protected override bool OnBackButtonPressed()
{
    if (mmVideoView.IsFullScreen)
    {
        mmVideoView.OnToggleFullScreen();
        return true; // 拦截默认返回行为
    }
    return base.OnBackButtonPressed();
}

三、替代方案:升级MediaManager版本

你的0.8.11版本是比较老旧的分支,MediaManager在2.x版本之后做了大量重构,已经自带全屏切换命令和手势支持,无需自定义渲染器就能实现需求。不过升级需要调整原有代码(API变化较大),如果项目允许的话,这是更省心的方案。

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

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.05.14 08:33:50