I implemented SearchView
in Fragment
as @David suggested in this topic. Does anyone know how to add Recent Query Suggestions to SearchView in Fragment?

- 1
- 1

- 379
- 3
- 15
-
what do you mean by **"Recent Query Suggestions"** ? – pskink Oct 06 '15 at 15:32
-
@pskink I mean search suggestions based on recent search queries. – hornet2319 Oct 06 '15 at 15:34
-
where you want your suggestions to be placed? in a drop down list? – pskink Oct 06 '15 at 15:35
-
@pskink yes, as in Play Market. – hornet2319 Oct 06 '15 at 15:37
-
`SearchView#setSuggestionsAdapter(CursorAdapter adapter)` – pskink Oct 06 '15 at 15:37
2 Answers
From android documentation.
STEP 1 - Create a Content Provider
Creathe MySuggestionProvider class, mine is located at searchviewpager/utils/MySuggestionProvider
package com.soon.karat.searchviewpager.utils;
import android.content.SearchRecentSuggestionsProvider;
public class MySuggestionProvider extends SearchRecentSuggestionsProvider {
// AUTHORITY is a unique name, but it is recommended to use the name of the
// package followed by the name of the class.
public final static String AUTHORITY = "com.soon.karat.searchviewpager.utils.MySuggestionProvider";
// Uncomment line below, if you want to provide two lines in each suggestion:
// public final static int MODE = DATABASE_MODE_QUERIES | DATABASE_MODE_2LINES;
public final static int MODE = DATABASE_MODE_QUERIES;
public MySuggestionProvider() {
setupSuggestions(AUTHORITY, MODE);
}
}
STEP 2 - Add the provider into the Manifest
Add the following line of code in your Manifest in the application level
<application...>
<provider
android:name=".utils.MySuggestionProvider"
android:authorities="com.soon.karat.searchviewpager.utils.MySuggestionProvider" />
android:name: It is the path where your SuggestionProvider class is located. Mine is located at searchviewpager/utils/MySuggestionProvider --> therefore the name will be .utils.MySuggestionProvider
android:authorities: It is the same String AUTHORITY you put in your SuggestionProvider, It should be the same name.
STEP 3 - Add search to your searchable.xml
Add the two last lines (searchSuggestAuthority and searchSuggestSelection) into your 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_for_anything"
android:searchSuggestAuthority="com.soon.karat.searchviewpager.utils.MySuggestionProvider"
android:searchSuggestSelection=" ?"/>
android:searchSuggestAuthority: It is the same String AUTHORITY you put in your MySuggestionProvider class.
android:searchSuggestSelection: It is just a space and a question marker.
STEP 4 - Save the Queries
Whenever the user submit a query you should save it in your SuggestionProvider, let's say you will save it inside onQueryTextSubmit
searchView.setOnQueryTextListener(new SearchView.OnQueryTextListener() {
@Override
public boolean onQueryTextSubmit(String query) {
Log.i(TAG, "onQueryTextSubmit: Query was submitted");
// -------------------------------------------------------
// Lines responsible to save the query
// -------------------------------------------------------
SearchRecentSuggestions suggestions = new SearchRecentSuggestions(MainActivity.this,
MySuggestionProvider.AUTHORITY,
MySuggestionProvider.MODE);
suggestions.saveRecentQuery(query, null);
// -------------------------------------------------------
displaySearchResults(query);
// The listener can override the standard behavior by returning true to indicate that it has
// handled the submit request. Otherwise return false to let the SearchView handle the
// submission by launching any associated intent.
return true;
}
ADITIONALS
Hide the Keyboard and Dismiss the SearchView
Maybe you want to hide the keyboard and dismiss the searchview when the user finish submitting the query to your app. In order to do that you use the following lines of code.
private void dismissKeyBoardAndSearchView() {
Helpers.hideKeyBoard(this);
// You should check if MenuItem is not null, otherwise the app would crash when rotating the screen
if (searchMenuItem != null) {
searchMenuItem.collapseActionView();
}
}
searchMenuItem is a MenuItem and it is the same you used when creating the SearchView in onCreateOptionsMenu, you just declare it globally to access in this other method or you parse it into the method:
@Override
public boolean onCreateOptionsMenu(Menu menu) {
MenuInflater inflater = getMenuInflater();
inflater.inflate(R.menu.options_menu, menu);
SearchManager searchManager = (SearchManager) getSystemService(Context.SEARCH_SERVICE);
// -----------------------------------------------
// This is the MenuItem you will collapse
// -----------------------------------------------
searchMenuItem = menu.findItem(R.id.menu_search);
// -----------------------------------------------
SearchView searchView = (SearchView) searchMenuItem.getActionView();
searchView.setSearchableInfo(searchManager.getSearchableInfo(getComponentName()));
searchView.setQueryRefinementEnabled(true);
searchView.setOnQueryTextListener(new SearchView.OnQueryTextListener() {
@Override
public boolean onQueryTextSubmit(String query) {
Hiding the keyboard is inside Helpers Class
public class Helpers {
public static void hideKeyBoard(Activity activity) {
InputMethodManager inputMethodManager = (InputMethodManager) activity.getSystemService(Activity.INPUT_METHOD_SERVICE);
View view = activity.getCurrentFocus();
if (view == null) {
view = new View(activity);
}
assert inputMethodManager != null;
inputMethodManager.hideSoftInputFromWindow(view.getWindowToken(), 0);
}
}
Handle when the user clicks in a suggestion
You may notice that when the user clicks in a suggestion nothing will happen because onQueryTextSubmit is not called. Android just restart the activity and send the query to it. Then, to handle clicks in the suggestion you should add the following code:
Supposing the activity that make the search is the same that receives the search:
STEP 1 - Set launchMode to singleTop
Set your activity launchMode to singleTop in the Manifest to prevent the system to recreate the activity several times when the user makes several searches.
<application...>
<provider
android:name=".utils.MySuggestionProvider"
android:authorities="com.soon.karat.searchviewpager.utils.MySuggestionProvider" />
<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>
STEP 2 - Handle the intent in onCreate and in onNewIntent
You should handle the search intent both in onCreate and in onNewItent. onNewIntent is necessary because you set your activity to singleTop, when the app receives a search, instead of recreating the activity and calling onCreate, it will just call onNewIntent.
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_main);
handleIntent(getIntent());
}
@Override
protected void onNewIntent(Intent intent) {
setIntent(intent);
handleIntent(intent);
}
Customizing your recent query suggestion layout
Maybe, you do not like the way your recent query suggestion are being displayed, then you can change its layout by a simple way.
<!-- Base application theme. -->
<style name="AppTheme" parent="Theme.AppCompat.Light.NoActionBar">
<!-- Customize your theme here. -->
<item name="colorPrimary">@color/colorPrimary</item>
<item name="colorPrimaryDark">@color/colorPrimaryDark</item>
<item name="colorAccent">@color/colorAccent</item>
<!-- Add this line -->
<item name="searchViewStyle">@style/MySearchViewStyle</item>
</style>
<style name="MySearchViewStyle" parent="Widget.AppCompat.SearchView" >
<item name="suggestionRowLayout">@layout/my_search_dropdown_item_icons</item>
</style>
my_search_drodown_item_icons: This is the layout you create and customize it.
WARNING: You should keep with the same ids android had for the previous layout in order for it to work. Then, go to this file @layout/abc_search_dropdown_item_icons_2line and copy the same ids.
If you have difficult to found this abc file, you can replace @layout/my_search_dropdown_item_icons to --> @layout/abc_search_dropdown_item_icons_2line --> then place your cursor into "abc_search..." and press Ctrl+B. You will be redirect to that file where you can take the same ids.

- 2,107
- 22
- 43
-
1How is this related to the Fragment, this is still using an activity to handle recent suggestions ? – duskandawn Jul 12 '20 at 01:55
Ok so I found this solution with the help of this particular repository on github thanks and credit to him.
https://github.com/nex3z/android-examples/tree/master/CustomSearchSuggestionItem
Here is a sample implementation of how you can integrate a search view in fragment with recent suggestion query in Kotlin -
Create Adapter For Showing Recent List
SearchAdapter.java
public class SearchAdapter extends CursorAdapter { private static final String LOG_TAG = SearchAdapter.class.getSimpleName(); public SearchAdapter(Context context, Cursor c, int flags) { super(context, c, flags); } @Override public View newView(Context context, Cursor cursor, ViewGroup parent) { View view = LayoutInflater.from(context).inflate( R.layout.search_item, parent, false); ViewHolder viewHolder = new ViewHolder(view); view.setTag(viewHolder); return view; } @Override public void bindView(View view, Context context, Cursor cursor) { ViewHolder viewHolder = (ViewHolder) view.getTag(); viewHolder.mTitle.setText( cursor.getString(cursor. getColumnIndex(SearchManager.SUGGEST_COLUMN_TEXT_1))); } public String getSuggestionText(int position) { if (position >= 0 && position < getCursor().getCount()) { Cursor cursor = getCursor(); cursor.moveToPosition(position); return cursor.getString(cursor.getColumnIndex(SearchManager.SUGGEST_COLUMN_TEXT_1)); } return null; } public static class ViewHolder { public TextView mTitle; public ViewHolder(View view) { // view of your custom layout mTitle = (TextView) view.findViewById(R.id.text); } } }
search_item.xml (Inside Layout Folder)
<TextView xmlns:android="http://schemas.android.com/apk/res/android" xmlns:app="http://schemas.android.com/apk/res-auto" xmlns:tool="http://schemas.android.com/tools" android:id="@+id/text" android:layout_width="wrap_content" android:layout_height="50dp" android:fontFamily="@font/poppins_bold" android:textColor="@color/colorPrimary" android:gravity="start|center" android:paddingStart="30dp" android:paddingEnd="30dp" tool:text="hello" />
menu.xml (put this inside your menu.xml file)
<item android:id="@+id/action_search" android:icon="@android:drawable/ic_menu_search" android:title="@string/search" app:actionViewClass="androidx.appcompat.widget.SearchView" app:showAsAction="always" />
SuggestionProvider.kt
class SuggestionProvider : SearchRecentSuggestionsProvider() { init { setupSuggestions(AUTHORITY, MODE) } companion object { // Set As Path To This File const val AUTHORITY = "com.your_package_name.SuggestionProvider" const val MODE: Int = DATABASE_MODE_QUERIES } }
AndroidManifest.xml (Make Sure android:authrotities field have same path which is on SuggestionProvider.kt AUTHORITY FIELD HAVE)
<application> ..... <provider android:name=".utils.SuggestionProvider" android:authorities="com.you_package_name.SuggestionProvider" /> ..... </application>
YourFragment.kt (Put this code inside your already created fragment where you want to have search bar in action bar)
private var mSuggestionAdapter: SearchAdapter? = null private lateinit var searchView: SearchView private var queryTextListener: SearchView.OnQueryTextListener? = null override fun onOptionsItemSelected(item: MenuItem): Boolean { when (item.itemId) { R.id.action_search -> return false } searchView.setOnQueryTextListener(queryTextListener) return super.onOptionsItemSelected(item) } override fun onCreateOptionsMenu(menu: Menu, inflater: MenuInflater) { inflater.inflate(R.menu.menu, menu) val searchItem = menu.findItem(R.id.action_search) val searchManager = activity!!.getSystemService(Context.SEARCH_SERVICE) as SearchManager mSuggestionAdapter = SearchAdapter(activity, null, 0) if (searchItem != null) { searchView = searchItem.actionView as SearchView } if (searchView != null) { searchView.setSearchableInfo(searchManager.getSearchableInfo(activity!!.componentName)) searchView.suggestionsAdapter = mSuggestionAdapter; queryTextListener = object : SearchView.OnQueryTextListener { override fun onQueryTextChange(newText: String): Boolean { // Update Cursor With Each Query Text Change val cursor = getRecentSuggestions(newText) if (cursor != null) { mSuggestionAdapter?.swapCursor(cursor) } return false } override fun onQueryTextSubmit(query: String): Boolean { // Save Submitted Query To Adapter val suggestions = SearchRecentSuggestions( activity, SuggestionProvider.AUTHORITY, SuggestionProvider.MODE ) suggestions.saveRecentQuery(query, null) // Do Your Search Stuff Here With Query return true } } searchView.setOnSuggestionListener(object : SearchView.OnSuggestionListener { override fun onSuggestionSelect(position: Int): Boolean { return false } override fun onSuggestionClick(position: Int): Boolean { // On Clicking Suggestion Load It To Submit Query Listener searchView.setQuery(mSuggestionAdapter?.getSuggestionText(position), true) return true } }) searchView.setOnQueryTextListener(queryTextListener) } super.onCreateOptionsMenu(menu, inflater) } // Function To Retrieve Suggestion From Content Resolver fun getRecentSuggestions(query: String): Cursor? { val uriBuilder = Uri.Builder() .scheme(ContentResolver.SCHEME_CONTENT) .authority(SuggestionProvider.AUTHORITY) uriBuilder.appendPath(SearchManager.SUGGEST_URI_PATH_QUERY) val selection = " ?" val selArgs = arrayOf(query) val uri = uriBuilder.build() return activity?.contentResolver?.query(uri, null, selection, selArgs, null) }
That's It you have a search bar in your fragment which query result you can control how you handle searching and a recent suggestion.

- 314
- 2
- 6
-
Thanks for this solution ! Globally it's works fine but sometines I got this error with no more details and no trace : `java.lang.IllegalStateException: attempt to re-open an already-closed object: SQLiteQuery: SELECT 0 AS suggest_format, 'android.resource://system/17301578' AS suggest_icon_1, display1 AS suggest_text_1, query AS suggest_intent_query, _id FROM suggestions WHERE display1 LIKE ? ORDER BY date DESC` Do you have any idea how to fix it ? – Thomas Pires Jan 14 '21 at 11:48