I have one Activity with a fragment to list some data. I've already implemented the search using the SearchView (the nice looking search field on action bar).
The search is working fine, user input the data, press the search button and the results are filtered as expected.
My problem is that I can't "reset" the search back to all results. I already tried setting the Dismiss and Cancel listenners on the searchManager object but the onDismiss and oncancel methods are never being called.
Here is my action on the manifest:
<activity
android:name=".ClientesActivity"
android:label="#string/title_activity_clientes"
android:parentActivityName=".MenuPrincipalActivity"
android:launchMode="singleTop" >
<meta-data
android:name="android.support.PARENT_ACTIVITY"
android:value="MenuPrincipalActivity" />
<meta-data android:name="android.app.searchable"
android:resource="#xml/cliente_searchable" />
<intent-filter>
<action android:name="android.intent.action.SEARCH" />
</intent-filter>
</activity>
The methods on the ClientesActivity:
#Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
clientesService = new ClientesService(getBaseContext());
setContentView(R.layout.activity_clientes);
if (savedInstanceState == null) {
getFragmentManager().beginTransaction()
.add(R.id.container, ClienteFragment.newInstance(), ClienteFragment.class.getSimpleName())
.commit();
}
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);
clientesService.findByNameLike(query);
}
}
#Override
public boolean onCreateOptionsMenu(Menu menu) {
getMenuInflater().inflate(R.menu.clientes, menu);
SearchManager searchManager =
(SearchManager) getSystemService(Context.SEARCH_SERVICE);
SearchView searchView =
(SearchView) menu.findItem(R.id.action_search).getActionView();
searchView.setSearchableInfo(
searchManager.getSearchableInfo(getComponentName()));
searchView.setSubmitButtonEnabled(true);
return true;
}
It's not showing the Listenners because I rolled back the changes after nothing worked.
But basically o added the listenners before the 'return true' on the onCreateOptionsMenu method.
It was nothing fancy at first, just annonymous class overriding the interface with onDismiss method implemented, tried to put some logs there and the method was never called.
Take a look at android.widget.SearchView#setOnQueryTextFocusChangeListener. As far as I can tell the hasFocus will be true when the searchview is displayed and will not be called again with false until the user hits the upstack/back arrow to close the search. Oddly, I never see android.widget.SearchView#setOnCloseListener called
Alternately, implement an onOptionsItemSelected and call android.app.Activity#onSearchRequested() to trigger the search dialog in order to have those registered callback methods utilized.
#Override
public boolean onOptionsItemSelected(final MenuItem item) {
switch (item.getItemId()) {
case R.id.search:
onSearchRequested();//delegate upstream
break;
default:
return super.onOptionsItemSelected(item);
}
}
It's not very clear in the Invoking the search dialog documentation. I don't see it declared in the documentation. But it seems if you use just the search widget, the SearchManager callbacks are not triggered.
Related
I've created a search view using search widget, and when I open and search it works normally, the problem is when I go to any activity, including the searchview activity, and back using backbutton or finish() the activity, the search widget stops working. I mean, I still can type on widget, but the button to search(return) stops working, the searchable activity doesn't open anymore.
In the HomeActivity.java(main) I set up the search widget:
#Override
public boolean onCreateOptionsMenu(Menu menu) {
// Inflate the menu; this adds items to the action bar if it is present.
getMenuInflater().inflate(R.menu.menu_home_settings, menu);
// Get the SearchView and set the searchable configuration
SearchManager searchManager = (SearchManager) getSystemService(Context.SEARCH_SERVICE);
SearchView searchView = (SearchView) menu.findItem(R.id.action_search).getActionView();
// Assumes current activity is the searchable activity
searchView.setSearchableInfo(searchManager.getSearchableInfo(getComponentName()));
searchView.setIconifiedByDefault(false); // Do not iconify the widget; expand it by default
// Change Text color from search bar
final EditText searchEditText = (EditText) searchView.findViewById(android.support.v7.appcompat.R.id.search_src_text);
searchEditText.setImeOptions(DEFAULT_KEYS_SEARCH_LOCAL);
return true;
}
in Searchable.java
#Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_searchable);
Toolbar toolbar = (Toolbar) findViewById(R.id.app_bar);
setSupportActionBar(toolbar);
if(getSupportActionBar() != null && toolbar != null) {
getSupportActionBar().setDisplayHomeAsUpEnabled(true);
toolbar.setTitle("");
}
.
.
.
#Override
public boolean onCreateOptionsMenu(Menu menu) {
// Inflate the menu; this adds items to the action bar if it is present.
getMenuInflater().inflate(R.menu.menu_search, menu);
// Get the SearchView and set the searchable configuration
SearchManager searchManager = (SearchManager) getSystemService(Context.SEARCH_SERVICE);
SearchView searchView = (SearchView) menu.findItem(R.id.action_search).getActionView();
// Assumes current activity is the searchable activity
searchView.setSearchableInfo(searchManager.getSearchableInfo(getComponentName()));
searchView.setIconifiedByDefault(false);
final EditText searchEditText = (EditText) searchView.findViewById(android.support.v7.appcompat.R.id.search_src_text);
searchEditText.setImeOptions(DEFAULT_KEYS_SEARCH_LOCAL);
return true;
}
#Override
protected void onNewIntent(Intent intent) {
setIntent( intent );
handleSearch( getIntent() );
}
public void handleSearch(Intent intent){
if( Intent.ACTION_SEARCH.equalsIgnoreCase( intent.getAction() )){
String query = intent.getStringExtra(SearchManager.QUERY);
setTitle(query);
executeSearchShow(query);
}
}
So as I said, using backButton to back or calling finish() on activity the searchview widget stops working, but if I use homeUpButton it works normally.
I've override onBackPressed in Searchable activity and it works, but if I back from another activity(that I need to finish) to home the widget still stopping to work.
#Override
public void onBackPressed() {
onNavigateUp();
}
AndroidManifest.xml :
<activity
android:name=".activities.HomeActivity"
android:configChanges="orientation|keyboardHidden|screenSize"
android:label="#string/app_name"
android:theme="#style/Theme.MySeries"
android:windowSoftInputMode="adjustPan|adjustNothing">
<meta-data
android:name="android.app.default_searchable"
android:value="br.com.adley.whatsnextseries.activities.Searchable"/>
</activity>
<activity android:name=".activities.Searchable"
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>
I am learning to work with the search bar. After the user hits search, it starts 2 new activities and these new activities no longer have a search bar. It also does not update the UI. I am looking to do a search and post the results all in the same activity, if possible. I am using Genymotion emmulator.
Main Activity
public class MainActivity extends Activity {
protected LibraryDataSource mDataSource;
protected ArrayList<String> mBookTitles;
protected ArrayList<Integer> mBookPages;
private EditText mEditText;
#Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_main);
mDataSource = new LibraryDataSource(MainActivity.this);
mBookTitles = new ArrayList<>();
mBookPages = new ArrayList<>();
Book book1 = new Book("Harry potter", 15);
Book book2 = new Book("Far Cry", 25);
Book book3 = new Book("Jumpman", 35);
List<Book> books = new ArrayList<>();
books.add(book1);
books.add(book2);
books.add(book3);
mDataSource.open();
mDataSource.deleteAll();
mDataSource.insertBook(books);
mEditText = (EditText) findViewById(R.id.title_type);
}
#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);
SearchView searchView =
(SearchView) menu.findItem(R.id.search).getActionView();
searchView.setSearchableInfo(
searchManager.getSearchableInfo(MainActivity.this.getComponentName()));
return true;
}
#Override
public boolean onOptionsItemSelected(MenuItem item) {
// Handle action bar item clicks here. The action bar will
// automatically handle clicks on the Home/Up button, so long
// as you specify a parent activity in AndroidManifest.xml.
int id = item.getItemId();
//noinspection SimplifiableIfStatement
if (id == R.id.search) {
return true;
}
return super.onOptionsItemSelected(item);
}
}
Android Mainfest
<application
android:allowBackup="true"
android:icon="#mipmap/ic_launcher"
android:label="#string/app_name"
android:supportsRtl="true"
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>
<meta-data
android:name="android.app.default_searchable"
android:value=".SearchBookActivity" />
</activity>
<activity android:name=".SearchBookActivity">
<intent-filter>
<action android:name="android.intent.action.SEARCH" />
</intent-filter>
<meta-data
android:name="android.app.searchable"
android:resource="#xml/searchable" />
</activity>
</application>
SearchBookActivity
public class SearchBookActivity extends Activity {
LibraryDataSource mDataSource;
private EditText mEditText;
#Override
public void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_main);
mEditText = (EditText) findViewById(R.id.title_type);
mDataSource = new LibraryDataSource(SearchBookActivity.this);
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);
//use the query to search your data somehow
Log.i("Search bar", query);
mDataSource.open();
mEditText.setText(mDataSource.searchKeyString(query));
}
}
}
I would use both <meta-data> tags together in the same activity where I want to centralise the Search feature. Although you could have more than one activity that supports search. Material Design apps show that having only one Search UI, Fragment or an Activity, is more effective and best practices for app production.
<meta-data
android:name="android.app.default_searchable"
android:value=".SearchBookActivity" />
<meta-data
android:name="android.app.searchable"
android:resource="#xml/searchable" />
The daily case is searching a collection of data in a collection of data. Grids, Lists, etc.
You could have only one search Activity in your app that would get called each time you an Intent with action Search is started. This means from your app and from others. That's should for instance add permissions to your activity making sure that is not available to other apps unless is its function.
<meta-data
android:name="android.app.default_searchable"
android:value=".ui.activity.SearchBookActivity" />
<intent-filter>
<action android:name="android.intent.action.SEARCH" />
</intent-filter>
The above code will bring the SearchBookActivity when an Intent with android.intent.action.SEARCH is started. Then you should already be showing the Search View layout which will get the focus.
Another implementation would be to use a Fragment which doesn't require to listen to SEARCH actions or define any default search activity or permission.
Many apps do not require such a feature and use ListFragments with support of searching in ListView or RecyclerViews.
Either way, whether you use an Activity or a Fragment you would need to inflate the menu:
//From a Fragment
#Override
public void onCreateOptionsMenu(final Menu menu, MenuInflater inflater )
{
super.onCreateOptionsMenu(menu, inflater);
inflater.inflate(R.menu.menu_search, menu);
}
//From an Activity use public boolean onCreateOptionsMenu(Menu menu) in an Actiivty
with menu_search.xml
<menu xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:app="http://schemas.android.com/apk/res-auto">
<item android:id="#+id/search"
android:title="#string/search_label"
android:icon="#drawable/ic_actionbar_search"
app:actionViewClass="android.widget.SearchView"
android:actionViewClass="android.widget.SearchView"
app:showAsAction="always"/>
</menu>
Finally but not least. You could remove this code from the MainActivity. In the main activity you just need a menu item with a search icon.
// Associate searchable configuration with the SearchView
SearchManager searchManager =
(SearchManager) getSystemService(Context.SEARCH_SERVICE);
SearchView searchView =
(SearchView) menu.findItem(R.id.search).getActionView();
searchView.setSearchableInfo(
searchManager.getSearchableInfo(MainActivity.this.getComponentName()));
Hope you implement your Search!! Check out Material Design.
I have an activity with the new Toolbar. In this toolbar i have only one icon... my SearchView icon. When i click on that icon it opens an EditText in the Toolbar and im able to write what im looking for. The problem is that when i click on Search icon, the content of my Activity (FrameLayout with fragment) is reloaded.
EDIT:
plus: when click on icon, it reloads the activity and open EditText, after that the Activity SearchResult is called, and if i press the back button in that activity i return to the MainActivity and the searchview is still opened.
How to prevent that?
Thats my Manifest part of search (MainActivity has the icon):
<meta-data
android:name="android.app.default_searchable"
android:value=".activity.SearchResult_" />
<activity
android:name=".activity.MainActivity_"
android:configChanges="orientation|keyboardHidden|screenSize"
android:label="#string/app_name"
android:screenOrientation="portrait" >
</activity>
<activity
android:name=".activity.SearchResult_"
android:label="#string/title_activity_search_result"
android:screenOrientation="portrait" >
<meta-data
android:name="android.app.searchable"
android:resource="#xml/searchable" />
<intent-filter>
<action android:name="android.intent.action.SEARCH" />
</intent-filter>
</activity>
Thast my OnCreateOptionsMenu:
#Override
public boolean onCreateOptionsMenu(Menu menu) {
// Inflate the menu; this adds items to the action bar if it is present.
getMenuInflater().inflate(R.menu.menu_main, menu);
MenuItem searchItem = menu.findItem(R.id.action_search);
SearchManager searchManager = (SearchManager) MainActivity.this.getSystemService(Context.SEARCH_SERVICE);
SearchView.OnQueryTextListener queryTextListener = new SearchView.OnQueryTextListener()
{
public boolean onQueryTextChange(String newText)
{
Log.d("Query", newText);
return true;
}
public boolean onQueryTextSubmit(String query)
{
Log.d("Search", query);
SearchResult_.intent(MainActivity.this).extra("query", query).start();
return true;
}
};
SearchView searchView = null;
if (searchItem != null) {
searchView = (SearchView) MenuItemCompat.getActionView(searchItem);
}
if (searchView != null) {
searchView.setSearchableInfo(searchManager.getSearchableInfo(MainActivity.this.getComponentName()));
}
return super.onCreateOptionsMenu(menu);
}
I know I am late to the thread, but here is the proper solution. The way the SearchView default behavior works is that it will automatically send an ACTION.SEARCH intent to the searchable activity declared in your manifest. Upon sending, it will create a new instance of that activity, so that the onCreate() method can be called and the intent can be handled.
In order to work around this behavior, simply declare the launchMode for the searchable activity as 'SingleTop'. android:launchMode="singleTop"
According to Android documentation for "singleTop"
(https://developer.android.com/guide/components/activities/tasks-and-back-stack):
"If an instance of the activity already exists at the top of the current task, the system routes the intent to that instance through a call to its onNewIntent() method, rather than creating a new instance of the activity." If you declare your searchable activity as 'singleTop', make sure you override onNewIntent() and handle the ACTION.SEARCH intent data accordingly.
In your onQueryTextSubmit method do:
searchView.setQuery (query, false);
You cant. unfortunately everyone has the same exact issue. searchViews always call the query when it is opened. no way to avoid it. I have seen some people have a boolean called first and set it to true. then only do logic if it is false. then on the first query call set it to false. not a good way but it should work
I don't know if that it is a bad practice, but I fix it like this:
#Override
protected void onResume() {
super.onResume();
if (searchView != null) {
if (searchView.getQuery().length() <= 0) {
searchView.setQuery("", false);
}
}
}
Normal Behavior:
When I request search in the SearchView widget on ActionBar, after
clicking soft-keyboard's search action button, the input text is
cleared from the SearchView.
My desired behavior:
I want the my input text to remain the same, when I perform search. How is that possible?
I have read official docs for SearchView, and guides from here, and also here, but haven't had any luck.
EDIT:
I discovered that this text reset happens whenever I scroll my ViewPager.
Try this:
mSearchView.setOnQueryTextListener(new SearchView.OnQueryTextListener()
{
#Override
public boolean onQueryTextSubmit(String query)
{
mSearchView.setQuery(query,false);
return false;
}
#Override
public boolean onQueryTextChange(String newText)
{
return false;
}
});
If your search widget calls another activity to handle the query, it gets cleared because the activity is newly created.
Therefore you need to set the query manually from 2 places:
onCreateOptionsMenu where you initialize your search widget
onNewIntent where you receive your query
Why from 2 places? Because if the search-handling activity is newly created, onNewIntent is called first and the search widget is not yet ready to be used. In that case save the query at onNewIntent and set it at onCreateOptionsMenu. Otherwise it can be directly set at onNewIntent.
Here's an example:
private String mQuery;
private SearchView mSearchView;
#Override
protected void onNewIntent(Intent intent) {
super.onNewIntent(intent);
mQuery = intent.getStringExtra(SearchManager.QUERY);
if (mSearchView != null) {
mSearchView.setQuery(mQuery, false);
}
// Do something with the new query
}
#Override
public boolean onCreateOptionsMenu(Menu menu) {
// Inflate the actionbar items
getMenuInflater().inflate(R.menu.actionbar_items, menu);
// Get SearchView and set the searchable configuration
SearchManager searchManager = (SearchManager) getSystemService(Context.SEARCH_SERVICE);
mSearchView = (SearchView) menu.findItem(R.id.action_search).getActionView();
mSearchView.setSearchableInfo(searchManager.getSearchableInfo(getComponentName()));
// Disable collapsing the search widget
mSearchView.setIconifiedByDefault(false);
// Set the query
mSearchView.setQuery(mQuery, false);
return true;
}
I have implemented search function using SearchManager and SearchView in the action bar. The same activity that shows the search view performs the search and shows the search result. This is working fine, and I can get the search query from the onNewIntent() without a problem.
When the user clicks the back button to get out of the search mode, I need to redisplay all items. How do I accomplish this? I tried intercepting OnDismiss and OnCancel of the search manager as well as the OnClose of the search view. Nothing ever gets called. Code below:
#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, menu);
// Associate searchable configuration with the SearchView
SearchManager searchManager =
(SearchManager) this.getSystemService(Context.SEARCH_SERVICE);
SearchView searchView =
(SearchView) menu.findItem(R.id.search).getActionView();
searchView.setSearchableInfo(
searchManager.getSearchableInfo(getComponentName()));
//Try to detect end of search session. None of the listeners getting called.
searchManager.setOnCancelListener(new OnCancelListener() {
#Override
public void onCancel() {//...}
});
searchManager.setOnDismissListener(new OnDismissListener() {
#Override
public void onDismiss() {//...}
});
searchView.setOnCloseListener(new OnCloseListener() {
#Override
public boolean onClose() {
return false;
}
});
return true;
}
The manifest file entry for the activity is like this:
<activity
android:name="com.example.MainActivity"
android:label="#string/app_name"
android:launchMode="singleTop">
<meta-data
android:name="android.app.searchable"
android:resource="#xml/searchable" />
<intent-filter>
<action android:name="android.intent.action.SEARCH" />
</intent-filter>
<intent-filter>
<action android:name="android.intent.action.MAIN" />
<category android:name="android.intent.category.LAUNCHER" />
</intent-filter>
</activity>
I was facing the same problem, and I found the solution which is quite simple. If you are using the searchWidget (like me) you simply need to do this:
searchView.setOnCloseListener(new SearchView.OnCloseListener() {
#Override
public boolean onClose() {
// Put your code here to clear and display the results
return false;
}
});
Because actually what are the user is closing is the SearchView, not the SearchManager. I've just test it and it works like a charm!
Hope this works for you.
Regards