11using Android . Content ;
2- using Android . Graphics . Drawables ;
32using Android . Text ;
43using Android . Views ;
54using Android . Views . InputMethods ;
65using Android . Widget ;
7- using Microsoft . Maui . Platform ;
86using 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
1110namespace 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