Resetting Search Widget (SearchView) value - java

I've got 2 activites : the first, HomepageActiviy, have a search widget that search data using another activity, SearchActivity.
What I want to do is when I go back from SearchActiviy to HomepageActivity, the search widget go collapsed and with a empty text.
I've tried to do this following thing :
public class HomepageActivity extends Activity {
#TargetApi(11)
#Override
public boolean onCreateOptionsMenu(Menu menu) {
MenuInflater inflater = getMenuInflater();
inflater.inflate(R.menu.projectslist, menu);
if(Build.VERSION.SDK_INT >= 11) {
SearchManager searchManager = (SearchManager) getSystemService(Context.SEARCH_SERVICE);
SearchView searchView = (SearchView) menu.findItem(R.id.homepage_search).getActionView();
ComponentName component = new ComponentName(this, SearchActivity.class);
searchView.setSearchableInfo(searchManager.getSearchableInfo(component));
searchView.setIconifiedByDefault(true);
searchView.setQuery("", false);
}
return super.onCreateOptionsMenu(menu);
}
[…]
#TargetApi(11)
#Override
protected void onRestart() {
super.onRestart();
if(Build.VERSION.SDK_INT >= 11)
invalidateOptionsMenu();
launchAsynchronousImageDownload();
}
}
If the widget is well displayed as collapsed, the text in the widget still remember searched text (after I re-open the widget).
How can I reset the text of the widget?
Thanks for any help! ;)

You might also try the following:
searchView.setQuery("", false);
searchView.clearFocus();

this is the magic
searchView.setQuery("", false); // clear the text
searchView.setIconified(true); // close the search editor and make search icon again
in the HomepageActivity insert the onSaveInstanceState function after oncreate function
this function will trigger everytime you open a new activity ,
before opening new activity it will reset the value of Search Widget
protected void onSaveInstanceState(Bundle outState) {
super.onSaveInstanceState(outState);
searchView.setQuery("", false);
searchView.setIconified(true);
}

This worked for me:
First, I declared the menu item variable at the top of the activity:
private MenuItem mSearchMenuItem;
I defined the variable in OnCreateOptionsMenu():
mSearchMenuItem = menu.findItem(R.id.action_search);
I declared invalidateOptionsMenu() in onResume():
#Override
protected void onResume() {
invalidateOptionsMenu();
super.onResume();
}
Lastly, I called collapseActionView() on the menu item in onPrepareOptionsMenu().
#Override
public boolean onPrepareOptionsMenu(Menu menu) {
mSearchMenuItem.collapseActionView();
return super.onPrepareOptionsMenu(menu);
}

searchView.setQuery("", false);
searchView.setIconified(false);

I had this problem too and it worked if I put it in onPrepareOptionsMenu.
#Override
public boolean onPrepareOptionsMenu (Menu menu) {
SearchView searchView = (SearchView) menu.findItem(R.id.action_search).getActionView();
searchView.setQuery("", false);
// rest of code...
}

Kotlin
Here's a cleaner solution, it fixes the following:
Issue of showing filtered list after screen rotation.
Issue of showing filtered list when the user switches to other app and returns back.
Issue of search menu shifting to the left when the user returns back.
No need to iconify if you are invalidating, it's done automatically.
override fun onSaveInstanceState(outState: Bundle) {
super.onSaveInstanceState(outState)
searchView.setQuery("", false)
(activity as YourActivity).invalidateOptionsMenu()
}
Make sure to change YourActivity to the Activity name in which you have your Fragment.

Just found an ugly way to make it work (read comments to see differences):
public class HomepageActivity extends Activity {
// Declaring SearchView as an instance object
private SearchView searchView;
#TargetApi(11)
#Override
public boolean onCreateOptionsMenu(Menu menu) {
MenuInflater inflater = getMenuInflater();
inflater.inflate(R.menu.projectslist, menu);
if(Build.VERSION.SDK_INT >= 11) {
SearchManager searchManager = (SearchManager) getSystemService(Context.SEARCH_SERVICE);
// Using instance var instead of local var
searchView = (SearchView) menu.findItem(R.id.homepage_search).getActionView();
ComponentName component = new ComponentName(this, SearchActivity.class);
searchView.setSearchableInfo(searchManager.getSearchableInfo(component));
searchView.setIconifiedByDefault(true);
// Setting query is not anymore required
//searchView.setQuery("", false);
}
return super.onCreateOptionsMenu(menu);
}
[…]
#TargetApi(11)
#Override
protected void onRestart() {
super.onRestart();
// Do not need to recreate menu
/*if(Build.VERSION.SDK_INT >= 11)
invalidateOptionsMenu();*/
if(Build.VERSION.SDK_INT >= 11) {
// Calling twice: first empty text field, second iconify the view
searchView.setIconified(true);
searchView.setIconified(true);
}
launchAsynchronousImageDownload();
}
}
It's pretty ugly, I think, so if anybody as a better idea, just tell me :)

Related

Android: Tabs and searchbars

I have two tabs and each tabs has its own searchbar.
I bind the searchbar in onCreateOptionsMenu. However, the searchbars only work if I leave the screen once and return to the screen (meaning it needs one more lifecycle for the searchbars to react). I confirmed that onCreateOptionsMenu is indeed called two times at the time of the creation of the ViewPagerFragment.
I bind them like this:
MenuItem searchItem = menu.findItem(R.id.search);
searchItem.setVisible(true);
SearchView searchView = (SearchView) searchItem.getActionView();
searchView.setImeOptions(EditorInfo.IME_ACTION_DONE);
searchView.setOnQueryTextListener(new SearchView.OnQueryTextListener() {
#Override
public boolean onQueryTextSubmit(String query) {
...
return false;
}
#Override
public boolean onQueryTextChange(String newText) {
...
return false;
}
});
I am guessing this bug is related to the tabs. How do implement a working searchbar with tabs (i.e. viewpager2)?
I call this on onCreateOptionsMenu:
public void onCreateOptionsMenu(#NonNull Menu menu, #NonNull MenuInflater inflater) {
super.onCreateOptionsMenu(menu, inflater);
// Call the above...
}
The ViewPager hosting them looks like this:
private void init(View view) {
ViewPager2 viewPager2 = view.findViewById(R.id.view_pager_fragment_view_pager);
TabLayout tabLayout = view.findViewById(R.id.tab_layout_fragment_view_pager);
viewPager2.setUserInputEnabled(true);
viewPager2.setAdapter(new ViewPagerFragmentAdapter(ViewPagerFragment.this));
viewPager2.setOffscreenPageLimit(5);
new TabLayoutMediator
(tabLayout, viewPager2,
(tab, position) -> tab.setText(titles[position])).attach();
}
OP and I were communicating while we found a solution.
First each fragment was changed to
public class MergedItemsFragment extends Fragment implements SearchView.OnQueryTextListener {
/// ------------------------------
/// SearchView.OnQueryTextListener
#Override
public boolean onQueryTextSubmit(String query) {
// ...
return false;
}
#Override
public boolean onQueryTextChange(String newText) {
// ...
return false;
}
/// --------
/// Fragment
#Override
public View onCreateView(#NonNull LayoutInflater inflater, ViewGroup container,
Bundle savedInstanceState) {
return inflater.inflate(R.layout.fragment_merged_items, container, false);
}
#Override
public void onCreateOptionsMenu(#NonNull Menu menu, #NonNull MenuInflater inflater) {
super.onCreateOptionsMenu(menu, inflater);
final MenuItem searchItem = menu.findItem(R.id.search);
final SearchView searchView = (SearchView) searchItem.getActionView();
searchView.setImeOptions(EditorInfo.IME_ACTION_DONE);
searchView.setOnQueryTextListener(this);
}
}
SearchView was inflated by using a menu.xml file within the App's Activity.
SearchView.OnQueryTextListener was implemented by each Fragment that needed access to the SearchView
Since onCreateOptionsMenu() is invoked each time a Fragment is created, or subsequently comes into view (e.g. Swipe); the SearchView's OnQueryTextListener is updated to the corresponding Fragment
Lastly, there was a line in OP's main Fragment containing the ViewPager2, which was removed: viewPager2.setOffscreenPageLimit(5); that caused each Fragment provided by the FragmentStateAdapter to instantiate and mutate the SearchView's OnQueryTextListener each time a Fragment was created.
Removing the line made sure that only the Fragment that was in view was bound to the Toolbar's SearchView.
If any more code is desired, i'd be happy to post what I have, and if I come up with a solution using viewPager2.setOffscreenPageLimit(5); i.e. caching, i'll post that as well

I want to remove the optionMenu when there are no items in ListView (emptyView is shown)?

I have an app that adds items to an sqlite database and returns a cursor, this cursor is then used with a custom CursorAdapter called StoreCursorAdapter to show the items in a ListView.
There is a (delete all) button as an optionsMenuItem.
I want to hide this optionsMenuItem when no items are avaliable in the ListView.
InventoryActivty
EditorActivity
Sorry for the links I am a new user so they don't allow me to embed images yet. :-(
#Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_inventory);
//Declare the views
FloatingActionButton fab = (FloatingActionButton) findViewById(R.id.fab);
ListView list = (ListView) findViewById(R.id.list);
emptyView = findViewById(R.id.empty_view);
//Set the screen to be shown when there are no list items
list.setEmptyView(emptyView);
//StoreCursorAdapter is a custom CursorAdapter
mAdapter = new StoreCursorAdapter(this, null);
list.setAdapter(mAdapter);
}
#Override
protected void onStart() {
super.onStart();
invalidateOptionsMenu();
}
#Override
public boolean onCreateOptionsMenu(Menu menu) {
// Inflate the menu options from the res/menu/menu_inventory.xml file.
// This adds menu items to the app bar.
getMenuInflater().inflate(R.menu.menu_inventory, menu);
return true;
}
#Override
public boolean onOptionsItemSelected(MenuItem item) {
// User clicked on a menu option in the app bar overflow menu
showDeleteConfirmationDialog();
return super.onOptionsItemSelected(item);
}
I tried
1 - emptyView.visibilty() == View.INVISBLE
2 - list.getAdapter == null
but they didn't work
What statement will do the job?!
#Override
public boolean onPrepareOptionsMenu(Menu menu) {
super.onPrepareOptionsMenu(menu);
if (//what statement) {
MenuItem menuItem = menu.findItem(R.id.action_delete_all);
menuItem.setVisible(false);
}
return true;
}
Note:
OnStart() gets called after I get back from the EditorActivity
Note:
In my app I can delete individual items from another activity so adding invalidateOptionsMenu(); in the onOptionsItemSelected won't do the job.
the correct condition to put inside onPrepareOptionsMenu is:
menuItem.setVisible(!mAdapter.isEmpty());
that is the same comparison the ListView uses manages the empty view (minus null check) (https://android.googlesource.com/platform/frameworks/base/+/refs/heads/master/core/java/android/widget/AdapterView.java#747)
but I believe there's another issue here, is that the data is changing after the activity started, during showDeleteConfirmationDialog(). That means you have to call invalidateOptionsMenu() the moment the data is changed. There're two ways of doing it. One more robust and the other is faster to code:
faster to code (but not very good/clean):
add invalidateOptionsMenu() after the code that executes the DB operations.
more robust/clean
you'll use start/stop callbacks to listen to changes in the data. Something like the following:
#Override protected void onStart(){
super.onStart();
invalidateOptionsMenu();
mAdapter.registerDataSetObserver(dataObserver);
}
#Override protected void onStop(){
mAdapter.unregisterDataSetObserver(dataObserver);
super.onStop();
}
private final DataSetObserver dataObserver = new DataSetObserver(){
#Override public void onChanged(){
invalidateOptionsMenu();
}
#Override public void onInvalidated(){
invalidateOptionsMenu();
}
};
all code above was typed by heart, there're likely typos, but that's the idea and the typos you can fix later at your code.
if you dont have any menu item to show, why are u inflating one, if you dont inflate you wont.
#Override
public boolean onCreateOptionsMenu(Menu menu) {
return false;
}
Unless you need to dynamically remove the menu options based on the current state of the activity, this might help How do you remove an inflated menu/items from the new Lollipop Toolbar?

android: How to update badge TextView from a class that extends RecyclerView

I am trying to update my badge based on click events of buttons inside a recycler view. this is my code:
#Override
public void onCreateOptionsMenu(Menu menu, MenuInflater inflater) {
inflater.inflate(R.menu.navigation_drawer, menu);
item = menu.findItem(R.id.add);
badgeLayout = (RelativeLayout) MenuItemCompat.getActionView(item);
mCounter = (TextView) badgeLayout.findViewById(R.id.counter);
cartButton=(Button) badgeLayout.findViewById(R.id.button1);
cartButton.setTypeface(icon);
super.onCreateOptionsMenu(menu, inflater);
}
I want to update the value of mCounter which here:
This is the recyclerview code:
holder.cartBtn.setOnClickListener(new View.OnClickListener() {
#Override
public void onClick(View v) {
mCounter.setText("2");// THIS DOES NOT WORK
}
});
PLEASE HELP ME!
You can perform this task in 4 ways :
Create public static variable and set its value to your badge TextView.
create BroadcastReceiver and call it when value changes.
Use EventBus reference
Create an interface
You should rather pass this event (or just the number You want to set) back to activity, invoke invalidateOptionsMenu(); and in the function onPrepareOptionsMenu(Menu menu) invoke the code You have in onCreateOptionsMenu(Menu menu, MenuInflater inflater)

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();
}
You can manage the filter on nested list by using an Observable/Observer pattern, this will update each nested list from one Observable parent. I fixed all troubles and it works well now to achieve the right behaviour.
Therefore, here's what I did to achieve it:
Using one parent SearchView in Activity
(optional) Create a Filter class (android.widget.Filter) in nested list Adapter
Then, using an Observable/Observer pattern for nested Fragment with Activity
Background: When I tried your code, I had three problems:
I cannot do a search using the ActionBar: onQueryTextChange seems to be never called in Fragments. When I tap on search icon, it seems to me that SearchView (edittext, icon, etc) is not attached with the search widget (but attached to the activity's widget).
I cannot run the custom method filter2: I mean, when I resolved the previous point, this method doesn't work. Indeed, I have to play with custom class extending by Filter and its two methods: performFiltering and publishResults. Without it, I got a blank screen when I tap a word in search bar. However, this could be only my code and maybe filter2() works perfectly for you...
I cannot have a persistent search between fragments: for each child fragment a new SearchView is created. It seems to me that you repeatedly call this line SearchView sv = new SearchView(...); in nested fragment. So each time I switch to the next fragment, the expanded searchview removes its previous text value.
Anyway, after some researches, I found this answer on SO about implementing a Search fragment. Almost the same code as yours, except that you "duplicate" the options menu code in parent activity and in fragments. You shouldn't do it - I think it's the cause of my first problem in previous points.
Besides, the pattern used in the answer's link (one search in one fragment) might not be adapted to yours (one search for multiple fragments). You should call one SearchView in the parent Activity for all nested Fragment.
Solution: This is how I managed it:
#1 Using a parent SearchView:
It will avoid duplicate functions and let the parent activity supervise all its children. Futhermore, this will avoid your duplication icon in the menu.
This is the main parent Activity class:
public class ActivityName extends AppCompatActivity implements SearchView.OnQueryTextListener {
#Override
public boolean onCreateOptionsMenu(Menu menu) {
getMenuInflater().inflate(R.menu.menu_main, menu);
MenuItem item = menu.findItem(R.id.action_search);
SearchView searchview = new SearchView(this);
SearchManager searchManager = (SearchManager) getSystemService(SEARCH_SERVICE);
searchview.setSearchableInfo(searchManager.getSearchableInfo(getComponentName()));
...
MenuItemCompat.setShowAsAction(item,
MenuItemCompat.SHOW_AS_ACTION_COLLAPSE_ACTION_VIEW |
MenuItemCompat.SHOW_AS_ACTION_IF_ROOM);
MenuItemCompat.setActionView(item, searchview);
searchview.setOnQueryTextListener(this);
searchview.setIconifiedByDefault(false);
return super.onCreateOptionsMenu(menu);
}
private void changeSearchViewTextColor(View view) { ... }
#Override
public boolean onQueryTextSubmit(String query) { return false; }
#Override
public boolean onQueryTextChange(String newText) {
// update the observer here (aka nested fragments)
return true;
}
}
#2 (optional) Create a Filter widget:
Like I said previously, I cannot get it work with filter2(), so I create a Filter class as any example on the web.
It quickly looks like, in the adapter of nested fragment, as follows:
private ArrayList<String> originalList; // I used String objects in my tests
private ArrayList<String> filteredList;
private ListFilter filter = new ListFilter();
#Override
public int getCount() {
return filteredList.size();
}
public Filter getFilter() {
return filter;
}
private class ListFilter extends Filter {
#Override
protected FilterResults performFiltering(CharSequence constraint) {
FilterResults results = new FilterResults();
if (constraint != null && constraint.length() > 0) {
constraint = constraint.toString().toLowerCase();
final List<String> list = originalList;
int count = list.size();
final ArrayList<String> nlist = new ArrayList<>(count);
String filterableString;
for (int i = 0; i < count; i++) {
filterableString = list.get(i);
if (filterableString.toLowerCase().contains(constraint)) {
nlist.add(filterableString);
}
}
results.values = nlist;
results.count = nlist.size();
} else {
synchronized(this) {
results.values = originalList;
results.count = originalList.size();
}
}
return results;
}
#SuppressWarnings("unchecked")
#Override
protected void publishResults(CharSequence constraint, FilterResults results) {
if (results.count == 0) {
notifyDataSetInvalidated();
return;
}
filteredList = (ArrayList<String>) results.values;
notifyDataSetChanged();
}
}
#3 Using an Observable/Observer pattern:
The activity - with the searchview - is the Observable object and the nested fragments are the Observers (see Observer pattern). Basically, when the onQueryTextChange will be called, it will trigger the update() method in the existant observers.
Here's the declaration in parent Activity:
private static ActivityName instance;
private FilterManager filterManager;
#Override
protected void onCreate(Bundle savedInstanceState) {
...
instance = this;
filterManager = new FilterManager();
}
public static FilterManager getFilterManager() {
return instance.filterManager; // return the observable class
}
#Override
public boolean onQueryTextChange(String newText) {
filterManager.setQuery(newText); // update the observable value
return true;
}
This is the Observable class which will listen and "pass" the updated data:
public class FilterManager extends Observable {
private String query;
public void setQuery(String query) {
this.query = query;
setChanged();
notifyObservers();
}
public String getQuery() {
return query;
}
}
In order to add the observer fragments to listen the searchview value, I do it when they are initialized in the FragmentStatePagerAdapter.
So in the parent fragment, I create the content tabs by passing the FilterManager:
private ViewPager pager;
private ViewPagerAdapter pagerAdapter;
#Override
public View onCreateView(...) {
...
pagerAdapter = new ViewPagerAdapter(
getActivity(), // pass the context,
getChildFragmentManager(), // the fragment manager
MainActivity.getFilterManager() // and the filter manager
);
}
The adapter will add the observer to the parent observable and remove it when the child fragments are destroyed.
Here's the ViewPagerAdapter of parent fragment:
public class ViewPagerAdapter extends FragmentStatePagerAdapter {
private Context context;
private FilterManager filterManager;
public ViewPagerAdapter(FragmentManager fm) {
super(fm);
}
public ViewPagerAdapter(Context context, FragmentManager fm,
FilterManager filterManager) {
super(fm);
this.context = context;
this.filterManager = filterManager;
}
#Override
public Fragment getItem(int i) {
NestedFragment fragment = new NestedFragment(); // see (*)
filterManager.addObserver(fragment); // add the observer
return fragment;
}
#Override
public int getCount() {
return 10;
}
#Override
public void destroyItem(ViewGroup container, int position, Object object) {
NestedFragment fragment = (NestedFragment) object; // see (*)
filterManager.deleteObserver(fragment); // remove the observer
super.destroyItem(container, position, object);
}
}
Finally, when filterManager.setQuery() in activity is called with onQueryTextChange(), this will be received in nested fragment in update() method which are implementing Observer.
This is the nested fragments with the ListView to filter:
public class NestedFragment extends Fragment implements Observer {
private boolean listUpdated = false; // init the update checking value
...
// setup the listview and the list adapter
...
// use onResume to filter the list if it's not already done
#Override
public void onResume() {
super.onResume();
// get the filter value
final String query = MainActivity.getFilterManager().getQuery();
if (listview != null && adapter != null
&& query != null && !listUpdated) {
// update the list with filter value
listview.post(new Runnable() {
#Override
public void run() {
listUpdated = true; // set the update checking value
adapter.getFilter().filter(query);
}
});
}
}
...
// automatically triggered when setChanged() and notifyObservers() are called
public void update(Observable obs, Object obj) {
if (obs instanceof FilterManager) {
String result = ((FilterManager) obs).getQuery(); // retrieve the search value
if (listAdapter != null) {
listUpdated = true; // set the update checking value
listAdapter.getFilter().filter(result); // filter the list (with #2)
}
}
}
}
#4 Conclusion:
This works well, the lists in all nested fragments are updated as expected by just one searchview. However, there is an incovenient in my above code that you should be aware of:
(see improvements below) I cannot call Fragment general object and add it to being an observer. Indeed, I have to cast and init with the specific fragment class (here NestedFragment); there might be a simple solution, but I didn't find it for now.
Despite this, I get the right behaviour and - I think - it might be a good pattern by keeping one search widget at the top, in activity. So with this solution, you could get a clue, a right direction, to achieve what you want. I hope you'll enjoy.
#5 Improvements (edit):
(see *) You can add the observers by keeping a global Fragment class extension on all nested fragments. This how I instantiate my fragments to the ViewPager:
#Override
public Fragment getItem(int index) {
Fragment frag = null;
switch (index) {
case 0:
frag = new FirstNestedFragment();
break;
case 1:
frag = new SecondFragment();
break;
...
}
return frag;
}
#Override
public Object instantiateItem(ViewGroup container, int position) {
ObserverFragment fragment =
(ObserverFragment) super.instantiateItem(container, position);
filterManager.addObserver(fragment); // add the observer
return fragment;
}
#Override
public void destroyItem(ViewGroup container, int position, Object object) {
filterManager.deleteObserver((ObserverFragment) object); // delete the observer
super.destroyItem(container, position, object);
}
By creating the ObserverFragment class as follows:
public class ObserverFragment extends Fragment implements Observer {
public void update(Observable obs, Object obj) { /* do nothing here */ }
}
And then, by extending and overriding update() in the nested fragments:
public class FirstNestedFragment extends ObserverFragment {
#Override
public void update(Observable obs, Object obj) { }
}
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. .
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]

android - Adding an Action search bar in Fragment

I’m trying to add a SearchView in the ActionBar of a Fragment which is a part of DrawerLayout. I need the SearchView only for one fragment. I do the following in the onCreateOptionsMenu() method of the Fragment.
#Override
public void onCreateOptionsMenu(Menu menu, MenuInflater inflater) {
// TODO Auto-generated method stub
menu.clear();
inflater.inflate(R.menu.alarm_list_menu, menu);
MenuItem searchItem = menu.findItem(R.id.action_search);
mSearchView = (SearchView) MenuItemCompat.getActionView(searchItem);
}
mSearchView is null every time. Have I missed some thing? I ‘m able to see the search icon on the ActionBar, but nothing more can be done.

Categories