I set up a SearchWidget as described in the Android API Guide. It properly displays a magnifying glass icon in the Action Bar and if I click on it, it launches the search widget within the Action Bar (an input field with dark background).
However, if I press the virtual device's search button, then a different search field is launched: It has a white background and does not invoke the callback methods I specified.
So I added this to my MainActivity class:
public boolean onSearchRequested() {
SearchView searchView = (SearchView) menu.findItem(R.id.menu_search).getActionView();
searchView.setIconified(!searchView.isIconified());
return false;
}
The set up of the search widget is exactly the same as it is described in the docs:
SearchManager searchManager = (SearchManager) getSystemService(Context.SEARCH_SERVICE);
SearchView searchView = (SearchView) menu.findItem(R.id.menu_search).getActionView();
searchView.setSearchableInfo(searchManager.getSearchableInfo(getComponentName()));
searchView.setIconifiedByDefault(false);
searchView.setOnQueryTextListener(new CustomSearchQueryTextListener());
My searchable.xml:
<?xml version="1.0" encoding="utf-8"?>
<searchable xmlns:android="http://schemas.android.com/apk/res/android"
android:label="#string/app_name">
</searchable>
And AndroidManifest.xml:
<activity
android:name="MainActivity"
android:label="#string/app_name"
android:launchMode="singleTask"
android:uiOptions="splitActionBarWhenNarrow" >
<intent-filter>
<action android:name="android.intent.action.MAIN" />
<category android:name="android.intent.category.LAUNCHER" />
</intent-filter>
<intent-filter>
<action android:name="android.intent.action.SEARCH" />
</intent-filter>
<meta-data android:name="android.app.searchable" android:resource="#xml/searchable"/>
</activity>
The reason why I tried the setIconfied() method is that in the Android API Guide it says under "The ability to toggle the search box visibility":
"By default, the search widget is "iconified," meaning that it is
represented only by a search icon (a magnifying glass), and expands to
show the search box when the user touches it. As shown above, you can
show the search box by default, by calling
setIconifiedByDefault(false). You can also toggle the search widget
appearance by calling setIconified()."
I also tried to call startSearch("", false, null, false); but this brings up the same search field with the white background.
So my question is: Is it possible to launch the search widget within the action bar if the user presses the device's search button, i.e. peform exactly the same action as if the search menu item is clicked?
While writing this question I came up with the solution by myself. I still decided to post it along with the answer, because it does not seem like this question has been asked on SO before and maybe someone encounters the same problem and/or comes up with a better solution.
All you need to do is call expandActionView() on the search menu item, and everything works like expected:
public boolean onSearchRequested() {
menu.findItem(R.id.menu_search).expandActionView();
return true;
}
Related
I'm trying to implement a search bar on the main Activity of my app. For that, I used the documentation from the android developer website.
I followed the instructions, and I have my search bar where I can put some query. The problem here is that for the few first time when I try to make a search, I have to click 2 times on a search icon.
First, my bar looks like a default one :
Search bar by default
I notice that the icon is not clearly white, it's like there is some transparency in it, but I've selected a full white icon.
Then after a click on the icon, my bar looks like this :
Search bar clicked one time
The icon goes to the left and I have a back button as I was already in the search field.
When I click on the search icon again, I finally have the result that I want :
Search bar in edit mode
When I quit the search bar and do it again, it's the same for 1 or 2 times, and then, I have the normal behavior, I go from the first image to the last just with one click.
Here's my files :
menu_app.xml
<menu xmlns:app="http://schemas.android.com/apk/res-auto"
xmlns:android="http://schemas.android.com/apk/res/android">
<item android:id="#+id/search"
android:title="#string/search"
android:icon="#drawable/ic_search"
app:showAsAction="collapseActionView|ifRoom"
app:actionViewClass="android.widget.SearchView" />
</menu>
AndroidManifest.xml
<activity
android:name=".activities.MainActivity"
android:label="#string/app_name"
android:theme="#style/AppTheme.NoActionBar">
<intent-filter>
<action android:name="android.intent.action.MAIN" />
<action android:name="android.intent.action.SEARCH"/>
<category android:name="android.intent.category.LAUNCHER" />
</intent-filter>
<meta-data android:name="android.app.searchable"
android:resource="#xml/searchable"/>
</activity>
searchable.xml
<searchable xmlns:android="http://schemas.android.com/apk/res/android"
android:label="#string/app_name"
android:hint="#string/search"/>
OnCreateOptionsMenu in MainActivity.java
#Override
public boolean onCreateOptionsMenu(Menu menu) {
MenuInflater inflater = getMenuInflater();
inflater.inflate(R.menu.menu_main, menu);
// Associate searchable configuration with the SearchView
SearchManager searchManager = (SearchManager) getSystemService(Context.SEARCH_SERVICE);
final SearchView searchView = (SearchView) menu.findItem(R.id.search).getActionView();
searchView.setSearchableInfo(searchManager.getSearchableInfo(getComponentName()));
searchView.setOnQueryTextListener(new SearchView.OnQueryTextListener() {
#Override
public boolean onQueryTextSubmit(String s) {
Log.d(TAG, s);
searchView.clearFocus();
return true;
}
#Override
public boolean onQueryTextChange(String s) {
Log.d(TAG, s);
return false;
}
});
return true;
}
Thanks in advance for your help!
I finally solved it, please look at my comment for the solution.
I know this is a common issue.
However, launching another activity whenever user clicks on the searchView (not the icon) is not working unless user submits the query.
Below are functional codes, that result whenever user submits a query, it launches another activity, however, I want to launch another activity as soon as user clicks on the searchView (expanded textfield).
Thanks in advance.
xml
<activity
android:name=".MainPage"
android:label="#string/title_activity_main_page"
android:theme="#style/AppThemeMain">
<intent-filter>
<action android:name="android.intent.action.MAIN"/>
<category android:name="android.intent.category.LAUNCHER"/>
</intent-filter>
<meta-data
android:name="android.app.default_searchable"
android:value=".SearchActivity" >
</meta-data>
</activity>
<provider android:name=".MySuggestionProvider"
android:authorities="ad.aureus.apicius.MySuggestionProvider" />
<activity android:name=".SearchActivity">
<intent-filter>
<action android:name="android.intent.action.SEARCH"/>
</intent-filter>
<meta-data
android:name="android.app.searchable"
android:resource="#xml/searchable"/>
</activity>
MainPage.class
#Override
public boolean onCreateOptionsMenu(Menu menu) {
// Inflate the menu; this adds items to the action bar if it is present.
getMenuInflater().inflate(R.menu.main_page, menu);
SearchManager searchManager = (SearchManager) getSystemService(Context.SEARCH_SERVICE);
SearchView searchView = (SearchView) menu.findItem(R.id.search).getActionView();
searchView.setSearchableInfo(searchManager.getSearchableInfo(new ComponentName(this, SearchActivity.class)));
return true;
}
BTW, I have tried where you can write if/switch statements in onOptionsItemSelected method to identify the searchView id then use Intents. Both did not work.
onCreate method:
Intent intent = getIntent();
if (Intent.ACTION_SEARCH.equals(intent.getAction())) {
String query = intent.getStringExtra(SearchManager.QUERY);
SearchRecentSuggestions suggestions = new SearchRecentSuggestions(this,
MySuggestionProvider.AUTHORITY, MySuggestionProvider.MODE);
suggestions.saveRecentQuery(query, null);
I do have this in onCreate, for the recent query suggestions, and I am not sure whether if this is the reason for my issue. I'm assuming that, because this is called initially, it suppresses the onOptionsItemSelected method's attributes? Not sure..
Here's a xml for menu
<menu xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:app="http://schemas.android.com/apk/res-auto">
<item
android:id="#+id/action_settings"
android:orderInCategory="100"
android:title="#string/action_settings"
app:showAsAction="never" />
<item android:id="#+id/search"
android:title="#string/title_activity_main_page"
app:showAsAction="always"
app:actionViewClass="android.support.v7.widget.SearchView"
/>
Thanks to some of the alternative methods in implementing the if length is 0 start a new activity, and setting setOnClickListener they have worked but only when I clicked on the logo which did not work when I set the icon to false.
In consideration to pskink's comment, I took a different approach that had solved my issue. It was rather a really simple approach where I got rid of the SearchView and changed that to EditText field to allow instant onClick on a single tap to another activity.
Thank you very much for the alternative thoughts and solutions for the issue. They have been a great help as I will reference them later on and that I now know which works and those that don't.
My use case is the following: in activity A, I have an action bar with a collapsible SearchView. When the user gives her query and presses the 'search' button, I would like to show activity B with the results. I'm failing to do so, here is my code:
searchable.xml:
<?xml version="1.0" encoding="utf-8"?>
<searchable xmlns:android="http://schemas.android.com/apk/res/android"
android:hint="#string/action_search_hint"
android:label="#string/app_name"/>
Activity A:
#Override
public boolean onCreateOptionsMenu(Menu menu) {
getMenuInflater().inflate(R.menu.menu, menu);
SearchManager searchManager = (SearchManager) getSystemService(Context.SEARCH_SERVICE);
SearchView searchView = (SearchView) menu.findItem(R.id.action_search).getActionView();
searchView.setSearchableInfo(searchManager.getSearchableInfo(getComponentName()));
return true;
}
AndroidManifest for Activity A and B:
<activity
android:name=".ActivityA">
<intent-filter>
<action android:name="android.intent.action.MAIN"/>
<category android:name="android.intent.category.LAUNCHER"/>
</intent-filter>
</activity>
<activity android:name=".ActivityB">
<intent-filter>
<action android:name="android.intent.action.SEARCH"/>
</intent-filter>
<meta-data
android:name="android.app.searchable"
android:resource="#xml/searchable"/>
</activity>
Howerver, no matter what I try, getSearchableInfo in A always returns null.
I have the feeling I'm not understanding something. The only way I can make getSearchableInfo() return not null is when I put the SEARCH intent filter and the searchable meta-data into activity A in the manifest. But then, when I press the 'search' button, another instance of activity A is started again on top of the current one, which definitely is not what I want. I guess I could add singleTop to A and handle the SEARCH action in onNewIntent and start B, but I generally don't want A to be singleTop, I want the default. Or do I?
I tried adding this to A in the manifest (as suggested in some other threads):
<meta-data
android:name="android.app.default_searchable"
android:value=".ActivityB" >
</meta-data>
What am I doing wrong?
EDIT: I replaced the 'getComponentName()' line in A with the following:
searchView.setSearchableInfo(searchManager.getSearchableInfo(new ComponentName(this, ActivityB.class)));
and it seems to work the way I want! Am I doing something wrong here, or is this the correct way? I'm a bit uncertain because it is not mentioned anywhere!
According to Android documentations, SearchManager.getSearchableInfo(componentName) takes one argument, which is:
componentName: The activity to get searchable information for
In your case you need ActivityB, so constructing a ComponentName that points to ActivityB is the correct way
new ComponentName(this, ActivityB.class)
<intent-filter>
<action android:name="android.intent.action.SEARCH"/>
</intent-filter>
add this intent filter to activity A.
2.SearchView Menu
SearchManager searchManager = (SearchManager) getSystemService(Context.SEARCH_SERVICE);
searchView = (SearchView) menu.findItem(R.id.search).getActionView();
searchView.setSearchableInfo(searchManager
.getSearchableInfo(getComponentName()));
xml:
<?xml version="1.0" encoding="utf-8"?>
<menu xmlns:android="http://schemas.android.com/apk/res/android" >
<item
android:id="#+id/search"
android:actionViewClass="android.widget.SearchView"
android:icon="#drawable/ic_action_search"
android:showAsAction="collapseActionView|always"
android:title="#string/search_hint"/>
</menu>
3.Call this method from oncreate.
private void handleIntent(Intent intent) {
if (Intent.ACTION_SEARCH.equals(intent.getAction())) {
String query = intent.getStringExtra(SearchManager.QUERY);
Debug.e("Suggestion handle click", "call " + query);
startNextActivity(query);
}
}
4. if you are using Autosuggestion Listview implement searchView.setOnQueryTextListener ,setOnSuggestionListener and setSuggestionsAdapter.
I have a search widget within the action bar of my main activity. I would like to display suggestions after the user type at least 2 characters.
I'm aware this can be done by setting android:searchSuggestThreshold="2", but it doesn't seem to work because the SuggestionContentProvider is called when the user types zero or many characters.
Even when the user clicks on the search icon (0 characters) to expand the widget, the ContentProvider is called.
Here is my searchable.xml:
<?xml version="1.0" encoding="utf-8"?>
<searchable xmlns:android="http://schemas.android.com/apk/res/android"
android:label="#string/app_name"
android:hint="#string/search_hint"
android:searchSettingsDescription="#string/search_settings"
android:searchSuggestAuthority="com.studiofrenetic.rmfb.Services.SearchContentProvider"
android:searchSuggestIntentAction="android.intent.action.VIEW"
android:searchSuggestThreshold="2">
</searchable>
Only one searchable activity in my Manifest.xml:
<activity
android:name="com.studiofrenetic.rmfb.MainActivity"
android:screenOrientation="portrait"
android:label="#string/app_name" >
<intent-filter>
<action android:name="android.intent.action.SEARCH" />
</intent-filter>
<meta-data android:name="android.app.searchable"
android:resource="#xml/searchable" />
</activity>
Code to set SearchableInfo in onCreateOptionsMenu:
MenuItem searchItem = menu.findItem(R.id.action_search);
mSearchView = (SearchView) MenuItemCompat.getActionView(searchItem);
mSearchView.setOnQueryTextListener(this);
// Assumes current activity is the searchable activity
SearchManager searchManager = (SearchManager)getSystemService(Context.SEARCH_SERVICE);
mSearchView.setSearchableInfo(searchManager.getSearchableInfo(getComponentName()));
mSearchView.setIconifiedByDefault(true);
I use ActionBarCompat (with minSdkVersion 14 and targetSdkVersion 19).
I search across the web and nobody seems to have this strange behavior... Please help me to understand the problem.
Thanks in advance :)
Finally I had a misunderstanding concerning the behavior related to searchSuggestThreshold:
It's normal that SuggestionContentProvider is called on every typed character, but the uri parameter doesn't contain the query until the threshold is reached.
So, the code related to suggestions generation should be skipped.
I'm developing search widget interface based on official tutorial: http://developer.android.com/guide/topics/search/search-dialog.html
Problem: My SearchableActivity doesn't get triggered when I enter my query and press Ok/enter.
Manifest for SearchableActivity:
<activity android:name="SearchableActivity" android:launchMode="singleTop" >
<intent-filter>
<action android:name="android.intent.action.SEARCH" />
</intent-filter>
<meta-data
android:name="android.app.searchable"
android:resource="#xml/searchable" />
</activity>
xml/searchable.xml
<?xml version="1.0" encoding="utf-8"?>
<searchable xmlns:android="http://schemas.android.com/apk/res/android"
android:hint="Search" android:label="#string/app_name" >
</searchable>
Main activity lifecycle method which adds icons to the action bar (works fine):
#Override
public boolean onCreateOptionsMenu(Menu menu) {
getMenuInflater().inflate(R.menu.main, menu);
// Get the SearchView and set the searchable configuration
SearchManager searchManager = (SearchManager)getSystemService(Context.SEARCH_SERVICE);
SearchView searchView = (SearchView) menu.findItem(R.id.menu_search).getActionView();
searchView.setSearchableInfo(searchManager.getSearchableInfo(getComponentName()));
// Do not iconify the widget;expand it by default
searchView.setIconifiedByDefault(false);
return true;
}
SearchableActivity.java
public class SearchableActivity extends ListActivity {
/** Called when the activity is first created. */
#Override
public void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
Log.d("MY", "search activity triggered");
}
}
Note: The search widget appear on the action bar and I can insert data, but pressing the OK/Enter doesn't take me to the SearchableActivity (doesn't trigger onCreate of the SearchableActivity).
Am I missing something or is the official tutorial flawed?
Problem solved: the tutorial seems to missing one important part: <meta-data android:name="android.app.default_searchable" android:value=".MySearchActivityName" /> has to be added inside <application> tags in manifest to get the search widget working correctly.
EDIT- Also a hint to solving a problem when the actionbar search is not triggered on data posting (no error given whatsoever and documentations hasn't got a word about this limitation): in searchable.xml file android:hint and android:label attributes MUST be references to strings in strings.xml. Source
You should override onOptionsItemSelected and probably onSearchRequested in your activity.
If "xml/searchable.xml" file is not correctly formatted (things such as "searchable" tag not in all lower case), there is no error message returned during execution and the "SearchableActivity" doesn't get invoked.