'SearchView doesn't filter in each child Tab of TabLayout

Here, I have a toolbar in an Activity which contains a SearchView. And that activity has multiple fragments. One main fragment out of them have 10 more fragments inside itself. All 10 fragments are showing data in listviews. Now I'm trying to filter all the lists of fragments by SearchView of MainActivity. But it never filters list of each fragment. Now I show you how I implemented it all.

MainActivity.java

public class MainActivity extends AppCompatActivity {
 @Override
public boolean onCreateOptionsMenu(Menu menu) {
    getMenuInflater().inflate(R.menu.menu_main, menu);
    final SearchView searchView = (SearchView) MenuItemCompat.getActionView(menu.findItem(R.id.action_search));
    SearchManager searchManager = (SearchManager) getSystemService(SEARCH_SERVICE);
    searchView.setSearchableInfo(searchManager.getSearchableInfo(getComponentName()));
    changeSearchViewTextColor(searchView);
    return true;
}
}

Fragment.java

public class CurrencyFragment2 extends android.support.v4.app.Fragment implements SearchView.OnQueryTextListener {

    @Override
public void setMenuVisibility(boolean menuVisible) {
    super.setMenuVisibility(menuVisible);
    if (menuVisible && getActivity() != null) {
        SharedPreferences pref = getActivity().getPreferences(0);
        int id = pref.getInt("viewpager_id", 0);
        if (id == 2)
            setHasOptionsMenu(true);
 }
 }
    @Override
public void onCreateOptionsMenu(Menu menu, MenuInflater inflater) {
    inflater.inflate(R.menu.main, menu); // removed to not double the menu items
    MenuItem item = menu.findItem(R.id.action_search);
    SearchView sv = new SearchView(((MainActivity) getActivity()).getSupportActionBar().getThemedContext());
    changeSearchViewTextColor(sv);
    MenuItemCompat.setShowAsAction(item, MenuItemCompat.SHOW_AS_ACTION_COLLAPSE_ACTION_VIEW | MenuItemCompat.SHOW_AS_ACTION_IF_ROOM);
    MenuItemCompat.setActionView(item, sv);
    sv.setOnQueryTextListener(this);
    sv.setIconifiedByDefault(false);
    super.onCreateOptionsMenu(menu, inflater);
}

private void changeSearchViewTextColor(View view) {
    if (view != null) {
        if (view instanceof TextView) {
            ((TextView) view).setTextColor(Color.WHITE);
            ((TextView) view).setHintTextColor(Color.WHITE);
            ((TextView) view).setCursorVisible(true);
            return;
        } else if (view instanceof ViewGroup) {
            ViewGroup viewGroup = (ViewGroup) view;
            for (int i = 0; i < viewGroup.getChildCount(); i++) {
                changeSearchViewTextColor(viewGroup.getChildAt(i));
            }
        }
    }
}

@Override
public boolean onQueryTextSubmit(String query) {
    return true;
}

@Override
public boolean onQueryTextChange(String newText) {
    if (adapter != null) {
        adapter.filter2(newText);
    }
    return true;
}

Filter method inside Adapter class.

// Filter Class
public void filter2(String charText) {
    charText = charText.toLowerCase(Locale.getDefault());
    items.clear();
    if (charText.length() == 0) {
        items.addAll(arraylist);
    } else {
        for (EquityDetails wp : arraylist) {
            if (wp.getExpert_title().toLowerCase(Locale.getDefault()).contains(charText)) {
                items.add(wp);
            }
        }
    }
    notifyDataSetChanged();
}


Solution 1:[1]

Just to understand the question better. 1. You have a search view in the Action Bar 2. You have multiple Fragments (from your image Advisory/TopAdvisors...) 3. Within each of these there are multiple Fragments for each Tab (example Equity ... etc) 4. You want all list views in the fragments to filter their data based on the search content

Right??

In your current implementation what is the status? Is the list view which is current being displayed getting filtered?

Or even that is not working? Then you need to check notifydatasetChanged is propagating correctly to the adapter. Meaning it should be on the UIThread itself.

For updates to all fragments, meaning once which were not on screen when you were typing the search text, you need to consider the Fragment lifecycle and include code in onResume of the fragment to make sure list is filtered before it is used to initialise the adapter. This is for scenario where you have typed search text already and now are moving between tabs/fragments. So fragments would go on and off screen hence the need for onResume.

Update: Include checking search box for text, and if it has something calling adapter.filter2() in onResume, because that is basically what is filtering your list. .

Solution 2:[2]

  • the problem is - when you launch a fragment, it "takes over" the activity's searchView and whatever you type AFTER this fragment is launched invokes the onQueryTextChange listener of JUST THIS fragment.. Hence, the search doesnt take place in any other fragment..
  • You want your fragments to check for the last search query (posted by any other fragment) when they are launched and perform a search in itself for that query. Also, any change in the search query must also be posted to the activity's search view so other fragments can read it when they are launched.

I'm assuming your viewpager/tabs are implemented in a way that whenever you switch the visible fragments, they get destroyed.

Make the following changes (read the comments)

public class MainActivity extends AppCompatActivity {
final SearchView searchView; //make searchView a class variable/field
 @Override
public boolean onCreateOptionsMenu(Menu menu) {
    getMenuInflater().inflate(R.menu.menu_main, menu);
    searchView = (SearchView) MenuItemCompat.getActionView(menu.findItem(R.id.action_search));
    SearchManager searchManager = (SearchManager) getSystemService(SEARCH_SERVICE);
    searchView.setSearchableInfo(searchManager.getSearchableInfo(getComponentName()));
    changeSearchViewTextColor(searchView);
    return true;
}
}

Now, in all your fragments do this -

        @Override
    public void onCreateOptionsMenu(Menu menu, MenuInflater inflater) {
        inflater.inflate(R.menu.main, menu); // removed to not double the menu items
        MenuItem item = menu.findItem(R.id.action_search);
        SearchView sv = new SearchView(((MainActivity) getActivity()).getSupportActionBar().getThemedContext());
        changeSearchViewTextColor(sv);
        MenuItemCompat.setShowAsAction(item, MenuItemCompat.SHOW_AS_ACTION_COLLAPSE_ACTION_VIEW | MenuItemCompat.SHOW_AS_ACTION_IF_ROOM);
        MenuItemCompat.setActionView(item, sv);
        sv.setOnQueryTextListener(this);
        sv.setIconifiedByDefault(false);
        sv.setQuery(((MainActivity)getActivity()).searchView.getQuery()); // set the main activity's search view query in the fragment's search view
        super.onCreateOptionsMenu(menu, inflater);
    }

Also, in your SearchView.OnQueryTextListener in the fragments, do this

SearchView.OnQueryTextListener() {
            @Override
            public boolean onQueryTextSubmit(String query) {
                search(query); // this is your usual adapter filtering stuff, which you are already doing
                ((MainActivity)getActivity()).searchView.setQuery(query);//sync activity's searchview query with fragment's searchview query, so other fragments can read from the activity's search query when they launch.
                return false;
            }

            @Override
            public boolean onQueryTextChange(String newText) {
                search(newText);// this is your usual adapter filtering stuff, which you are already doing
                ((MainActivity)getActivity()).searchView.setQuery(query);//sync activity's searchview query with fragment's searchview query
                return false;
            }
        });

I hope you get the general idea

  • You have different search view instances in each of your fragments and the activity
  • you need to sync the search queries among each of them, make each of them aware of what the other instances have..

I don't recommend this design though, what i would rather do is

  • just use the activity's search view and in the onQueryTextChange listener of activity's search view.
  • i would notify all the fragments which are alive from onQueryTextChange in the activity (the fragments would register and deregister onQueryTextChange listeners with the activity in their onResume & onPause of the respective fragments). [Side note - This design pattern is called the Observer pattern]

Sources

This article follows the attribution requirements of Stack Overflow and is licensed under CC BY-SA 3.0.

Source: Stack Overflow

Solution Source
Solution 1
Solution 2