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

如何在Xamarin Android中实现带关闭按钮的Overlay Window通知,以自定义视图替代FCM推送的常规通知

Implement Custom Overlay Window for FCM Notifications in Xamarin.Forms Android

Hey there! I’ve tackled this exact scenario before—here’s a step-by-step guide to skip the default Android system notification and show a custom overlay window when an FCM push arrives, complete with a close button.

1. First, Request Required Permissions

Overlay windows need specific permissions, and they vary a bit across Android versions.

Add Permissions to AndroidManifest.xml

Open your Android project’s Properties/AndroidManifest.xml and add these critical permissions:

<uses-permission android:name="android.permission.SYSTEM_ALERT_WINDOW" />
<uses-permission android:name="android.permission.POST_NOTIFICATIONS" /> <!-- Android 13+ -->
<uses-permission android:name="android.permission.SYSTEM_OVERLAY_WINDOW" />

Dynamic Permission Request (Critical for Android 6.0+)

Manifest permissions aren’t enough—you need to prompt users for overlay access at runtime. Add this code to your MainActivity (or a dedicated permission handler):

private void RequestOverlayPermission()
{
    if (!Android.Provider.Settings.CanDrawOverlays(this))
    {
        var intent = new Intent(Android.Provider.Settings.ActionManageOverlayPermission,
            Android.Net.Uri.Parse("package:" + PackageName));
        StartActivityForResult(intent, 1001);
    }
}

// Handle permission result
protected override void OnActivityResult(int requestCode, Result resultCode, Intent data)
{
    base.OnActivityResult(requestCode, resultCode, data);
    if (requestCode == 1001)
    {
        if (Android.Provider.Settings.CanDrawOverlays(this))
        {
            // Permission granted, overlay windows are now allowed
        }
        else
        {
            Toast.MakeText(this, "Overlay permission is required to show custom notifications", ToastLength.Long).Show();
        }
    }
}

2. Configure FCM to Skip Default Notifications

The key here is to use Data Messages instead of Notification Messages when sending FCM pushes. Notification Messages are handled by the Android system directly in the background, but Data Messages are delivered to your FirebaseMessagingService regardless of app state.

Update FirebaseMessagingService

Create (or modify) your MyFirebaseMessagingService class to override OnMessageReceived—skip the default SendNotification method and trigger your overlay instead:

[Service]
[IntentFilter(new[] { "com.google.firebase.MESSAGING_EVENT" })]
public class MyFirebaseMessagingService : FirebaseMessagingService
{
    public override void OnMessageReceived(RemoteMessage message)
    {
        base.OnMessageReceived(message);

        // Extract data from FCM payload
        var title = message.Data.TryGetValue("title", out var t) ? t : "New Notification";
        var body = message.Data.TryGetValue("body", out var b) ? b : "You have a new message";

        // Check permission before showing overlay
        if (Android.Provider.Settings.CanDrawOverlays(this))
        {
            ShowOverlayWindow(title, body);
        }
        else
        {
            // Fallback: show default notification if permission is missing
            SendDefaultNotification(title, body);
        }
    }

    private void SendDefaultNotification(string title, string body)
    {
        // Standard system notification as fallback
        var notificationManager = GetSystemService(Context.NotificationService) as NotificationManager;
        var channelId = "default_channel";
        
        if (Build.VERSION.SdkInt >= BuildVersionCodes.O)
        {
            var channel = new NotificationChannel(channelId, "Default", NotificationImportance.Default);
            notificationManager.CreateNotificationChannel(channel);
        }

        var intent = new Intent(this, typeof(MainActivity));
        intent.AddFlags(ActivityFlags.ClearTop);
        var pendingIntent = PendingIntent.GetActivity(this, 0, intent, PendingIntentFlags.OneShot | PendingIntentFlags.Immutable);
        
        var notificationBuilder = new NotificationCompat.Builder(this, channelId)
            .SetSmallIcon(Resource.Drawable.ic_notification)
            .SetContentTitle(title)
            .SetContentText(body)
            .SetAutoCancel(true)
            .SetContentIntent(pendingIntent);
        
        notificationManager.Notify(0, notificationBuilder.Build());
    }
}

3. Implement the Custom Overlay Window

Now let’s build the overlay window with a close button using WindowManager to add a custom view to the screen.

Create the Overlay Layout

Add a new layout file Resources/layout/OverlayNotification.axml:

<?xml version="1.0" encoding="utf-8"?>
<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
    android:layout_width="match_parent"
    android:layout_height="wrap_content"
    android:background="#FFFFFF"
    android:orientation="vertical"
    android:padding="16dp"
    android:elevation="8dp"
    android:layout_margin="16dp">

    <TextView
        android:id="@+id/tv_title"
        android:layout_width="match_parent"
        android:layout_height="wrap_content"
        android:textSize="18sp"
        android:textStyle="bold"
        android:textColor="#000000"/>

    <TextView
        android:id="@+id/tv_body"
        android:layout_width="match_parent"
        android:layout_height="wrap_content"
        android:textSize="14sp"
        android:textColor="#333333"
        android:layout_marginTop="8dp"/>

    <Button
        android:id="@+id/btn_close"
        android:layout_width="wrap_content"
        android:layout_height="wrap_content"
        android:text="Close"
        android:layout_gravity="end"
        android:layout_marginTop="16dp"/>
</LinearLayout>

Add Overlay Window Logic

Add this ShowOverlayWindow method to your MyFirebaseMessagingService (or a dedicated helper class):

private void ShowOverlayWindow(string title, string body)
{
    var windowManager = GetSystemService(Context.WindowService) as WindowManager;
    var inflater = LayoutInflater.From(this);
    var overlayView = inflater.Inflate(Resource.Layout.OverlayNotification, null);

    // Populate notification content
    overlayView.FindViewById<TextView>(Resource.Id.tv_title).Text = title;
    overlayView.FindViewById<TextView>(Resource.Id.tv_body).Text = body;

    // Configure close button behavior
    var closeBtn = overlayView.FindViewById<Button>(Resource.Id.btn_close);
    closeBtn.Click += (s, e) =>
    {
        windowManager.RemoveView(overlayView);
    };

    // Set overlay layout parameters
    var layoutParams = new WindowManagerLayoutParams();
    if (Build.VERSION.SdkInt >= BuildVersionCodes.O)
    {
        layoutParams.Type = WindowManagerTypes.ApplicationOverlay;
    }
    else
    {
        layoutParams.Type = WindowManagerTypes.Phone;
    }
    
    layoutParams.Flags = WindowManagerFlags.NotTouchModal | WindowManagerFlags.NotFocusable;
    layoutParams.Width = WindowManagerLayoutParams.MatchParent;
    layoutParams.Height = WindowManagerLayoutParams.WrapContent;
    layoutParams.Format = Android.Graphics.Format.Translucent;
    layoutParams.Gravity = GravityFlags.Top | GravityFlags.CenterHorizontal;

    // Add the view to the screen
    windowManager.AddView(overlayView, layoutParams);
}

4. Important Notes & Edge Cases

  • App State Handling: Track app foreground/background state with ActivityLifecycleCallbacks to avoid showing overlays when users are actively interacting with your app.
  • Memory Leaks: Always remove the overlay view when it’s no longer needed (e.g., app resumes, user clicks close). Use weak references if needed to prevent leaks.
  • Android 10+ Restrictions: For Android 10 and above, you may need additional permissions if showing overlays while the app is in the background—test thoroughly on target versions.
  • Fallback Logic: Never skip the default notification fallback—users without overlay permission should still get notified.

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

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.04.27 19:37:30