Reputation: 339
I implemented SearchView
in Fragment
as @David suggested in this topic. Does anyone know how to add Recent Query Suggestions to SearchView in Fragment?
Upvotes: 3
Views: 2519
Reputation: 412
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.
Upvotes: 3
Reputation: 2219
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.
Upvotes: 6