DanilGholtsman
DanilGholtsman

Reputation: 2374

How to prevent android SearchView Query toggling while text is typing

I got my view with SearchView component. The problem is every new symbol in the input field toggling Query action, so there are some http request are fire etc, so it starts to working slow.

I want it to run only after I click on search button on virtual keyboard (bellow button ).

enter image description here

Is there any properties for that in SearchView?

layout code:

<?xml version="1.0" encoding="utf-8"?>
<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
                xmlns:local="http://schemas.android.com/apk/res-auto"
    android:orientation="vertical"
    android:layout_width="match_parent"
    android:layout_height="match_parent">


    <SearchView
        android:id="@+id/search"
        android:layout_width="match_parent"
        android:layout_height="wrap_content"
        android:clickable="true"
        local:MvxBind="Query SearchString" />

</LinearLayout>

Or I should make something like custom binding which detects keyboard key click?

UPD:

Some code to make clear part of how search works:

View model

public class RemoteMusicSearchViewModel : MvxViewModel
    {
        private IMvxNavigationService _mvxNavigationService;
        private IRemoteMusicDataService _remoteMusicDataService;
        private int _currentPage;

        public RemoteMusicSearchViewModel(IMvxNavigationService mvxNavigationService,
            IRemoteMusicDataService remoteMusicDataService)
        {
            _mvxNavigationService = mvxNavigationService;
            _remoteMusicDataService = remoteMusicDataService;
        }

        public override void Start()
        {
            base.Start();

            _currentPage = 0;
        }

        private string _searchString;

        public string SearchString
        {
            get { return _searchString; }

            set
            {
                _searchString = value;
                RaisePropertyChanged(() => SearchString);
                PerformBasicSearch().ConfigureAwait(false);
            }
        }

        private ObservableCollection<DownloadableEntity> _foundItems;

        public ObservableCollection<DownloadableEntity> FoundItems
        {
            get { return _foundItems; }

            set
            {
                if (_currentPage > 0)
                {
                    _foundItems = new ObservableCollection<DownloadableEntity>(_foundItems.Concat(value));
                }
                else
                {
                    _foundItems = value;
                }

                RaisePropertyChanged(() => FoundItems);
            }
        }

        private async Task PerformBasicSearch(int page = 0)
        {
            string request = SearchString;
            string result = await _remoteMusicDataService.SearchByProperty(request, MusicSearchType.ByTracks, page).ConfigureAwait(false);
            var searchResult = MusicSearchResult.FromJson(result);

            await PrepareDataForOutput(searchResult).ConfigureAwait(false);
        }
    }

full layout code:

<?xml version="1.0" encoding="utf-8"?>
<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
                xmlns:local="http://schemas.android.com/apk/res-auto"
    android:orientation="vertical"
    android:layout_width="match_parent"
    android:layout_height="match_parent">


    <SearchView
        android:id="@+id/search"
        android:layout_width="match_parent"
        android:layout_height="wrap_content"
        android:clickable="true"
        local:MvxBind="Query SearchString"
        />

    <MvvmCross.Binding.Droid.Views.MvxListView
        android:id="@+id/searchlist"
        android:layout_width="fill_parent"
        android:layout_height="fill_parent"
        android:layout_alignParentLeft="true"
        android:layout_alignParentStart="true"
        local:MvxBind="ItemsSource FoundItems"
        local:MvxItemTemplate="@layout/listitem"/>


</LinearLayout>

Upvotes: 0

Views: 585

Answers (2)

DanilGholtsman
DanilGholtsman

Reputation: 2374

I found the solution - I just created new binding for that:

public class SearchViewKeyPressEventsBinding : MvxAndroidTargetBinding
    {
        private readonly SearchView _searchView;
        private IMvxAsyncCommand _command;

        public SearchViewKeyPressEventsBinding(SearchView searchView) : base(searchView)
        {
            _searchView = searchView;
            _searchView.QueryTextSubmit += _searchView_KeyPress;
        }

        private void _searchView_KeyPress(object sender, SearchView.QueryTextSubmitEventArgs e)
        {
            try
            {
                if (_command != null)
                {
                    _command.ExecuteAsync();
                }
            }
            catch (Exception ex)
            {
                throw ex;
            }
        }

        public override Type TargetType
        {
            get { return typeof(IMvxAsyncCommand); }
        }


        protected override void SetValueImpl(object target, object value)
        {
            try
            {
                _command = (IMvxAsyncCommand)value;
            }
            catch (Exception e)
            {
                Log.Error("SOME BINDER FAIL\n\t" + e.Message + "\n", "SOME BINDER FAIL\n\t" + e.Message + "\n");
                throw;
            }
        }

        protected override void Dispose(bool isDisposing)
        {
            if (isDisposing)
            {
                _searchView.QueryTextSubmit -= _searchView_KeyPress;
            }
            base.Dispose(isDisposing);
        }
    }
}

Then registred it and it worked:

   registry.RegisterFactory(new MvxCustomBindingFactory<SearchView>("OnSearchSubmit", (sv) => new SearchViewKeyPressEventsBinding(sv)))

In layout:

<SearchView
        android:id="@+id/search10"
        android:layout_width="match_parent"
        android:layout_height="wrap_content"
        android:clickable="true"
        local:MvxBind="Query SearchString; OnSearchSubmit OnSubmitPressCommand" />

And don't forget to set up viewmodel's property:

 public IMvxAsyncCommand OnSubmitPressCommand
        {
            get
            {
                return new MvxAsyncCommand(OnSearchSubmit);
            }
        }

  private async Task OnSearchSubmit()
        {
            //HideKeyboardOnSearchStart.Invoke(this, null);
            await PerformBasicSearch().ConfigureAwait(false);
        }

Upvotes: 0

elmorabea
elmorabea

Reputation: 3263

I am guessing you are looking for a manual solution for the problem.

With each key event you get you can fire a delayed action after a certain threshold 500 milliseconds for example.

Maybe using Handler.postDelayed()

Then there three scenarios

  • Another event comes before that fires, so you call Handler.removeCallbacks() and fire another delayed action
  • Another event comes while the request is already fired, but no result yet, so you cancel the request and fire your delayed action.
  • An event comes while no delayed action pending or request pending, so you just fire a delayed action.

However, if you are willing to use any Reactive variant, there are more sophisticated solutions for that, by basically debouncing the events of keyboard strokes.

Upvotes: 1

Related Questions