I want to add a searchview to my Android app but I can't understand the documentation. I have added
<searchable xmlns:android="http://schemas.android.com/apk/res/android"
android:includeInGlobalSearch="true"
android:searchSuggestAuthority="dictionary"
android:searchSuggestIntentAction="android.intent.action.VIEW">
</searchable>
to my xml and <intent-filter>
<action android:name="android.intent.action.SEARCH" />
<category android:name="android.intent.category.DEFAULT" /
</intent-filter>
<meta-data android:name="android.app.searchable" android:resource="#xml/searchable" />
to my manifest. But where should the provider-tag go? i get a error inflating class exception when running the app.
Anyone know of a good tutorial? THanks!
This answer is quite later, but as I can see, other answers are only answer-links. Then, I will try to provide some explainations with short example code.
Adding a SearchView is described in the Documentation, and it's quite easy to follow the steps. As we can read on Create a Search Interface topic:
The<intent-filter>does not need a<category>with the DEFAULT value (which you usually see in<activity>elements), because the system delivers the ACTION_SEARCH intent explicitly to your searchable activity, using its component name.
Then, your manifest become:
<intent-filter>
<action android:name="android.intent.action.SEARCH" />
</intent-filter>
<meta-data android:name="android.app.searchable"
android:resource="#xml/my_searchable_layout"/>
"Traditionally, your search results should be presented in a ListView, so you might want your searchable activity to extend ListActivity" - still from Docs. So your SearchActivity might be:
public class SearchActivity extends ListActivity { }
"When the user executes a search from the search dialog or a search widget, the system creates an Intent and stores the user query in it. The system then starts the activity that you've declared to handle searches (the "searchable activity") and delivers it the intent". You need to get the query search from the search dialog or widget by using an Intent in onCreate method:
// Get the intent, verify the action and get the query
Intent intent = getIntent();
if (Intent.ACTION_SEARCH.equals(intent.getAction())) {
// Receive the query
String query = intent.getStringExtra(SearchManager.QUERY);
// Search method..
doMySearch(query);
}
doMySearch() can be an AsyncTask, a new Thread.. connected to a SQLite DataBase, a SharedPreference, whatever.. "The process of storing and searching your data is unique to your application". This being said, you should create an Adapter to provide your results in the list.
Here is a short ListActivity example for SearchActivity using an asynctask to populate a list:
public class SearchActivity extends ListActivity {
// Create an array
String[] values;
#Override
protected void onCreate(Bundle savedInstanceState){
super.onCreate(savedInstanceState);
setContentView(R.layout.list_activity_layout);
Intent intent = getIntent();
if (Intent.ACTION_SEARCH.equals(intent.getAction())) {
String query = intent.getStringExtra(SearchManager.QUERY);
// At the end of doMySearch(), you can populate
// a String Array as resultStringArray[] and set the Adapter
doMySearch(query);
}
}
class doMySearch extends AsyncTask<String,Void,String> {
#Override
protected String doInBackground(String... params) {
// Connect to a SQLite DataBase, do some stuff..
// Populate the Array, and return a succeed message
// As String succeed = "Loaded";
return succeed;
}
#Override
protected void onPostExecute(String result) {
if(result.equals("Loaded") {
// You can create and populate an Adapter
ArrayAdapter<String> adapter = new ArrayAdapter<String>(
SearchActivity.this,
android.R.layout.simple_list_item_1, values);
setListAdapter(adapter);
}
}
}
}
Finally, I prefer to use a SearchView widget with the AppCompat or ActionBarSherlock and it describes at the end of the topic. It's I think more adapted since you asked your question (3 years ago ^^). So, to do:
// Example with AppCompat
#Override
public boolean onCreateOptionsMenu(Menu menu) {
// Inflate the options menu
getMenuInflater().inflate(R.menu.options_menu, menu);
MenuItem searchItem = menu.findItem(R.id.menu_search);
// Get the SearchView and set the searchable configuration
SearchManager searchManager = (SearchManager) getSystemService(Context.SEARCH_SERVICE);
SearchView searchView = (SearchView) MenuItemCompat.getActionView(searchItem);
// Assumes current activity is the searchable activity
searchView.setSearchableInfo(searchManager.getSearchableInfo(getComponentName()));
searchView.setIconifiedByDefault(true); // Iconify the widget
return true;
}
And perform a startActivity() method to pass the query to SearchActivity in onOptionsItemSelected method. Then, you will just to add this search item into your menu as follows (don't forget the custom prefix) like you can see on Adding an Action View:
<item android:id="#+id/action_search"
android:title="#string/action_search"
android:icon="#drawable/ic_action_search"
yourapp:showAsAction="ifRoom|collapseActionView"
yourapp:actionViewClass="android.support.v7.widget.SearchView" />
In sample Docs, you have the Searchable Dictionary demo which contains a ListView and provide a full demo of connecting SQLite DataBase via an Adapter.
Voila! I hope you found the solution yet and this post will help someone which want the same.
Related
According to the official documentation, there are two ways of providing a search interface: using either the search dialog or a SearchView widget. I'd like to pay attention to passing search context data using these two ways.
So, the documentation says:
..you can provide additional data in the intent that the system sends to
your searchable activity. You can pass the additional data in the
APP_DATA Bundle, which is included in the ACTION_SEARCH intent.
To pass this kind of data to your searchable activity, override the
onSearchRequested() method for the activity from which the user can
perform a search, create a Bundle with the additional data, and call
startSearch() to activate the search dialog. For example:
#Override
public boolean onSearchRequested() {
Bundle appData = new Bundle();
appData.putBoolean(SearchableActivity.JARGON, true);
startSearch(null, false, appData, false);
return true;
}
..Once the user submits a query, it's delivered to your searchable
activity along with the data you've added. You can extract the extra
data from the APP_DATA Bundle to refine the search. For example:
Bundle appData = getIntent().getBundleExtra(SearchManager.APP_DATA);
if (appData != null) {
boolean jargon = appData.getBoolean(SearchableActivity.JARGON);
}
This refers to the search dialog. And what about the search widget?
Is it possible to pass search context data using the SearchView widget only?
Hope, someone could give clear explanation and/or suggest another or similar way to accomplish the goal.
Thanks!
I've discovered the solution. Even two solutions!
They don't need to invoke onSearchRequested() thus there is no search dialog at all :)
First, I provide some common steps to create the search interface and then give the solutions of the source problem.
We add the Search View to the App Bar by creating res/menu/options_menu.xml file with the following code:
<?xml version="1.0" encoding="utf-8"?>
<menu xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:app="http://schemas.android.com/apk/res-auto"
xmlns:tools="http://schemas.android.com/tools"
tools:context=".MainActivity" >
<item
android:id="#+id/action_search"
android:icon="#drawable/ic_action_search"
android:title="#string/search_string"
app:showAsAction="collapseActionView|ifRoom"
app:actionViewClass="android.support.v7.widget.SearchView" />
</menu>
Create a Searchable Configuration in res/xml/searchable.xml file:
<?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" />
Declare two activities in AndroidManifest.xml:
<?xml version="1.0" encoding="utf-8"?>
<manifest xmlns:android="http://schemas.android.com/apk/res/android"
package="com.example.searchinterface">
<application
android:icon="#mipmap/ic_launcher"
android:label="#string/app_name"
android:theme="#style/AppTheme">
<activity
android:name=".MainActivity"
android:label="#string/app_name">
<intent-filter>
<action android:name="android.intent.action.MAIN" />
<category android:name="android.intent.category.LAUNCHER" />
</intent-filter>
</activity>
<activity android:name=".SearchableActivity"
android:label="#string/app_name"
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>
</application>
</manifest>
Create a Searchable Activity where we handle the ACTION_SEARCH intent and the search context data, passed from the MainActivity. We extract the extra data from the APP_DATA Bundle to refine the search:
public class SearchableActivity extends AppCompatActivity {
public static final String JARGON = "com.example.searchinterface.jargon";
#Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_searchable);
handleIntent(getIntent());
}
#Override
protected void onNewIntent(Intent intent) {
super.onNewIntent(intent);
handleIntent(intent);
}
private void handleIntent(Intent intent) {
if (Intent.ACTION_SEARCH.equals(intent.getAction())) {
String query = intent.getStringExtra(SearchManager.QUERY);
// use the query to search the data somehow
Bundle appData = intent.getBundleExtra(SearchManager.APP_DATA);
if (appData != null) {
boolean jargon = appData.getBoolean(SearchableActivity.JARGON);
// use the context data to refine our search
}
}
}
}
Now, we need to implement our MainActivity class. So, we inflate our menu and configure the SearchView element as well. We also need to set the SearchView.OnQueryTextListener and implement its methods, especially onQueryTextSubmit(). It is invoked when the user presses the submit button and it contains the main logic of passing the search context data to the SearchableActivity.
Finally, we reached the main answer section. As I said, there are two solutions:
1. Create an intent with Bundle extra and send it to the SearchableActivity manually;
Here is the MainActivity with all necessary contents:
public class MainActivity extends AppCompatActivity implements SearchView.OnQueryTextListener {
private SearchView mSearchView;
#Override
public boolean onCreateOptionsMenu(Menu menu) {
getMenuInflater().inflate(R.menu.options_menu, menu);
MenuItem searchItem = menu.findItem(R.id.action_search);
mSearchView = (SearchView) MenuItemCompat.getActionView(searchItem);
// associate searchable configuration with the SearchView
SearchManager searchManager = (SearchManager) getSystemService(Context.SEARCH_SERVICE);
mSearchView.setSearchableInfo(searchManager.getSearchableInfo(
new ComponentName(this, SearchableActivity.class)));
mSearchView.setOnQueryTextListener(this);
return true;
}
#Override
public boolean onQueryTextSubmit(String query) {
Intent searchIntent = new Intent(this, SearchableActivity.class);
searchIntent.putExtra(SearchManager.QUERY, query);
Bundle appData = new Bundle();
appData.putBoolean(SearchableActivity.JARGON, true); // put extra data to Bundle
searchIntent.putExtra(SearchManager.APP_DATA, appData); // pass the search context data
searchIntent.setAction(Intent.ACTION_SEARCH);
startActivity(searchIntent);
return true; // we start the search activity manually
}
#Override
public boolean onQueryTextChange(String newText) {
return false;
}
}
Thanks to https://stackoverflow.com/a/22184137/6411150.
The second solution is also put in onQueryTextSubmit() (but it's not necessary):
2. Create search context data Bundle and pass it to the setAppSearchData() method of the SearchView.
So, we don't need to create and pass the whole search intent and launch respective searchable activity, the system will take care of it.
Here is another code snippet:
/*
You may need to suppress the “restrictedApi” error that you could possibly
receive from this method "setAppSearchData(appData)”.I had to
I’m targetSdkVersion 26. I’m also using Android Studio 3
with the new gradle plugin, which might be causing this.
If you’re not running Android Studio 3 you can simply put
“//noinspection RestrictedApi"
right above the line: mSearchView.setAppSearchData(appData);
*/
#SuppressWarnings("RestrictedApi")
#Override
public boolean onQueryTextSubmit(String query) {
Bundle appData = new Bundle();
appData.putBoolean(SearchableActivity.JARGON, true); // put extra data to Bundle
mSearchView.setAppSearchData(appData); // pass the search context data
return false; // we do not need to start the search activity manually, the system does it for us
}
Thanks to https://stackoverflow.com/a/38295904/6411150.
Note: Only support library's version of SearchView (android.support.v7.widget.SearchView) contains the setAppSearchData() method, so be attentive.
If the design works for your needs, you can use the SearchView by itself, and add an OnQueryTextListener to it, and deal with it there. There is no need for anything else, no Intents, Meta-tags, nor XML files. I have done this a few times, and the docs are a bit not clear on this.
I am using the Search interface in andriod, and folowing the android guide I have set up the the interface.
I create a SearchableActivity which will handle both the user input the search result.
Now I meet a problem in that when user input something in the SearchView and hit the search key, the Search requset will be submited, then I will receive the query string and do the search, but at this moment, the text in the SearchView was changed to the default SearchHit value while I want it to be the query string.
So I tried to use the searchView.setQuery(query, false);, but it does not work, what's the problem?
The core Activity codes:
public void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.map_activity_layout);
Log.d("map", "create");
handleIntent(getIntent());
}
public void onNewIntent(Intent intent) {
setIntent(intent);
handleIntent(intent);
}
private void handleIntent(Intent intent) {
Log.d("map", "handleIntent");
if (Intent.ACTION_SEARCH.equals(intent.getAction())) {
String query = intent.getStringExtra(SearchManager.QUERY);
//try search
Log.d("map", "search and set query:" + query);
searchView.setQuery(query, false); // reset the value in of the SearchView
}
}
The AndroidManifest.xml:
<activity
android:name=".ui.MapActivity"
android:launchMode="singleTop">
<intent-filter>
<action android:name="android.intent.action.MAIN" />
<category android:name="android.intent.category.LAUNCHER" />
<action android:name="android.intent.action.SEARCH" />
</intent-filter>
<meta-data
android:name="android.app.searchable"
android:resource="#xml/searchable" />
</activity>
I suggest you to delay the setting until the next event loop (add it to the message queue), so the code may looks like this:
private void handleIntent(Intent intent) {
if (Intent.ACTION_SEARCH.equals(intent.getAction())) {
final String query = intent.getStringExtra(SearchManager.QUERY);
searchView.post(new Runnable() {
#Override
public void run() {
// Important! Make sure searchView has been initialized
// and referenced to the correct (current) SearchView.
// Config changes (e.g. screen rotation) may make the
// variable value null.
searchView.setQuery(query, false);
}
};
}
}
This doesn't work
mSearchView.setQuery(mKeyword, true);
MenuItemCompat.expandActionView(mMenuSearch);
This does
MenuItemCompat.expandActionView(mMenuSearch);
mSearchView.setQuery(mKeyword, true);
It seems it only remembers it after you expanded the action view.
I'm using MenuItemCompat just for backwards compatibility
need to put code in post handler with MenuItemCompat
inflater.inflate(R.menu.menu_my_team, menu);
// search manager integration for search widget
SearchManager searchManager = (SearchManager) getActivity()
.getSystemService(Context.SEARCH_SERVICE);
final MenuItem searchMenuItem = menu.findItem(R.id.action_search);
// MenuItem helpTutorialMenuItem = menu.findItem(R.id.action_help);
mSearchView = (SearchView) MenuItemCompat.getActionView(searchMenuItem);
mSearchView.setSearchableInfo(searchManager
.getSearchableInfo(getActivity().getComponentName()));
mSearchView.setQueryHint("Search by");
if (!TextUtils.isEmpty(queryText)) {
mSearchView.post(new Runnable() {
#Override
public void run() {
MenuItemCompat.expandActionView(searchMenuItem);
mSearchView.setQuery(queryText, false);
}
});
}
mSearchView.clearFocus();
It seems that when the intent with Action Intent.ACTION_SEARCH is received, the following method will be called:
handleIntent();
onCreateOptionsMenu();
And the handleIntent will run before the onCreateOptionsMenu.
At the same time, people used to setup the searchView at the onCreateOptionsMenu.
Which means that the option menu will be re-inflated, and the searchView will be re-build, then all the settings in the handleIntent will not work.
I think this is the reason.
Please feel free to fix me if I am wrong.
In my case, it wasn't working because the fragment I was in was no longer attached to the window. Moving it to above where I swapped in the new search results fragment fixed it.
I'm actually trying to use the built-in search interface of Android, but I have some issues when I try to pass data with the search query.
Here is a brief explanation : I have an object in a first Activity (FirstActivity) called "Category" which implements Serializable (I already pass it successfuly between Activities) and I want to perform a search related to that category, and display the results in a second Activity (SecondActivity).
So, in FirstActivity I override the onSearchRequest method :
#Override
public boolean onSearchRequested() {
Bundle appData = new Bundle();
appData.putSerializable("category", _currentCategory);
Log.d(Utils.LOG_TAG, "Bundle : "+appData.keySet());
startSearch(null, false, appData, false);
return true;
}
And in SecondActivity, I try to get this Bundle :
#Override
public void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
...
handleIntent(getIntent());
}
private void handleIntent(Intent intent){
Bundle appData = intent.getBundleExtra(SearchManager.APP_DATA);
if(appData == null) Log.d(Utils.LOG_TAG, "appData == null");
Log.d(Utils.LOG_TAG, "Extras : "+intent.getExtras().keySet());
}
Problem is that appData seems to be equals to null everytime. Here is the logcat output :
Bundle : [category]
appData == null
Extras : [query, user_query]
I tried to add some other objects into the Bundle (Booleans, etc...) but it doesn't change anything at all and I keep having a null appData.
I had problems figuring this out as well, and the examples I found didn't really help. A lot of them suggested overriding onSearchRequested(), but that actually doesn't work for SearchWidget. I ended up using the following (from danada) as a solution, since it seemed much simpler for me than setting up the OnQueryTextListener. I just overrode startActivity (in the first, search Activity) like so:
#Override
public void startActivity(Intent intent) {
//check if search intent
if(Intent.ACTION_SEARCH.equals(intent.getAction())) {
intent.putExtra("KEY", "VALUE");
}
super.startActivity(intent);
}
Then in the second, searchable Activity, I pulled out the info like so (called from onCreate() or from overriding onNewIntent() (if using singleTop)):
private void handleIntent(Intent intent){
if(Intent.ACTION_SEARCH.equals(intent.getAction())){
mSearchedQuery = intent.getStringExtra(SearchManager.QUERY);
mExtraData = intent.getStringExtra("KEY");
}
Simple, and worked like a charm! Check the link to the article above if you would like a little more explanation about it.
If you're using SearchView, it will not send your appData. Instead, consider using OnQueryTextListener. For example:
...
#Override
public boolean onCreateOptionsMenu(Menu menu) {
MenuInflater inflater = getMenuInflater();
inflater.inflate(R.menu.your-menu-id, menu);
/*
* Get the SearchView and set the searchable configuration.
*/
SearchManager searchManager = (SearchManager) getSystemService(Context.SEARCH_SERVICE);
SearchView searchView = (SearchView) menu.findItem(R.id.your-search-menuitem-id)
.getActionView();
searchView.setSearchableInfo(searchManager.getSearchableInfo(getComponentName()));
/*
* Set query text listener here.
*/
searchView.setOnQueryTextListener(mSearchViewOnQueryTextListener);
return true;
}// onCreateOptionsMenu()
...
private final SearchView.OnQueryTextListener mSearchViewOnQueryTextListener = new SearchView.OnQueryTextListener() {
#Override
public boolean onQueryTextSubmit(String query) {
/*
* You don't need to deal with "appData", because you already
* have the search query here.
*/
// Tell the SearchView that we handled the query.
return true;
}// onQueryTextSubmit()
#Override
public boolean onQueryTextChange(String newText) {
// TODO Auto-generated method stub
return false;
}// onQueryTextChange()
};// mSearchViewOnQueryTextListener
Note: You still need to keep the old way (using appData inside onSearchRequested()). In your onCreate(), if the extra for SearchManager.APP_DATA is null, that means you already handled the search query in the listener.
Conclusion:
If the SearchView is inactive, and you invoke it via onSearchRequested(), this will happen: onSearchRequested() >> onCreate() (ACTION_SEARCH contains SearchManager.APP_DATA).
If the SearchView is active, the user types and submits search, this will happen: SearchView.OnQueryTextListener.onQueryTextSubmit() >> onCreate() (ACTION_SEARCH without SearchManager.APP_DATA).
While putting data and retrieving it you are using two different keys. while putting you are using "category" and while retrieving you are using SearchManager.APP_DATA instead of using "category"
Try with
Bundle appData = intent.getBundleExtra("category");
Thanks
Deepak
In your example, you are asking for the keyset on the original intent object, and not the Bundle containing your appData. Here is an example that should work:
private void handleIntent(Intent intent){
final Bundle appData = intent.getBundleExtra(SearchManager.APP_DATA);
for (String key : appData.keySet()) {
Log.d(TAG, "key="+appData.getString(key));
}
}
I have an Activity that holds a list. Via the Android onSearchRequested() I implemented a search.
The results are shown as a list with the same adapter in another Activity. Working fine so far.
Also, I want to be able to search from that second Activity showing the new results in the same list.
My AndroidManifest.xml for the two activities:
<activity android:name=".ListActivity" android:label="List">
<meta-data android:name="android.app.default_searchable" android:value=".SearchActivity" />
</activity>
<activity android:name=".SearchActivity" android:label="Results">
<intent-filter>
<action android:name="android.intent.action.SEARCH" />
</intent-filter>
<meta-data android:name="android.app.searchable" android:resource="#xml/search" android:value=".SearchActivity" />
</activity>
The SearchActivity's onResume():
#Override
protected void onResume() {
super.onResume();
Intent queryIntent = getIntent();
String value = queryIntent.getStringExtra(SearchManager.QUERY);
setView(value);
}
The setView() method does a foreach loop through all objects adding them to a result-array which is used for a new Adapter that the list shows.
ca = new CustomAdapter(this, R.layout.customadapter, resultArray);
list.setAdapter(pa);
list.invalidate();
When trying to search from the second Activity the search bar appears, I can enter my search value, send it - but the list doesn't change (and even the keyboard stays).
What's missing?
Edit: Tried to make it easier to understand.
Found that question which describes kind of the same problem.
Instead of overriding onResume() I have to override onNewIntent()
#Override
protected void onNewIntent(Intent intent) {
super.onNewIntent(intent);
String value = intent.getStringExtra(SearchManager.QUERY);
setView(value);
}
I followed the steps described on http://developer.android.com/guide/topics/search/search-dialog.html to implement a search feature in my notepad application.
My problem is, that when I finish the search a new activity opens capturing my search query. But what I really want, is the query returned to the current activity instead of starting a new one.
Is this possible?
UPDATE:
AndroidManifest.xml
<activity android:name="MyNotepad"
android:label="#string/app_name">
<intent-filter>
<action android:name="android.intent.action.MAIN" />
<category android:name="android.intent.category.LAUNCHER" />
<action android:name="android.intent.action.SEARCH"></action>
</intent-filter>
<meta-data android:resource="#xml/searchable" android:name="android.app.searchable"></meta-data>
</activity><activity android:name="Preferences" android:label="Preferences" >
</activity>
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">
</searchable>
JAVA-code
#Override
public void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.main_pad);
Intent intent = getIntent();
if (Intent.ACTION_SEARCH.equals(intent.getAction())) {
String query = intent.getStringExtra(SearchManager.QUERY);
Toast.makeText(getApplicationContext(), query, Toast.LENGTH_LONG).show();
}
}
#Override
public boolean onOptionsItemSelected(MenuItem item)
{
switch(item.getItemId())
{
case R.id.menuItemSearch:
startSearch("", false, null, false);
break;
}
return true;
}
Even if I use the search-button on the phone it doesn't work. I therefor believe that the problem is in the AndroidManifest.xml
In your Application Manifest you need to define the current activity as a searchable activity.
<activity android:name="BrowseItems" android:label="#string/browseitems"
android:launchMode="singleTop">
<intent-filter>
<action android:name="android.intent.action.SEARCH" />
</intent-filter>
<meta-data android:name="android.app.searchable"
android:resource="#xml/itemsearchable" />
</activity>
You then use the following code, which is from http://developer.android.com/guide/topics/search/search-dialog.html#LifeCycle
#Override
public void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.search);
handleIntent(getIntent());
}
#Override
protected void onNewIntent(Intent intent) {
setIntent(intent);
handleIntent(intent);
}
private void handleIntent(Intent intent) {
if (Intent.ACTION_SEARCH.equals(intent.getAction())) {
String query = intent.getStringExtra(SearchManager.QUERY);
// Do work using string
}
}
You can then use the string to reload your activity, if its a list activity you can call your code that you use to load data and use the string in that.
Add to AndroidManifest.xml in your Searchable Activity:
android:launchMode="singleTop"
so, your AndroidManifest.xml looks like:
<activity android:name="MyNotepad"
android:label="#string/app_name"
android:launchMode="singleTop">
<intent-filter>
<action android:name="android.intent.action.MAIN" />
<category android:name="android.intent.category.LAUNCHER" />
<action android:name="android.intent.action.SEARCH"></action>
</intent-filter>
<meta-data android:resource="#xml/searchable" android:name="android.app.searchable"></meta-data>
</activity><activity android:name="Preferences" android:label="Preferences" >
The reason:
From this post:
The activity launch mode has four valid values:
"standard"
"singleTop"
"singleTask"
"singleInstance"
The 'standard' is the default value. The four values fall into two groups:
'standard' and 'singleTop' can instantiate multiple activity instances and the instance will stay in the same task.
For 'singleTask' or 'singleInstance', the activity class uses the singleton pattern, and that instance will be the root activity of a new task. Let's examine each value:
"standard":
Multiple instances of the activity class can be instantiated and multiple instances can be added to the same task or different tasks. This is the common mode for most of the activities.
"singleTop":
The difference from 'standard' is, if an instance of activity already exists at the top of the current task and system routes intent to this activity, no new instance will be created because it will fire off an onNewIntent() method instead of creating a new object.
I simply use this:-
searchView.setOnQueryTextListener(new SearchView.OnQueryTextListener() {
#Override
public boolean onQueryTextSubmit(String s) {
//on submit
return false;
}
#Override
public boolean onQueryTextChange(String s) {
//get all text changes
return false;
}
});
This is best used when you have to search across a listview and have to filter out items. I never go by implementing the search function using the manifest file. The 2 methods do all the job.
I was also facing the same problem, then I wrote this code and it solved my problem.
Implement this in your Activity
**implements SearchView.OnQueryTextListener, SearchView.OnCloseListener**
Add this function in the class:
private void setupSearchView()
{
searchView.setIconifiedByDefault(true);
SearchManager searchManager = (SearchManager) getSystemService(Context.SEARCH_SERVICE);
if (searchManager != null)
{
List<SearchableInfo> searchables = searchManager.getSearchablesInGlobalSearch();
// Try to use the "applications" global search provider
SearchableInfo info = searchManager.getSearchableInfo(getComponentName());
for (SearchableInfo inf : searchables)
{
if (inf.getSuggestAuthority() != null && inf.getSuggestAuthority().startsWith("applications"))
{
info = inf;
}
}
searchView.setSearchableInfo(info);
}
searchView.setOnQueryTextListener(this);
searchView.setOnCloseListener(this);
}
Call the function in the onCreateOptionsMenu(Menu menu)
#Override
public boolean onCreateOptionsMenu(Menu menu)
{
MenuInflater inflater = getMenuInflater();
inflater.inflate(R.menu.menu, menu);
//restoreActionBar();
// Associate searchable configuration with the SearchView
//SearchManager searchManager = (SearchManager) getSystemService(Context.SEARCH_SERVICE);
searchView = (SearchView) menu.findItem(R.id.action_search).getActionView();
//searchView.setSearchableInfo(searchManager.getSearchableInfo(getComponentName()));
setupSearchView();
return super.onCreateOptionsMenu(menu);
}
This will totally solve your problem..
Happy Coding!!
Briefly:
Add android:launchMode="singleTop" to the searchable activity definition in the AndroidManifest.xml
Implement onNewIntent in the searchable activity and handle the search there.
I followed Lachlan's clue of "You can then use the string to reload your activity", and finally worked this out. So please read Lachlan's post first, and then take the following 3 steps "to reload your activity":
Save query string in ApplicationContext.
Finish the new opened search Activity.
Override OnResume, get query string you just saved, and then refresh the list.
Codes in List Activity:
private void handleIntent(Intent intent) {
if (Intent.ACTION_SEARCH.equals(intent.getAction())) {
String searchText = intent.getStringExtra(SearchManager.QUERY);
((MyApp)this.getApplicationContext()).setSearchText(searchText);
this.finish();
}
}
protected void onResume() {
super.onResume();
String searchText = ((MyApp)this.getApplicationContext()).getSearchText();
//refresh your list here...
}
MyApp class: (This original idea is from here: Android: How to declare global variables?)
public class MyApp extends Application {
private String mSearchText;
public String getSearchText() {
return mSearchText;
}
public void setSearchText(String searchText) {
this.mSearchText = searchText;
}
}
Don't forget to add property android:name=".MyApp" to your application in the Manifest file. Good luck!
in the manifest file, add:
android:launchMode="singleTop"
To your search activity. Then, let your search activity implement SearchView.OnSuggestionListener
finally:
mSearchView.setOnSuggestListener(this)
this way you can handle the suggestion click event with out creating a new instance of your search activity.
It doesn't bring up a new activity if the SearchView.OnSuggestionListener on the searchView is set, and the overridden methods return true (overriding the default behavior). You can do that in the onCreateOptionsMenu() implementation, like this:
searchView.setOnSuggestionListener(new SearchView.OnSuggestionListener() {
#Override
public boolean onSuggestionSelect(int position) {
return true;
}
#Override
public boolean onSuggestionClick(int position) {
CursorAdapter selectedView = searchView.getSuggestionsAdapter();
Cursor cursor = (Cursor) selectedView.getItem(position);
int index = cursor.getColumnIndexOrThrow(SearchManager.SUGGEST_COLUMN_TEXT_1);
searchView.setQuery(cursor.getString(index), true);
return true;
}
});
No need to force the activity to singleTop or other hacks.
Just add
<application>
<meta-data
android:name="android.app.default_searchable"
android:value="#Activity_Name" />
<!-- All your activities, service, etc. -->
</application>
in your android_manifest.xml file where #Activity_Name is the name of the activity that handles the search.