java Android:在没有可搜索活动的情况下提供最近的搜索建议?

声明:本页面是StackOverFlow热门问题的中英对照翻译,遵循CC BY-SA 4.0协议,如果您需要使用它,必须同样遵循CC BY-SA许可,注明原文地址和作者信息,同时你必须将它归于原作者(不是我):StackOverFlow 原文地址: http://stackoverflow.com/questions/13733460/
Warning: these are provided under cc-by-sa 4.0 license. You are free to use/share it, But you must attribute it to the original authors (not me): StackOverFlow

提示:将鼠标放在中文语句上可以显示对应的英文。显示中英文
时间:2020-10-31 13:48:57  来源:igfitidea点击:

Android: Providing recent search suggestions without searchable activity?

javaandroidsearchview

提问by Matthew Mitchell

I have an ActionBar SearchView and I am successfully able to make searches with it. The android documentation does not explain how to implement search suggestions. I do not want to have a searchable activity.

我有一个 ActionBar SearchView 并且我能够成功地使用它进行搜索。android 文档没有解释如何实现搜索建议。我不想有可搜索的活动。

This is my search code:

这是我的搜索代码:

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;
            }
        });

This works for searching but I have no idea to implement recent search query suggestions. How do I go about doing this?

这适用于搜索,但我不知道实施最近的搜索查询建议。我该怎么做?

Thank you.

谢谢你。

回答by

Ok, I spent my time for this. I make my own simplesuggestion implementation from SQLiteDatabase.

好的,我花了我的时间。让我自己简单的建议执行SQLiteDatabase

We will create 3 classes like the following

我们将创建 3 个类,如下所示

  1. MainActivity- for test of SearchViewsuggestion from database
  2. SuggestionDatabase- this will store your recent search keyword.
  3. SuggestionSimpleCursorAdapter- this is a subclass of SimpleCursorAdapter. I'll explain why I make this class instead of using SimpleCursorAdapter.
  1. MainActivity- 用于测试SearchView来自数据库的建议
  2. SuggestionDatabase- 这将存储您最近的搜索关键字。
  3. SuggestionSimpleCursorAdapter- 这是SimpleCursorAdapter. 我将解释为什么我创建这个类而不是使用SimpleCursorAdapter.

The codes

代码

// 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;
        }
    }

}

How it works

怎么运行的

  1. When user taps the search button, the onQueryTextSubmit()will be triggered and then the search keyword will be saved in our database. Let's say we submit a keyword "Hello"
  2. If the user writes a string for example "Hel" or "H" in SearchViewthe onQueryTextChange()will be called and then we search this keyword in SQLiteDatabase(SuggestionDatabase). If "Hel" or "H" matches "Hello" , display the results of query by setting the returned Cursor in SuggestionSimpleCursorAdapterand then set this adapter in SearchView. Here's the picture.
  1. 当用户点击搜索按钮时,onQueryTextSubmit()将被触发,然后搜索关键字将保存在我们的数据库中。假设我们提交了一个关键字“你好”
  2. 如果用户在例如将一个字符串“赫尔”或“H”SearchViewonQueryTextChange()将被调用,然后我们在搜索这个关键字SQLiteDatabaseSuggestionDatabase)。如果 "Hel" 或 "H" 与 "Hello" 匹配,则通过设置返回的 Cursor 来显示查询结果,SuggestionSimpleCursorAdapter然后在 中设置此适配器SearchView。这是图片。

enter image description here
3. Ofcourse we will tap the suggestion which is "Hello", onSuggestionClick(int position)will be called for that. We get the SQLiteCursorobject from the SearchView's adapter (SuggestionSimpleCursorAdapter) and get the Suggestion text from it, set the suggestion text in SearchViewobject

在此处输入图片说明
3. 当然,我们会点击“你好”的建议,onSuggestionClick(int position)会被调用。我们SQLiteCursorSearchView适配器 ( SuggestionSimpleCursorAdapter) 中获取对象并从中获取建议文本,在SearchView对象中设置建议文本

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

If we use SimpleCursorAdapterit also works properly but let's say we have this scenario

如果我们使用SimpleCursorAdapter它也可以正常工作,但假设我们有这种情况

  1. If We run this program in smartphone and type the keyword "Hel", the suggestion will appear properly.
  1. 如果我们在智能手机上运行这个程序并输入关键字“Hel”,建议就会正确显示。

enter image description here

在此处输入图片说明

  1. What if We rotate the screen in landscape? It will switch in Full Screen mode and you can still type the keyword.
  1. 如果我们横向旋转屏幕怎么办?它将在全屏模式下切换,您仍然可以键入关键字。

What will happen in suggestion? Let's take a look.

建议中会发生什么?让我们来看看。

enter image description here

在此处输入图片说明

See the weird suggestion? How we solve that? By overriding the convertToString(Cursor cursor)which returns a CharSequence

看到奇怪的建议了吗?我们如何解决?通过覆盖convertToString(Cursor cursor)返回一个CharSequence

 // 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);
    }


}

By overriding convertToString(Cursor cursor), here's the result

通过覆盖convertToString(Cursor cursor),这是结果

enter image description here

在此处输入图片说明

And here's the database

这是数据库

// 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) {

    }

  }

}

I hope this answer is useful to other programmers alot. :)

我希望这个答案对其他程序员很有用。:)

回答by brandall

My need was even more simplistic - I didn't require a database, as I had the suggestions I many want to show contained in an ArrayList.

我的需求甚至更简单——我不需要数据库,因为我有很多想要显示的建议包含在 ArrayList 中。

Here's an example implementation:

这是一个示例实现:

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;
    }
}

In my actual code, I have a custom Interface which populates the ArrayList with dynamic terms fetched from a Server. You would update the dataset in this way:

在我的实际代码中,我有一个自定义接口,它使用从服务器获取的动态术语填充 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();
} 

I found that not initialising an empty cursor or re-creating it every time caused issues.

我发现每次不初始化空游标或重新创建它都会导致问题。

Hope it helps.

希望能帮助到你。

回答by user2795263

In the MainActivityclass from above in onCreate methoduse this code first

MainActivity上面的类中首先onCreate method使用此代码

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

This setThreshold(1)means it can search the text from one character as well now.

setThreshold(1)意味着它现在也可以从一个字符搜索文本。

回答by Devguy

There is one issue i notice in the above approach.

我在上述方法中注意到一个问题。

When the user enters just one character (eg "H"), after getting the entries from the DB and setting the adapter to the searchView via searchView.setSuggestionsAdapter(<adapter>), the drop down list is not shown.

当用户仅输入一个字符(例如"H")时,在从 DB 获取条目并将适配器设置为 searchView via 后searchView.setSuggestionsAdapter(<adapter>),不会显示下拉列表。

Only after entering a second character (eg " ", "a"), the suggestion list gets displayed. Is anybody else observing this behavior ?

只有在输入第二个字符(例如" ", "a")后,才会显示建议列表。还有其他人观察这种行为吗?