Search code examples
androidxamarinxamarin.androidxamarin-studio

Xamarin Android: ActionBar SearchView ActionView returns null


I'm using the ActionBar menu as:

<menu xmlns:android="http://schemas.android.com/apk/res/android">

    <item
        android:id="@+id/menu_search"
        android:actionViewClass="com.abc.AppSearchView"
        android:icon="@drawable/ic_menu_search"
        android:showAsAction="always|collapseActionView" />
</menu>

Then I inflate it and :

public override bool OnCreateOptionsMenu (IMenu menu)
{
    MenuInflater.Inflate (Resource.Menu.actionMenu, menu);
    _actionBarMenu = menu;
    // Get the SearchView and set the searchable configuration
    var searchManager = (SearchManager)GetSystemService (SearchService);
    var searchMenuItem = menu.FindItem (Resource.Id.menu_search);

    var searchView = (AppSearchView)searchMenuItem.ActionView;
    // Assumes current activity is the searchable activity
    searchView.SetSearchableInfo (searchManager.GetSearchableInfo (ComponentName));
    searchView.SetSearchViewListener (this);   
    return base.OnCreateOptionsMenu (menu);
}

I have a custom Search View implementation:

public class AppSearchView : SearchView
{
    private IAppSearchViewListener mListener;

    public AppSearchView (Context ctxt) : base (ctxt)
    {

    }

    public override void OnActionViewCollapsed ()
    {
        if (mListener != null)
            mListener.OnSearchViewCollapsed (this);
        base.OnActionViewCollapsed ();
    }

    public override void OnActionViewExpanded ()
    {
        if (mListener != null)
            mListener.OnSearchViewExpanded (this);
        base.OnActionViewExpanded ();
    }

    public interface IAppSearchViewListener
    {
        void OnSearchViewCollapsed (SearchView sView);

        void OnSearchViewExpanded (SearchView sView);
    }

    public void SetSearchViewListener (IAppSearchViewListener listener)
    {
        mListener = listener;
    }
}

The searchMenuItem.ActionView is null (Consequently, search view is null). But this same code works in another application I have. Both applications are using Xamarin.Android.Support.v13. Also, if I use android.widget.SearchView, it works fine and ActionView is not null. Could someone please shed some light on the same?


Solution

  • Problem is that you are trying to cast a Java type into a .NET type. This only works for some types.

    Instead in your OnCreateOptionsMenu implementation you will need to use .JavaCast<T>() extension method to help you.

    Something like this works for me:

    public override bool OnCreateOptionsMenu(IMenu menu)
    {
        MenuInflater.Inflate(Resource.Menu.main, menu);
    
        var item = menu.FindItem(Resource.Id.action_search);
    
        var searchView = MenuItemCompat.GetActionView(item);
        _searchView = searchView.JavaCast<SearchView>();
    
        _searchView.QueryTextChange += (s, e) => _adapter.Filter.InvokeFilter(e.NewText);
    
        _searchView.QueryTextSubmit += (s, e) =>
        {
            // Handle enter/search button on keyboard here
            Toast.MakeText(this, "Searched for: " + e.Query, ToastLength.Short).Show();
            e.Handled = true;
        };
    
        MenuItemCompat.SetOnActionExpandListener(item, new SearchViewExpandListener(_adapter));
    
        return true;
    }
    

    This is the SearchViewExpandListener:

    private class SearchViewExpandListener 
        : Java.Lang.Object, MenuItemCompat.IOnActionExpandListener
    {
        private readonly IFilterable _adapter;
    
        public SearchViewExpandListener(IFilterable adapter)
        {
            _adapter = adapter;
        }
    
        public bool OnMenuItemActionCollapse(IMenuItem item)
        {
            _adapter.Filter.InvokeFilter("");
            return true;
        }
    
        public bool OnMenuItemActionExpand(IMenuItem item)
        {
            return true;
        }
    }
    

    EDIT:

    Ok so the above answer is still relevant, which shows the usage with Support v7. Either way, the new version of Xamarin.Android explicity specifies that, all views and types that you are going to reference in you AXML/XML files, need to use [Register("my.view.Name")]. If you don't do this, when the app is compiled a checksum is used, instead of your namespace as was used previously, for your package name. This means that you will never be able to find the view.

    So in your custom AppSearchView class you need to add the RegisterAttributeFlags like:

    [Register("com.abc.AppSearchView)]
    public class AppSearchView : SearchView
    {
    }
    

    Now you should be able to find the ActionView.

    More information about the breaking changes of Xamarin.Android 5 can be read on the forums.