web-dev-qa-db-ja.com

Android:検索可能なアクティビティなしで最近の検索候補を提供しますか?

ActionBar SearchViewがあり、それを使用して正常に検索を行うことができます。 Androidのドキュメントには、検索候補の実装方法が説明されていません。検索可能なアクティビティは必要ありません。

これは私の検索コードです:

public boolean onCreateOptionsMenu(Menu menu) {
        getMenuInflater().inflate(R.menu.activity_add_song, menu);
        final SearchView searchView = (SearchView) menu.findItem(R.id.song_search).getActionView();
        searchView.setFocusable(true);
        searchView.setIconified(false);
        final AddSongActivity activity = this;
        searchView.setOnQueryTextListener(new SearchView.OnQueryTextListener() {
            @Override
            public boolean onQueryTextChange(String newText) {
                // Do nothing
                return true;
            }

            @Override
            public boolean onQueryTextSubmit(String query) {
                // Clear SearchView
                searchView.clearFocus();
                // Begin Spotify Search
                TextView notice = (TextView)findViewById(R.id.search_notice);
                URL url;
                try {
                    url = new URL("http://ws.spotify.com/search/1/track.json?q=" + URLEncoder.encode(query,"UTF-8"));
                } catch (MalformedURLException e) {
                    notice.setText("Malformed Search");
                    notice.setHeight(noticeHeight);
                    return true;
                } catch (UnsupportedEncodingException e) {
                    notice.setText("Unsupported Encoding. Maybe a problem with your device.");
                    notice.setHeight(noticeHeight);
                    return true;
                }
                new SearchDownload(url, activity).execute();
                notice.setText("Loading Tracks");
                notice.setHeight(noticeHeight);
                Log.i("infodb","" + noticeHeight);
                return true;
            }
        });

これは検索で機能しますが、最近の検索クエリの提案を実装する考えはありません。これを行うにはどうすればよいですか?

ありがとうございました。

24

OK、私はこれに時間を費やしました。 SQLiteDatabaseからsimple提案実装を自分で作成します。

次のような3つのクラスを作成します

  1. MainActivity-データベースからのSearchView提案のテスト用
  2. SuggestionDatabase-これには、最近の検索キーワードが保存されます。
  3. SuggestionSimpleCursorAdapter-これはSimpleCursorAdapterのサブクラスです。 SimpleCursorAdapterを使用する代わりに、このクラスを作成する理由を説明します。

コード

_// MainActivity.Java

public class MainActivity 
    extends Activity
    implements SearchView.OnQueryTextListener,
                SearchView.OnSuggestionListener
{

    private SuggestionsDatabase database;
    private SearchView searchView;

    @Override
    protected void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        setContentView(R.layout.activity_main);


        database = new SuggestionsDatabase(this);
        searchView = (SearchView) findViewById(R.id.searchView1);
        searchView.setOnQueryTextListener(this); 
        searchView.setOnSuggestionListener(this);
    }

    @Override
    public boolean onSuggestionSelect(int position) {

        return false;
    }

    @Override
    public boolean onSuggestionClick(int position) {

        SQLiteCursor cursor = (SQLiteCursor) searchView.getSuggestionsAdapter().getItem(position);
        int indexColumnSuggestion = cursor.getColumnIndex( SuggestionsDatabase.FIELD_SUGGESTION);

        searchView.setQuery(cursor.getString(indexColumnSuggestion), false);

        return true;
    }

    @Override
    public boolean onQueryTextSubmit(String query) {
        long result = database.insertSuggestion(query);
        return result != -1;
    }

    @Override
    public boolean onQueryTextChange(String newText) {

        Cursor cursor = database.getSuggestions(newText);
        if(cursor.getCount() != 0)
        {
            String[] columns = new String[] {SuggestionsDatabase.FIELD_SUGGESTION };
            int[] columnTextId = new int[] { Android.R.id.text1};

            SuggestionSimpleCursorAdapter simple = new SuggestionSimpleCursorAdapter(getBaseContext(),
                    Android.R.layout.simple_list_item_1, cursor,
                    columns , columnTextId
                    , 0);

            searchView.setSuggestionsAdapter(simple);
            return true;
        }
        else
        {
            return false;
        }
    }

}
_

使い方

  1. ユーザーが検索ボタンをタップすると、onQueryTextSubmit()がトリガーされ、検索キーワードがデータベースに保存されます。 「こんにちは」というキーワードを送信するとします
  2. ユーザーがSearchViewに「Hel」や「H」などの文字列を書き込むと、onQueryTextChange()が呼び出され、SQLiteDatabaseSuggestionDatabase)。 「Hel」または「H」が「Hello」と一致する場合は、返されたカーソルをSuggestionSimpleCursorAdapterに設定してクエリの結果を表示し、次にこのアダプタをSearchViewに設定します。こちらがその写真です。

enter image description here
3。もちろん、「Hello」という提案をタップします。そのためにonSuggestionClick(int position)が呼び出されます。 SQLiteCursorのアダプター(SearchView)からSuggestionSimpleCursorAdapterオブジェクトを取得し、そこから提案テキストを取得し、SearchViewオブジェクトに提案テキストを設定します

_SQLiteCursor cursor = (SQLiteCursor) searchView.getSuggestionsAdapter().getItem(position);
int indexColumnSuggestion = cursor.getColumnIndex( SuggestionsDatabase.FIELD_SUGGESTION);
searchView.setQuery(cursor.getString(indexColumnSuggestion), false);
_

SimpleCursorAdapterを使用する場合も適切に機能しますが、このシナリオがあるとしましょう

  1. このプログラムをスマートフォンで実行し、キーワード「Hel」を入力すると、提案が適切に表示されます。

enter image description here

  1. 画面を横向きに回転させるとどうなりますか?全画面モードに切り替わり、キーワードを入力できます。

提案では何が起こりますか?見てみましょう。

enter image description here

奇妙な提案を参照してください?それをどのように解決しますか? CharSequenceを返すconvertToString(Cursor cursor)をオーバーライドする

_ // SuggestionSimpleCursorAdapter.Java
public class SuggestionSimpleCursorAdapter
    extends SimpleCursorAdapter
{

    public SuggestionSimpleCursorAdapter(Context context, int layout, Cursor c,
            String[] from, int[] to) {
        super(context, layout, c, from, to);
    }

    public SuggestionSimpleCursorAdapter(Context context, int layout, Cursor c,
            String[] from, int[] to, int flags) {
        super(context, layout, c, from, to, flags);
    }

    @Override
    public CharSequence convertToString(Cursor cursor) {

        int indexColumnSuggestion = cursor.getColumnIndex(SuggestionsDatabase.FIELD_SUGGESTION);

        return cursor.getString(indexColumnSuggestion);
    }


}
_

convertToString(Cursor cursor)をオーバーライドすると、結果は次のようになります

enter image description here

そして、これがデータベースです

_// SuggestionDatabase.Java
public class SuggestionsDatabase {

  public static final String DB_SUGGESTION = "SUGGESTION_DB";
  public final static String TABLE_SUGGESTION = "SUGGESTION_TB";
  public final static String FIELD_ID = "_id";
  public final static String FIELD_SUGGESTION = "suggestion";

  private SQLiteDatabase db;
  private Helper helper;

  public SuggestionsDatabase(Context context) {

    helper = new Helper(context, DB_SUGGESTION, null, 1);
    db = helper.getWritableDatabase();
  }

  public long insertSuggestion(String text)
  {
    ContentValues values = new ContentValues();
    values.put(FIELD_SUGGESTION, text);
    return db.insert(TABLE_SUGGESTION, null, values);
  }

  public Cursor getSuggestions(String text)
  {
    return db.query(TABLE_SUGGESTION, new String[] {FIELD_ID, FIELD_SUGGESTION}, 
            FIELD_SUGGESTION+" LIKE '"+ text +"%'", null, null, null, null);
  }


    private class Helper extends SQLiteOpenHelper
    {

    public Helper(Context context, String name, CursorFactory factory,
            int version) {
        super(context, name, factory, version);
    }

    @Override
    public void onCreate(SQLiteDatabase db) {
        db.execSQL("CREATE TABLE "+TABLE_SUGGESTION+" ("+
                    FIELD_ID+" integer primary key autoincrement, "+FIELD_SUGGESTION+" text);");
        Log.d("SUGGESTION", "DB CREATED");
    }

    @Override
    public void onUpgrade(SQLiteDatabase db, int oldVersion, int newVersion) {

    }

  }

}
_

この答えが他のプログラマーの多くに役立つことを願っています。 :)

56
user948620

私のニーズはさらに単純化されていました-私が多くの人がArrayListに含まれるように表示したい提案があるので、データベースを必要としませんでした。

次に実装例を示します。

import Java.util.ArrayList;

import Android.app.Activity;
import Android.app.SearchManager;
import Android.content.Context;
import Android.database.Cursor;
import Android.database.MatrixCursor;
import Android.os.Bundle;
import Android.view.LayoutInflater;
import Android.view.Menu;
import Android.view.MenuItem;
import Android.view.View;
import Android.view.View.OnClickListener;
import Android.view.ViewGroup;
import Android.widget.CursorAdapter;
import Android.widget.SearchView;
import Android.widget.SearchView.OnQueryTextListener;
import Android.widget.TextView;
import Android.widget.Toast;

public class ActivityTest extends Activity implements OnQueryTextListener {

    private static final String COLUMN_ID = "_id";
    private static final String COLUMN_TERM = "term";
    private static final String DEFAULT = "default";

    private SearchManager searchManager;
    private SearchView searchView;
    private MenuItem searchMenuItem;
    private SuggestAdapter suggestionsAdapter;
    private final ArrayList<String> suggestionsArray = new ArrayList<String>();
    private final ArrayList<String> dummyArray = new ArrayList<String>();

    @Override
    protected void onCreate(final Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        setContentView(R.layout.activity_main);

        // Create some dummy entries
        dummyArray.add("apples");
        dummyArray.add("oranges");
        dummyArray.add("bananas");
        dummyArray.add("pears");
        dummyArray.add("plums");

    }

    @Override
    public boolean onCreateOptionsMenu(final Menu menu) {
        getMenuInflater().inflate(R.menu.main, menu);

        searchManager = (SearchManager) getSystemService(Context.SEARCH_SERVICE);
        searchMenuItem = menu.findItem(R.id.action_search);

        searchView = (SearchView) searchMenuItem.getActionView();
        searchView.setSearchableInfo(searchManager.getSearchableInfo(getComponentName()));
        searchView.setOnQueryTextListener(this);

        final MatrixCursor matrixCursor = getCursor(suggestionsArray);
        suggestionsAdapter = new SuggestAdapter(this, matrixCursor, suggestionsArray);
        searchView.setSuggestionsAdapter(suggestionsAdapter);
        suggestionsAdapter.notifyDataSetChanged();

        return true;
    }

    @Override
    public boolean onQueryTextChange(final String newText) {

        suggestionsArray.clear();

        for (int i = 0; i < dummyArray.size(); i++) {

            if (dummyArray.get(i).contains(newText)) {
                suggestionsArray.add(dummyArray.get(i));
            }
        }

        final MatrixCursor matrixCursor = getCursor(suggestionsArray);
        suggestionsAdapter = new SuggestAdapter(this, matrixCursor, suggestionsArray);
        searchView.setSuggestionsAdapter(suggestionsAdapter);
        suggestionsAdapter.notifyDataSetChanged();

        return true;
    }

    @Override
    public boolean onQueryTextSubmit(final String query) {
        // TODO Auto-generated method stub
        return false;
    }

    private class SuggestAdapter extends CursorAdapter implements OnClickListener {

        private final ArrayList<String> mObjects;
        private final LayoutInflater mInflater;
        private TextView tvSearchTerm;

        public SuggestAdapter(final Context ctx, final Cursor cursor, final ArrayList<String> mObjects) {
            super(ctx, cursor, 0);

            this.mObjects = mObjects;
            this.mInflater = (LayoutInflater) ctx.getSystemService(Context.LAYOUT_INFLATER_SERVICE);
        }

        @Override
        public View newView(final Context ctx, final Cursor cursor, final ViewGroup parent) {
            final View view = mInflater.inflate(R.layout.list_item_search, parent, false);

            tvSearchTerm = (TextView) view.findViewById(R.id.tvSearchTerm);

            return view;
        }

        @Override
        public void bindView(final View view, final Context ctx, final Cursor cursor) {

            tvSearchTerm = (TextView) view.findViewById(R.id.tvSearchTerm);

            final int position = cursor.getPosition();

            if (cursorInBounds(position)) {

                final String term = mObjects.get(position);
                tvSearchTerm.setText(term);

                view.setTag(position);
                view.setOnClickListener(this);

            } else {
                // Something went wrong
            }
        }

        private boolean cursorInBounds(final int position) {
            return position < mObjects.size();
        }

        @Override
        public void onClick(final View view) {

            final int position = (Integer) view.getTag();

            if (cursorInBounds(position)) {

                final String selected = mObjects.get(position);

                Toast.makeText(getApplicationContext(), selected, Toast.LENGTH_SHORT).show();

                // Do something

            } else {
                // Something went wrong
            }
        }
    }

    private MatrixCursor getCursor(final ArrayList<String> suggestions) {

        final String[] columns = new String[] { COLUMN_ID, COLUMN_TERM };
        final Object[] object = new Object[] { 0, DEFAULT };

        final MatrixCursor matrixCursor = new MatrixCursor(columns);

        for (int i = 0; i < suggestions.size(); i++) {

            object[0] = i;
            object[1] = suggestions.get(i);

            matrixCursor.addRow(object);
        }

        return matrixCursor;
    }
}

実際のコードでは、サーバーからフェッチされた動的な用語をArrayListに入力するカスタムインターフェイスがあります。この方法でデータセットを更新します。

@Override
public void onDataReceived(final ArrayList<String> results) {

    suggestionsArray.clear();
    suggestionsArray.addAll(results);

    final MatrixCursor matrixCursor = getCursor(suggestionsArray);
    suggestionsAdapter = new SuggestAdapter(this, matrixCursor, suggestionsArray);
    searchView.setSuggestionsAdapter(suggestionsAdapter);
    suggestionsAdapter.notifyDataSetChanged();
} 

空のカーソルを初期化しない、または毎回それを再作成しないと問題が発生することがわかりました。

それが役に立てば幸い。

5
brandall

上記のMainActivityクラスの_onCreate method_では、最初にこのコードを使用します

_AutoCompleteTextView search_text = (AutoCompleteTextView) searchView.findViewById(searchView.getContext().getResources().getIdentifier("Android:id/search_src_text", null, null));
search_text.setThreshold(1);
_

このsetThreshold(1)は、1文字からテキストを検索できることを意味します。

4
user2795263

上記のアプローチには、1つの問題があります。

ユーザーが1文字だけ入力すると(例:_"H"_)、DBからエントリを取得し、searchView.setSuggestionsAdapter(<adapter>)を介してアダプタをsearchViewに設定した後、ドロップダウンリストは表示されません。

2番目の文字(_" ", "a"_など)を入力した後にのみ、候補リストが表示されます。他の誰かがこの行動を観察していますか?

0
Devguy