Реализация виджета поиска панели действий Android в ListFragment

В настоящее время мое приложение настроено с ListFragment слева и DetailsFragment справа (аналогично расположению на планшете ниже).

layout

На панели действий у меня есть виджет поиска, который при отправке поискового запроса должен обновлять содержимое списка (в ListFragment). Я следил за руководством по поиску на Android-разработчике (http://developer.android.com/guide/topics/search/search-dialog.html), но у меня возникают проблемы с поиском для обновления списка в том же действии.

Мне удалось заставить некоторую форму реализации работать, вызывая новое действие при отправке поискового запроса, но это заставляет кнопку «Назад» вести себя странно (т.е. вы нажимаете «Назад» и переходите к предыдущему действию с виджетом поиска, все еще отображающим поисковый запрос) ).

Я добавил строки намерения в манифест следующим образом

<intent-filter>
        <action android:name="android.intent.action.SEARCH" />
    </intent-filter>
    <meta-data android:name="android.app.searchable"
               android:resource="@xml/searchable"/>

Можно ли заставить виджет поиска обновлять список текущей активности?




Ответы (1)


Да, на самом деле, это «нормальный» вариант использования. Что вы должны помнить, так это то, что фрагмент, который использует виджет панели действий поиска, должен быть частью, которая регистрирует его в целом, поскольку виджет будет уничтожен вместе с этим фрагментом.

Вот как правильно завязывать:

в вашем ListFragment сообщите, что у вас есть пункты меню опций...

@Override
public void onActivityCreated(Bundle savedInstanceState) {
    ..
    setHasOptionsMenu(true);
    ..
}

затем внутри того же ListFragment создайте меню параметров, переопределив обратный вызов, когда у вас есть ссылка на виджет SearchView, зарегистрируйте обратный вызов QueryTextListener:

@Override 
public void onCreateOptionsMenu(Menu menu, MenuInflater inflater) {
    inflater.inflate(R.menu.grid_default, menu); 
    SearchView searchView = (SearchView)menu.findItem(R.id.grid_default_search).getActionView();
    searchView.setOnQueryTextListener(queryListener);
}

создайте виджет поиска либо программно (Java), либо декларативно (XML), вот XML (с именем grid_default.xml, сверху):

<?xml version="1.0" encoding="utf-8"?>
<menu xmlns:android="http://schemas.android.com/apk/res/android">

    <item     
        android:id="@+id/grid_default_search"
        android:icon="@android:drawable/ic_menu_search"
        android:title="search"
        android:showAsAction="always"
        android:actionViewClass="android.widget.SearchView" 
    />

    <!-- other items or whatever  -->

</menu>

Теперь, вернувшись в свой ListFragment, вам нужно создать queryListener, который мы зарегистрировали выше:

private String grid_currentQuery = null; // holds the current query...

final private OnQueryTextListener queryListener = new OnQueryTextListener() {       

    @Override
    public boolean onQueryTextChange(String newText) {
        if (TextUtils.isEmpty(newText)) {
            getActivity().getActionBar().setSubtitle("List");               
            grid_currentQuery = null;
        } else {
            getActivity().getActionBar().setSubtitle("List - Searching for: " + newText);
            grid_currentQuery = newText;

        }   
        getLoaderManager().restartLoader(0, null, MyListFragment.this); 
        return false;
    }

    @Override
    public boolean onQueryTextSubmit(String query) {            
        Toast.makeText(getActivity(), "Searching for: " + query + "...", Toast.LENGTH_SHORT).show();
        return false;
    }
};

теперь вы знаете, когда запрос изменен или отправлен, в моем примере я повторно запрашиваю каждый раз, когда нажимается клавиша, потому что моя база данных была маленькой и быстрой с результатами, вам может потребоваться изменить это.. но вы заметите, как я просто используя виджет, чтобы установить поле частного класса внутри ListFragment для текущего текста виджета поиска (grid_currentQuery), а затем я просто вызываю getLoaderManager().restartLoader(0, null, MyListFragment.this);, где внутри моего onCreateLoader я просто обновляю запрос, который я использовал следующим образом:

@Override
public Loader<Cursor> onCreateLoader(int id, Bundle bargs) {

    String sort = "SortColumn ASC";
    String[] grid_columns = new String[] { "ColumnA", "ColumnB", "Etc..." };
    String grid_whereClause = "ColumnToSearchBy LIKE ?"

    if (!TextUtils.isEmpty(grid_currentQuery)) {            
        return new CursorLoader(getActivity(), DataProvider.CONTENT_URI, grid_columns, grid_whereClause, new String[] { grid_currentQuery + "%" }, sort);
    }       

    return new CursorLoader(getActivity(), DataProvider.CONTENT_URI, grid_columns, null, null, sort);
}

и это все, что вам действительно нужно сделать, я понимаю, что это немного нарезано, но это так, потому что есть еще много вещей, которые должны произойти, установка адаптера, запуск загрузчика в первую очередь с использованием getLoaderManager().initLoader(0, null, this); и все остальное обратные вызовы загрузчика, которые необходимо обрабатывать... но если вы следуете рекомендациям, предложенным пользователями Android (используя LoaderManagers в ваших ListActivitys и ListFragmentss, это должно быть довольно легко для вас...

Я надеюсь, что это поможет, не стесняйтесь просить разъяснений в комментариях, если вам нужно -ck

person ckozl    schedule 05.03.2012
comment
Большое спасибо за подробное объяснение. Я постараюсь реализовать его в ближайшие несколько дней и дам вам знать, если у меня возникнут какие-либо проблемы. - person bencallis; 05.03.2012
comment
Спасибо, мне удалось заставить его работать, очень информативный ответ. Одна вещь, которую я задавался вопросом. По умолчанию в приложении у меня есть 3 вкладки, каждая из которых представляет категорию, при поиске я хотел бы удалить эти вкладки. Это не проблема, проблема, с которой я сталкиваюсь, заключается в том, как узнать, когда пользователь закончил поиск (вернулся), чтобы повторно добавить вкладки. - person bencallis; 07.03.2012
comment
@bencallis, вы бы больше всего хотели использовать setOnCloseListener (прослушиватель SearchView.OnCloseListener) в SearchView... - person ckozl; 07.03.2012
comment
Я только что занимался этим, но у меня проблемы. searchView.setOnCloseListener(new OnCloseListener() { @Override public boolean onClose() { Toast.makeText(DealPadActivity.this, Closed, Toast.LENGTH_SHORT).show(); Log.d(Поиск, поиск закрыт); Это не так вроде звонят. - person bencallis; 07.03.2012
comment
он вызывается только при нажатии x, чтобы закрыть поиск, это отлично работает для меня: ; } }); - person ckozl; 07.03.2012
comment
вы можете попробовать ActionMode, так как он имеет более четкое представление о том, что он закончен или закрыт... - person ckozl; 07.03.2012
comment
stackoverflow.com/questions/9327826/ кажется, есть ошибка в ICS. Как будет работать ActionMode? - person bencallis; 08.03.2012
comment
Кажется, onCloseListener работает на моем планшете с ICS, но не на моем телефоне :( - person bencallis; 08.03.2012
comment
Хм, моя единственная проблема в том, что я не знаю, что добавить в DataProvider.CONTENT_URI. Я использую предварительно заполненную базу данных sqlite, которую копирую в пользовательскую папку, поэтому у меня нет класса для каждой таблицы. Что я должен делать? - person Deses; 10.05.2013
comment
getLoaderManager().restartLoader(0, null, MyListFragment.this); эта строка дает мне ошибку, мне нужно выполнить поиск в ArrayList‹String›, можно ли настроить адаптер для отображения предложения по изменению текста - person Dwivedi Ji; 28.04.2014
comment
@ckozl Моя панель действий Search View, увеличенная из фрагмента, показывает мягкую клавиатуру при касании, но курсор не появляется, и текст, который я печатаю, не появляется в поле поиска. Это почему? - person Sndn; 23.02.2015