From a1546bf78ea905cff5b2a12bda7f95480ba68bf3 Mon Sep 17 00:00:00 2001 From: Thom Ritterfeld Date: Tue, 9 Dec 2025 12:06:02 +0100 Subject: [PATCH] Fix Android crash when changing shared Drawable tint on Searchbar --- .../Platform/Android/SearchViewExtensions.cs | 41 +++++++++++++------ 1 file changed, 28 insertions(+), 13 deletions(-) diff --git a/src/Core/src/Platform/Android/SearchViewExtensions.cs b/src/Core/src/Platform/Android/SearchViewExtensions.cs index 33d786aed442..45ca4cfc4ec0 100644 --- a/src/Core/src/Platform/Android/SearchViewExtensions.cs +++ b/src/Core/src/Platform/Android/SearchViewExtensions.cs @@ -44,7 +44,7 @@ public static void UpdatePlaceholderColor(this SearchView searchView, ISearchBar editText.SetHintTextColor(color); var searchMagIconImage = searchView.FindViewById(Resource.Id.search_mag_icon); - searchMagIconImage?.Drawable?.SetTint(color); + SafeSetTint(searchMagIconImage, color); } } @@ -57,7 +57,7 @@ internal static void UpdateTextColor(this SearchView searchView, ITextStyle entr editText.SetTextColor(color); var searchMagIconImage = searchView.FindViewById(Resource.Id.search_mag_icon); - searchMagIconImage?.Drawable?.SetTint(color); + SafeSetTint(searchMagIconImage, color); } } @@ -129,14 +129,10 @@ public static void UpdateCancelButtonColor(this SearchView searchView, ISearchBa if (searchCloseButtonIdentifier > 0) { var image = searchView.FindViewById(searchCloseButtonIdentifier); - - if (image is not null && image.Drawable is Drawable drawable) - { - if (searchBar.CancelButtonColor is not null) - drawable.SetColorFilter(searchBar.CancelButtonColor, FilterMode.SrcIn); - else if (TryGetDefaultStateColor(searchView, AAttribute.TextColorPrimary, out var color)) - drawable.SetColorFilter(color, FilterMode.SrcIn); - } + if (searchBar.CancelButtonColor is not null) + SafeSetTint(image, searchBar.CancelButtonColor.ToPlatform()); + else if (TryGetDefaultStateColor(searchView, AAttribute.TextColorPrimary, out var color)) + SafeSetTint(image, color); } } @@ -154,9 +150,9 @@ internal static void UpdateSearchIconColor(this SearchView searchView, ISearchBa if (image?.Drawable is not null) { if (searchBar.SearchIconColor is not null) - image.Drawable.SetColorFilter(searchBar.SearchIconColor, FilterMode.SrcIn); + SafeSetTint(image, searchBar.SearchIconColor.ToPlatform()); else - image.Drawable.ClearColorFilter(); + SafeSetTint(image, Color.Transparent); } } } @@ -240,5 +236,24 @@ static bool TryGetDefaultStateColor(SearchView searchView, int attribute, out Co color = new Color(cs.GetColorForState(state, Color.Black)); return true; } + + /// + /// Safely applies tint to an ImageView's drawable by mutating it first. + /// This prevents crashes when the drawable is shared across multiple views. + /// + /// + /// Android shares Drawable resources for memory efficiency. Modifying a shared + /// drawable without calling Mutate() first causes race conditions and crashes. + /// See: https://developer.android.com/reference/android/graphics/drawable/Drawable#mutate() + /// + internal static void SafeSetTint(ImageView? imageView, Color color) + { + if (imageView?.Drawable is not Drawable drawable) + return; + + var safe = drawable.Mutate(); + safe.SetTint(color); + imageView?.SetImageDrawable(safe); + } } -} \ No newline at end of file +}