Skip to content

Commit eac4fb8

Browse files
CopilotVetle444
andcommitted
Replace custom Android search field with actual Material 3 SearchBar + SearchView components
Co-authored-by: Vetle444 <35739538+Vetle444@users.noreply.github.com>
1 parent c941c2b commit eac4fb8

2 files changed

Lines changed: 53 additions & 94 deletions

File tree

CHANGELOG.md

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,6 @@
11
## [55.5.0]
22
- [iOS][BottomSheet] Replaced MAUI SearchBar with native UISearchController for bottom sheet search, integrating the search bar into the navigation bar
3-
- [Android][BottomSheet] Replaced MAUI SearchBar with native Material 3 styled search field for bottom sheet search
3+
- [Android][BottomSheet] Replaced MAUI SearchBar with Material 3 SearchBar and SearchView components for bottom sheet search
44

55
## [55.4.0]
66
- [iOS][BottomSheet] Use native UINavigationBar for bottom sheet header with centered title, system close/back buttons, and proper blur behavior
Lines changed: 52 additions & 93 deletions
Original file line numberDiff line numberDiff line change
@@ -1,129 +1,89 @@
11
using Android.Content;
2-
using Android.Graphics.Drawables;
32
using Android.Text;
43
using Android.Views;
54
using Android.Views.InputMethods;
65
using Android.Widget;
7-
using Microsoft.Maui.Platform;
86
using AView = Android.Views.View;
9-
using Colors = DIPS.Mobile.UI.Resources.Colors.Colors;
7+
using MaterialSearchBar = Google.Android.Material.Search.SearchBar;
8+
using MaterialSearchView = Google.Android.Material.Search.SearchView;
109

1110
namespace DIPS.Mobile.UI.Components.BottomSheets.Android;
1211

1312
/// <summary>
14-
/// A native Material 3 styled search field for use in BottomSheets.
15-
/// Uses an EditText with rounded background, search icon, and clear button
16-
/// to match the Material 3 search bar design.
13+
/// A Material 3 search component for BottomSheets using the actual
14+
/// <see cref="MaterialSearchBar"/> and <see cref="MaterialSearchView"/> components.
15+
/// The SearchBar provides the collapsed pill-shaped search trigger,
16+
/// while the SearchView provides the expanded search input with EditText.
1717
/// </summary>
18-
internal class BottomSheetSearchField : Java.Lang.Object, ITextWatcher, AView.IOnFocusChangeListener
18+
internal class BottomSheetSearchField : Java.Lang.Object, ITextWatcher, MaterialSearchView.ITransitionListener
1919
{
2020
private readonly WeakReference<BottomSheet> m_weakBottomSheet;
21-
private readonly EditText m_editText;
21+
private readonly MaterialSearchBar m_searchBar;
22+
private readonly MaterialSearchView m_searchView;
2223
private readonly FrameLayout m_container;
23-
private readonly ImageView m_searchIcon;
24-
private readonly ImageView m_clearButton;
2524
private string m_previousText = string.Empty;
2625

2726
public BottomSheetSearchField(Context context, BottomSheet bottomSheet)
2827
{
2928
m_weakBottomSheet = new WeakReference<BottomSheet>(bottomSheet);
3029

31-
var density = context.Resources?.DisplayMetrics?.Density ?? 1f;
32-
33-
// Create the outer container with M3 search bar styling
30+
// Create wrapper container
3431
m_container = new FrameLayout(context);
35-
var containerParams = new LinearLayout.LayoutParams(
32+
m_container.LayoutParameters = new LinearLayout.LayoutParams(
3633
ViewGroup.LayoutParams.MatchParent,
37-
(int)(56 * density)) // M3 search bar height is 56dp
38-
{
39-
LeftMargin = (int)(16 * density),
40-
RightMargin = (int)(16 * density),
41-
TopMargin = (int)(8 * density),
42-
BottomMargin = (int)(8 * density)
43-
};
44-
m_container.LayoutParameters = containerParams;
45-
46-
// Rounded background matching M3 search bar (pill shape)
47-
var background = new GradientDrawable();
48-
background.SetShape(ShapeType.Rectangle);
49-
background.SetCornerRadius(28 * density);
50-
background.SetColor(Colors.GetColor(ColorName.color_surface_subtle).ToPlatform());
51-
m_container.Background = background;
52-
53-
// Search icon
54-
m_searchIcon = new ImageView(context);
55-
m_searchIcon.SetImageResource(global::Android.Resource.Drawable.IcMenuSearch);
56-
m_searchIcon.SetColorFilter(Colors.GetColor(ColorName.color_icon_default).ToPlatform());
57-
var searchIconParams = new FrameLayout.LayoutParams(
58-
(int)(24 * density),
59-
(int)(24 * density),
60-
GravityFlags.CenterVertical | GravityFlags.Start);
61-
searchIconParams.LeftMargin = (int)(16 * density);
62-
m_searchIcon.LayoutParameters = searchIconParams;
63-
m_container.AddView(m_searchIcon);
64-
65-
// EditText
66-
m_editText = new EditText(context);
67-
m_editText.SetHintTextColor(Colors.GetColor(ColorName.color_text_subtle).ToPlatform());
68-
m_editText.SetTextColor(Colors.GetColor(ColorName.color_text_default).ToPlatform());
69-
m_editText.Background = null; // Transparent - container handles background
70-
m_editText.SetSingleLine(true);
71-
m_editText.ImeOptions = ImeAction.Search;
72-
m_editText.InputType = InputTypes.ClassText | InputTypes.TextFlagNoSuggestions;
73-
var editTextParams = new FrameLayout.LayoutParams(
34+
ViewGroup.LayoutParams.WrapContent);
35+
36+
// Material 3 SearchBar (pill-shaped search trigger)
37+
m_searchBar = new MaterialSearchBar(context);
38+
m_searchBar.LayoutParameters = new FrameLayout.LayoutParams(
39+
ViewGroup.LayoutParams.MatchParent,
40+
ViewGroup.LayoutParams.WrapContent);
41+
m_container.AddView(m_searchBar);
42+
43+
// Material 3 SearchView (expanded search with EditText)
44+
m_searchView = new MaterialSearchView(context);
45+
m_searchView.LayoutParameters = new FrameLayout.LayoutParams(
7446
ViewGroup.LayoutParams.MatchParent,
7547
ViewGroup.LayoutParams.MatchParent);
76-
editTextParams.LeftMargin = (int)(48 * density); // After search icon
77-
editTextParams.RightMargin = (int)(48 * density); // Before clear button
78-
m_editText.LayoutParameters = editTextParams;
79-
m_editText.SetPadding(0, 0, 0, 0);
80-
m_editText.AddTextChangedListener(this);
81-
m_editText.OnFocusChangeListener = this;
82-
m_container.AddView(m_editText);
83-
84-
// Clear button
85-
m_clearButton = new ImageView(context);
86-
m_clearButton.SetImageResource(global::Android.Resource.Drawable.IcMenuCloseClearCancel);
87-
m_clearButton.SetColorFilter(Colors.GetColor(ColorName.color_icon_default).ToPlatform());
88-
m_clearButton.Visibility = ViewStates.Gone;
89-
m_clearButton.Clickable = true;
90-
m_clearButton.Focusable = true;
91-
var clearParams = new FrameLayout.LayoutParams(
92-
(int)(24 * density),
93-
(int)(24 * density),
94-
GravityFlags.CenterVertical | GravityFlags.End);
95-
clearParams.RightMargin = (int)(16 * density);
96-
m_clearButton.LayoutParameters = clearParams;
97-
m_clearButton.Click += OnClearButtonClicked;
98-
m_container.AddView(m_clearButton);
48+
49+
// Connect SearchView to SearchBar for proper M3 transitions
50+
m_searchView.SetupWithSearchBar(m_searchBar);
51+
52+
// Listen for text changes on the SearchView's EditText
53+
m_searchView.EditText.AddTextChangedListener(this);
54+
55+
// Listen for transition events (show/hide)
56+
m_searchView.AddTransitionListener(this);
57+
58+
m_container.AddView(m_searchView);
9959
}
10060

10161
public AView View => m_container;
62+
63+
/// <summary>
64+
/// Returns the SearchBar for external layout if needed.
65+
/// </summary>
66+
public MaterialSearchBar SearchBar => m_searchBar;
10267

10368
public void Focus()
10469
{
105-
m_editText.RequestFocus();
106-
var imm = (InputMethodManager?)m_editText.Context?.GetSystemService(Context.InputMethodService);
107-
imm?.ShowSoftInput(m_editText, ShowFlags.Implicit);
70+
m_searchView.Show();
71+
m_searchView.EditText.RequestFocus();
72+
var imm = (InputMethodManager?)m_searchView.EditText.Context?.GetSystemService(Context.InputMethodService);
73+
imm?.ShowSoftInput(m_searchView.EditText, ShowFlags.Implicit);
10874
}
10975

11076
public void Unfocus()
11177
{
112-
m_editText.ClearFocus();
113-
var imm = (InputMethodManager?)m_editText.Context?.GetSystemService(Context.InputMethodService);
114-
imm?.HideSoftInputFromWindow(m_editText.WindowToken, 0);
115-
}
116-
117-
private void OnClearButtonClicked(object? sender, EventArgs e)
118-
{
119-
m_editText.Text = string.Empty;
78+
var imm = (InputMethodManager?)m_searchView.EditText.Context?.GetSystemService(Context.InputMethodService);
79+
imm?.HideSoftInputFromWindow(m_searchView.EditText.WindowToken, 0);
80+
m_searchView.Hide();
12081
}
12182

12283
// ITextWatcher implementation
12384
public void AfterTextChanged(IEditable? s)
12485
{
12586
var newText = s?.ToString() ?? string.Empty;
126-
m_clearButton.Visibility = string.IsNullOrEmpty(newText) ? ViewStates.Gone : ViewStates.Visible;
12787

12888
if (!m_weakBottomSheet.TryGetTarget(out var bottomSheet))
12989
return;
@@ -140,26 +100,25 @@ public void OnTextChanged(Java.Lang.ICharSequence? s, int start, int before, int
140100
{
141101
}
142102

143-
// IOnFocusChangeListener implementation
144-
public void OnFocusChange(AView? v, bool hasFocus)
103+
// MaterialSearchView.ITransitionListener implementation
104+
public void OnStateChanged(MaterialSearchView searchView, MaterialSearchView.TransitionState previousState, MaterialSearchView.TransitionState newState)
145105
{
146106
if (!m_weakBottomSheet.TryGetTarget(out var bottomSheet))
147107
return;
148108

149-
if (hasFocus)
109+
if (newState == MaterialSearchView.TransitionState.Shown)
150110
{
151111
bottomSheet.OnSearchFieldFocused();
152112
}
153-
else
113+
else if (newState == MaterialSearchView.TransitionState.Hidden)
154114
{
155115
bottomSheet.OnSearchFieldUnfocused();
156116
}
157117
}
158118

159119
public void Cleanup()
160120
{
161-
m_editText.RemoveTextChangedListener(this);
162-
m_editText.OnFocusChangeListener = null;
163-
m_clearButton.Click -= OnClearButtonClicked;
121+
m_searchView.EditText.RemoveTextChangedListener(this);
122+
m_searchView.RemoveTransitionListener(this);
164123
}
165124
}

0 commit comments

Comments
 (0)