Я реализовал SearchView
в Fragment
, как предложил @David в этой теме. Кто-нибудь знает, как добавить Recent Query Suggestions в SearchView во фрагменте?
Недавние предложения запросов во фрагменте
Ответы (2)
ШАГ 1. Создайте поставщика контента
Создайте класс MySuggestionProvider, мой находится в 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);
}
}
ШАГ 2. Добавьте провайдера в манифест
Добавьте следующую строку кода в свой манифест на уровне приложения.
<application...>
<provider
android:name=".utils.MySuggestionProvider"
android:authorities="com.soon.karat.searchviewpager.utils.MySuggestionProvider" />
android:name: это путь, по которому находится ваш класс SuggestionProvider. Мой находится в searchviewpager/utils/MySuggestionProvider --> поэтому имя будет .utils.MySuggestionProvider
android:authorities: это та же самая строка AUTHORITY, которую вы указали в SuggestionProvider. Она должна иметь то же имя.
ШАГ 3. Добавьте поиск в файл searchable.xml
Добавьте две последние строки (searchSuggestAuthority и searchSuggestSelection) в файл 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: это та же строка AUTHORITY, которую вы указали в своем классе MySuggestionProvider.
android:searchSuggestSelection: это просто пробел и маркер вопроса.
ШАГ 4. Сохраните запросы
Всякий раз, когда пользователь отправляет запрос, вы должны сохранить его в своем SuggestionProvider, скажем, вы сохраните его внутри 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;
}
ДОПОЛНИТЕЛЬНО
Скрыть клавиатуру и закрыть SearchView
Возможно, вы хотите скрыть клавиатуру и закрыть окно поиска, когда пользователь завершит отправку запроса в ваше приложение. Для этого вы используете следующие строки кода.
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 — это MenuItem, и это то же самое, что вы использовали при создании SearchView в onCreateOptionsMenu, вы просто объявляете его глобально для доступа в этом другом методе или анализируете его в методе:
@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) {
Скрытие клавиатуры внутри класса Helpers
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);
}
}
Обрабатывать, когда пользователь нажимает на предложение
Вы можете заметить, что когда пользователь нажимает на предложение, ничего не происходит, потому что onQueryTextSubmit не вызывается. Android просто перезапускает действие и отправляет ему запрос. Затем для обработки кликов в предложении необходимо добавить следующий код:
Предположим, что действие, которое выполняет поиск, совпадает с действием, которое получает поиск:
ШАГ 1. Установите для параметра launchMode значение singleTop
Задайте для запуска действия значение singleTop в манифесте, чтобы система не создавала действие несколько раз, когда пользователь выполняет несколько поисков.
<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
Вы должны обрабатывать цель поиска как в onCreate, так и в onNewItent. onNewIntent необходим, потому что вы устанавливаете свою активность в singleTop, когда приложение получает поиск, вместо того, чтобы воссоздавать активность и вызывать onCreate, оно просто вызывает 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);
}
Настройка макета подсказки для недавнего запроса
Возможно, вам не нравится, как отображается ваш недавний запрос, тогда вы можете изменить его макет простым способом.
<!-- 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: это макет, который вы создаете и настраиваете.
ВНИМАНИЕ: вы должны использовать те же идентификаторы, которые были у Android для предыдущего макета, чтобы он работал. Затем перейдите в этот файл @layout/abc_search_dropdown_item_icons_2line и скопируйте те же идентификаторы.
Если вам сложно найти этот файл abc, вы можете заменить @layout/my_search_dropdown_item_icons на --> @layout/abc_search_dropdown_item_icons_2line --> затем поместите курсор на «abc_search...» и нажмите Ctrl+B. Вы будете перенаправлены в этот файл, где вы можете взять те же идентификаторы.
Итак, я нашел это решение с помощью этого конкретного репозитория на github, спасибо ему и спасибо.
https://github.com/nex3z/android-examples/tree/master/CustomSearchSuggestionItem
Вот пример реализации того, как вы можете интегрировать представление поиска во фрагменте с недавним запросом предложения в Kotlin -
Создать адаптер для отображения списка последних
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 (внутри папки макета)
<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 (поместите его в файл menu.xml)
<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 (убедитесь, что в поле android:authrotities указан тот же путь, что и в поле AUTHORITY FIELD SuggestionProvider.kt)
<application> ..... <provider android:name=".utils.SuggestionProvider" android:authorities="com.you_package_name.SuggestionProvider" /> ..... </application>
YourFragment.kt (поместите этот код в уже созданный фрагмент, где вы хотите, чтобы панель поиска отображалась на панели действий)
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) }
Вот и все, у вас есть панель поиска в вашем фрагменте, результат запроса которого вы можете контролировать, как вы обрабатываете поиск и последнее предложение.
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
У вас есть идеи, как это исправить?
- person Thomas Pires; 14.01.2021