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
相关产品推荐
相关产品推荐

