diff --git a/maui/src/NumericEntry/SfNumericEntry.Methods.cs b/maui/src/NumericEntry/SfNumericEntry.Methods.cs
index 32aaf8df..9c4652f1 100644
--- a/maui/src/NumericEntry/SfNumericEntry.Methods.cs
+++ b/maui/src/NumericEntry/SfNumericEntry.Methods.cs
@@ -2756,6 +2756,25 @@ void WindowEntryHandler(object? sender)
}
}
+ ///
+ /// Cleans up Android entry event handlers to prevent memory leaks.
+ ///
+ void CleanupAndroidEntryEvents()
+ {
+#if ANDROID
+ if (_androidEntry != null)
+ {
+ _androidEntry.EditorAction -= AndroidEntry_EditorAction;
+ _androidEntry.TextChanged -= OnTextBoxTextChanged;
+ _androidEntry.BeforeTextChanged -= AndroidEntry_BeforeTextChanged;
+ _androidEntry.AfterTextChanged -= AndroidEntry_AfterTextChanged;
+ _androidEntry.Click -= AndroidEntry_Click;
+ _androidEntry.FocusChange -= AndroidEntry_FocusChange;
+ _androidEntry = null;
+ }
+#endif
+ }
+
///
/// Configures platform-specific behavior for Android Entry controls.
/// This method is called when the Entry's handler changes.
@@ -2764,16 +2783,27 @@ void WindowEntryHandler(object? sender)
void AndroidEntryHandler(object? sender)
{
#if ANDROID
- if ((sender is SfEntryView textBox) && textBox.Handler != null && textBox.Handler.PlatformView is AndroidX.AppCompat.Widget.AppCompatEditText androidEntry)
+ if (sender is SfEntryView textBox)
{
- androidEntry.EditorAction += AndroidEntry_EditorAction;
- androidEntry.TextChanged += OnTextBoxTextChanged;
- androidEntry.BeforeTextChanged += AndroidEntry_BeforeTextChanged;
- androidEntry.AfterTextChanged += AndroidEntry_AfterTextChanged;
- androidEntry.KeyListener = global::Android.Text.Method.DigitsKeyListener.GetInstance(KEYS);
- androidEntry.Click += AndroidEntry_Click;
- // Disable EmojiCompat to prevent the IllegalArgumentException crash on Android when start typing text.
- androidEntry.EmojiCompatEnabled = false;
+ // Clean up existing event handlers if we have a previous Android entry
+ CleanupAndroidEntryEvents();
+
+ // Subscribe to new handler's platform view if available
+ if (textBox.Handler != null && textBox.Handler.PlatformView is AndroidX.AppCompat.Widget.AppCompatEditText androidEntry)
+ {
+ // Store reference to the Android entry for cleanup
+ _androidEntry = androidEntry;
+
+ androidEntry.EditorAction += AndroidEntry_EditorAction;
+ androidEntry.TextChanged += OnTextBoxTextChanged;
+ androidEntry.BeforeTextChanged += AndroidEntry_BeforeTextChanged;
+ androidEntry.AfterTextChanged += AndroidEntry_AfterTextChanged;
+ androidEntry.KeyListener = global::Android.Text.Method.DigitsKeyListener.GetInstance(KEYS);
+ androidEntry.Click += AndroidEntry_Click;
+ androidEntry.FocusChange += AndroidEntry_FocusChange;
+ // Disable EmojiCompat to prevent the IllegalArgumentException crash on Android when start typing text.
+ androidEntry.EmojiCompatEnabled = false;
+ }
}
#endif
}
@@ -2877,6 +2907,58 @@ void AndroidEntry_Click(object? sender, EventArgs e)
}
}
}
+
+ ///
+ /// Handles the focus change event for an Android Entry control.
+ /// Ensures proper value update and formatting when focus is lost.
+ /// This fixes the issue where clicking a button doesn't update the value
+ /// or dismiss the keyboard because the entry doesn't properly lose focus.
+ ///
+ /// The object that triggered the event, typically an Android Entry component.
+ /// The focus change event arguments.
+ void AndroidEntry_FocusChange(object? sender, Android.Views.View.FocusChangeEventArgs e)
+ {
+ // Capture local references to avoid race conditions
+ // The _androidEntry and _textBox could be set to null on another thread
+ var androidEntry = _androidEntry;
+ var textBox = _textBox;
+
+ // Verify references are valid before processing
+ if (sender == null || androidEntry == null || textBox == null)
+ {
+ return;
+ }
+
+ // When focus is lost (HasFocus is false), ensure the MAUI layer is notified
+ // This is crucial for Android where tapping outside (e.g., on a button)
+ // doesn't always properly sync the native focus state with MAUI's focus state
+ if (!e.HasFocus)
+ {
+ // The native Android entry has lost focus
+ // Check if MAUI's Entry is still reporting as focused
+ // If so, we need to trigger the unfocus to ensure proper event handling
+ MainThread.BeginInvokeOnMainThread(() =>
+ {
+ try
+ {
+ // Use the local captured references to avoid race conditions
+ // Only proceed if the textBox is still focused
+ if (textBox.IsFocused)
+ {
+ // MAUI still thinks the entry is focused, but the native control has lost focus
+ // This can happen when clicking outside (e.g., on a button)
+ // Calling Unfocus() will trigger TextBoxOnLostFocus -> OnLostFocus() -> UpdateValue() -> FormatValue()
+ textBox.Unfocus();
+ }
+ }
+ catch (ObjectDisposedException)
+ {
+ // The control was disposed between capturing the reference and using it
+ // This is safe to ignore as the control no longer needs to be unfocused
+ }
+ });
+ }
+ }
#endif
///
@@ -2895,6 +2977,9 @@ void UnHookEvents()
_textBox.Unfocused -= TextBoxOnLostFocus;
}
+
+ // Clean up Android-specific event handlers
+ CleanupAndroidEntryEvents();
}
#endregion
diff --git a/maui/src/NumericEntry/SfNumericEntry.cs b/maui/src/NumericEntry/SfNumericEntry.cs
index e4451e22..47e49af0 100644
--- a/maui/src/NumericEntry/SfNumericEntry.cs
+++ b/maui/src/NumericEntry/SfNumericEntry.cs
@@ -300,6 +300,11 @@ public partial class SfNumericEntry : SfView, ITextElement, ITouchListener, IKey
///
bool _isFirstFocus = true;
+ ///
+ /// Represents the underlying Android AppCompatEditText used in the control.
+ ///
+ AndroidX.AppCompat.Widget.AppCompatEditText? _androidEntry;
+
#endif
#if !WINDOWS