web-dev-qa-db-ja.com

Android ListFragmentでのアクションバー検索ウィジェットの実装

現在、アプリケーションを左側にListFragment、右側にDetailsFragmentを設定しています(下のタブレットのレイアウトと同様)。

layout

アクションバーには、検索用語が送信されたときにリストの内容を更新する検索ウィジェットがあります(ListFragment内)。 Android dev( 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"/>

検索ウィジェットを取得して、現在のアクティビティのリストを更新することはできますか?

20
bencallis

はい、実際のところ、これは「通常の」ユースケースです。覚えておく必要があるのは、検索アクションバーウィジェットを使用するフラグメントは、ウィジェットがそのフラグメントとともに破棄されるため、一般にそれを登録する部分である必要があるということです。

適切に結び付ける方法は次のとおりです。

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が必要な場合は、コメントで説明を求めてください。

46
ckozl