如何在Xamarin.Forms中创建类似录屏样式的自定义弹窗?
嘿,我来帮你搞定在Xamarin.Forms里打造录屏样式的自定义弹窗这件事!下面是一步步的实现方案,从UI到原生逻辑都给你安排明白:
如何在Xamarin.Forms中创建录屏样式的自定义弹窗
第一步:搭建自定义弹窗的UI结构
我们先做一个带录屏控制按钮的弹窗UI,这里用ContentView配合Frame来模拟弹窗效果,也可以用Rg.Plugins.Popup插件简化弹窗管理,不过先讲原生实现方式:
<ContentView xmlns="http://xamarin.com/schemas/2014/forms" xmlns:x="http://schemas.microsoft.com/winfx/2009/xaml" x:Class="YourApp.Views.ScreenRecorderPopup"> <Frame CornerRadius="12" Padding="20" BackgroundColor="#2D2D2D" WidthRequest="320" HeightRequest="220"> <StackLayout Spacing="18"> <!-- 录屏状态提示 --> <Label x:Name="StatusLabel" Text="准备开始录屏" TextColor="#FFFFFF" HorizontalOptions="Center" FontSize="16"/> <!-- 可选:实时预览区域(需要原生层传递画面) --> <Image x:Name="PreviewImage" HeightRequest="110" WidthRequest="190" HorizontalOptions="Center" BackgroundColor="#1A1A1A"/> <!-- 控制按钮组 --> <StackLayout Orientation="Horizontal" HorizontalOptions="Center" Spacing="25"> <Button x:Name="StartBtn" Text="开始" Clicked="StartBtn_Clicked" BackgroundColor="#4CAF50" TextColor="White" WidthRequest="90" CornerRadius="8"/> <Button x:Name="StopBtn" Text="停止" Clicked="StopBtn_Clicked" BackgroundColor="#F44336" TextColor="White" WidthRequest="90" CornerRadius="8" IsEnabled="False"/> </StackLayout> </StackLayout> </Frame> </ContentView>
然后写弹窗的后台逻辑,处理按钮点击和状态切换:
using Xamarin.Forms; namespace YourApp.Views { public partial class ScreenRecorderPopup : ContentView { // 定义事件,通知宿主页面录屏结束 public event System.EventHandler<string> RecordingCompleted; public ScreenRecorderPopup() { InitializeComponent(); } private void StartBtn_Clicked(object sender, EventArgs e) { ToggleRecordingState(true); DependencyService.Get<IScreenRecorder>().StartRecording(); } private void StopBtn_Clicked(object sender, EventArgs e) { ToggleRecordingState(false); var videoPath = DependencyService.Get<IScreenRecorder>().StopRecording(); RecordingCompleted?.Invoke(this, videoPath); } // 切换按钮状态和提示文本 private void ToggleRecordingState(bool isRecording) { StatusLabel.Text = isRecording ? "正在录屏..." : "录屏已停止"; StartBtn.IsEnabled = !isRecording; StopBtn.IsEnabled = isRecording; } } }
第二步:定义跨平台录屏服务接口
因为Xamarin.Forms本身没有提供录屏能力,我们用依赖服务调用原生平台的API,先定义统一接口:
namespace YourApp.Services { public interface IScreenRecorder { void StartRecording(); string StopRecording(); // 返回录制好的视频文件路径 void PauseRecording(); // 可选:添加暂停功能 } }
第三步:实现Android平台的录屏逻辑
Android需要用到MediaProjection和MediaRecorder,还要处理录屏权限申请,核心代码如下:
using Android.App; using Android.Content; using Android.Media; using Android.OS; using Android.Views; using YourApp.Droid.Services; using YourApp.Services; using Xamarin.Forms; [assembly: Dependency(typeof(AndroidScreenRecorder))] namespace YourApp.Droid.Services { public class AndroidScreenRecorder : IScreenRecorder { private MediaProjection _mediaProjection; private MediaRecorder _mediaRecorder; private VirtualDisplay _virtualDisplay; private string _outputFilePath; public void StartRecording() { var context = Android.App.Application.Context; var displayMetrics = context.Resources.DisplayMetrics; int screenWidth = displayMetrics.WidthPixels; int screenHeight = displayMetrics.HeightPixels; // 创建视频输出路径 _outputFilePath = System.IO.Path.Combine( Android.OS.Environment.ExternalStorageDirectory.AbsolutePath, $"ScreenRecord_{DateTime.Now:yyyyMMddHHmmss}.mp4"); // 初始化MediaRecorder _mediaRecorder = new MediaRecorder(); _mediaRecorder.SetAudioSource(AudioSource.Mic); _mediaRecorder.SetVideoSource(VideoSource.Surface); _mediaRecorder.SetOutputFormat(OutputFormat.Mpeg4); _mediaRecorder.SetVideoEncoder(VideoEncoder.H264); _mediaRecorder.SetAudioEncoder(AudioEncoder.Aac); _mediaRecorder.SetVideoSize(screenWidth, screenHeight); _mediaRecorder.SetVideoFrameRate(30); _mediaRecorder.SetOutputFile(_outputFilePath); _mediaRecorder.Prepare(); // 请求录屏权限(需要在MainActivity中处理回调) var projectionManager = (MediaProjectionManager)context.GetSystemService(Context.MediaProjectionService); var captureIntent = projectionManager.CreateScreenCaptureIntent(); ((MainActivity)Forms.Context).StartActivityForResult(captureIntent, 1001); } public string StopRecording() { _mediaRecorder?.Stop(); _mediaRecorder?.Release(); _virtualDisplay?.Release(); _mediaProjection?.Stop(); return _outputFilePath; } // 供MainActivity调用,拿到权限后启动录屏 public void StartRecordingWithProjection(MediaProjection projection) { _mediaProjection = projection; var displayMetrics = Android.App.Application.Context.Resources.DisplayMetrics; _virtualDisplay = _mediaProjection.CreateVirtualDisplay( "ScreenRecorder", displayMetrics.WidthPixels, displayMetrics.HeightPixels, displayMetrics.DensityDpi, DisplayFlags.Presentation, _mediaRecorder.Surface, null, null); _mediaRecorder.Start(); } } }
然后在MainActivity中处理权限回调:
protected override void OnActivityResult(int requestCode, Result resultCode, Intent data) { base.OnActivityResult(requestCode, resultCode, data); if (requestCode == 1001 && resultCode == Result.Ok) { var projectionManager = (MediaProjectionManager)GetSystemService(Context.MediaProjectionService); var projection = projectionManager.GetMediaProjection((int)resultCode, data); DependencyService.Get<IScreenRecorder>().StartRecordingWithProjection(projection); } }
第四步:实现iOS平台的录屏逻辑
iOS用ReplayKit框架实现录屏,核心代码:
using YourApp.iOS.Services; using YourApp.Services; using Foundation; using ReplayKit; using UIKit; using Xamarin.Forms; [assembly: Dependency(typeof(iOSScreenRecorder))] namespace YourApp.iOS.Services { public class iOSScreenRecorder : IScreenRecorder, RPPreviewViewControllerDelegate { private RPScreenRecorder _screenRecorder; private string _tempVideoPath; public iOSScreenRecorder() { _screenRecorder = RPScreenRecorder.SharedRecorder; } public void StartRecording() { if (!_screenRecorder.Available) { // 录屏不可用,提示用户 Device.BeginInvokeOnMainThread(() => { Application.Current.MainPage.DisplayAlert("提示", "当前设备不支持录屏", "确定"); }); return; } _screenRecorder.StartRecording(new RPScreenRecorderDelegate(), (NSError error) => { if (error != null) { Device.BeginInvokeOnMainThread(() => { Application.Current.MainPage.DisplayAlert("错误", $"启动录屏失败:{error.LocalizedDescription}", "确定"); }); } }); } public string StopRecording() { _screenRecorder.StopRecording((previewVC, error) => { if (error != null) { Device.BeginInvokeOnMainThread(() => { Application.Current.MainPage.DisplayAlert("错误", $"停止录屏失败:{error.LocalizedDescription}", "确定"); }); return; } if (previewVC != null) { previewVC.Delegate = this; UIApplication.SharedApplication.KeyWindow.RootViewController.PresentViewController(previewVC, true, null); } }); return _tempVideoPath; } [Export("previewControllerDidFinish:")] public void DidFinish(RPPreviewViewController previewViewController) { previewViewController.DismissViewController(true, null); } } }
第五步:在页面中调用显示弹窗
在你的主页面中添加触发按钮,动态将弹窗添加到页面:
<ContentPage xmlns="http://xamarin.com/schemas/2014/forms" xmlns:x="http://schemas.microsoft.com/winfx/2009/xaml" xmlns:views="clr-namespace:YourApp.Views" x:Class="YourApp.MainPage"> <StackLayout Padding="20" VerticalOptions="Center"> <Button Text="打开录屏工具" Clicked="OpenRecorderPopup_Clicked" FontSize="16"/> <AbsoluteLayout x:Name="PopupHost" AbsoluteLayout.LayoutBounds="0,0,1,1" AbsoluteLayout.LayoutFlags="All"/> </StackLayout> </ContentPage>
后台代码:
private void OpenRecorderPopup_Clicked(object sender, EventArgs e) { var recorderPopup = new ScreenRecorderPopup(); recorderPopup.RecordingCompleted += RecorderPopup_RecordingCompleted; // 将弹窗居中显示 AbsoluteLayout.SetLayoutBounds(recorderPopup, new Rectangle(0.5, 0.5, AbsoluteLayout.AutoSize, AbsoluteLayout.AutoSize)); AbsoluteLayout.SetLayoutFlags(recorderPopup, AbsoluteLayoutFlags.PositionProportional); PopupHost.Children.Add(recorderPopup); } private void RecorderPopup_RecordingCompleted(object sender, string videoPath) { PopupHost.Children.Clear(); DisplayAlert("录屏完成", $"视频已保存到:{videoPath}", "好的"); }
关键注意事项
- 权限声明:Android需要在
AndroidManifest.xml中添加WRITE_EXTERNAL_STORAGE、RECORD_AUDIO权限;iOS需要在Info.plist中添加NSMicrophoneUsageDescription权限描述。 - 版本兼容性:Android 5.0(API 21)以上支持
MediaProjection,iOS 9.0以上支持ReplayKit。 - 错误处理:要添加权限拒绝、录屏不可用等场景的提示,提升用户体验。
内容的提问来源于stack exchange,提问作者Parmendra
相关产品推荐
相关产品推荐

