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
Android: Providing recent search suggestions without searchable activity?
提问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 个类,如下所示
MainActivity
- for test ofSearchView
suggestion from databaseSuggestionDatabase
- this will store your recent search keyword.SuggestionSimpleCursorAdapter
- this is a subclass ofSimpleCursorAdapter
. I'll explain why I make this class instead of usingSimpleCursorAdapter
.
MainActivity
- 用于测试SearchView
来自数据库的建议SuggestionDatabase
- 这将存储您最近的搜索关键字。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
怎么运行的
- 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" - If the user writes a string for example "Hel" or "H" in
SearchView
theonQueryTextChange()
will be called and then we search this keyword inSQLiteDatabase
(SuggestionDatabase
). If "Hel" or "H" matches "Hello" , display the results of query by setting the returned Cursor inSuggestionSimpleCursorAdapter
and then set this adapter inSearchView
. Here's the picture.
- 当用户点击搜索按钮时,
onQueryTextSubmit()
将被触发,然后搜索关键字将保存在我们的数据库中。假设我们提交了一个关键字“你好” - 如果用户在例如将一个字符串“赫尔”或“H”
SearchView
的onQueryTextChange()
将被调用,然后我们在搜索这个关键字SQLiteDatabase
(SuggestionDatabase
)。如果 "Hel" 或 "H" 与 "Hello" 匹配,则通过设置返回的 Cursor 来显示查询结果,SuggestionSimpleCursorAdapter
然后在 中设置此适配器SearchView
。这是图片。
3. Ofcourse we will tap the suggestion which is "Hello", onSuggestionClick(int position)
will be called for that. We get the SQLiteCursor
object from the SearchView
's adapter (SuggestionSimpleCursorAdapter
) and get the Suggestion text from it, set the suggestion text in SearchView
object
3. 当然,我们会点击“你好”的建议,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);
If we use SimpleCursorAdapter
it also works properly but let's say we have this scenario
如果我们使用SimpleCursorAdapter
它也可以正常工作,但假设我们有这种情况
- If We run this program in smartphone and type the keyword "Hel", the suggestion will appear properly.
- 如果我们在智能手机上运行这个程序并输入关键字“Hel”,建议就会正确显示。
- What if We rotate the screen in landscape? It will switch in Full Screen mode and you can still type the keyword.
- 如果我们横向旋转屏幕怎么办?它将在全屏模式下切换,您仍然可以键入关键字。
What will happen in suggestion? Let's take a look.
建议中会发生什么?让我们来看看。
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)
,这是结果
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 MainActivity
class from above in onCreate method
use 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"
)后,才会显示建议列表。还有其他人观察这种行为吗?