build.gradle:
dependencies {
compile 'com.android.support:appcompat-v7:23.3.0'
compile 'com.jakewharton.rxbinding:rxbinding-appcompat-v7:0.4.0'
}
menu/menu.xml:
<menu xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:app="http://schemas.android.com/apk/res-auto">
<item android:id="@+id/action_search" android:title="Search"
android:icon="@android:drawable/ic_menu_search"
app:actionViewClass="android.support.v7.widget.SearchView"
app:showAsAction="always"/>
</menu>
MainActivity.java:
@Override
public boolean onCreateOptionsMenu(Menu menu) {
MenuInflater inflater = getMenuInflater();
inflater.inflate(R.menu.menu, menu);
MenuItem searchMenuItem = menu.findItem(R.id.action_search);
setupSearchView(searchMenuItem );
return true;
}
private void setupSearchView(MenuItem searchMenuItem) {
SearchView searchView = (SearchView) searchMenuItem.getActionView();
searchView.setQueryHint(getString(R.string.search_hint)); // your hint here
SearchAdapter searchAdapter = new SearchAdapter(this);
searchView.setSuggestionsAdapter(searchAdapter);
// optional: set the letters count after which the search will begin to 1
// the default is 2
try {
int autoCompleteTextViewID = getResources().getIdentifier("android:id/search_src_text", null, null);
AutoCompleteTextView searchAutoCompleteTextView = (AutoCompleteTextView) searchView.findViewById(autoCompleteTextViewID);
searchAutoCompleteTextView.setThreshold(1);
} catch (Exception e) {
Logs.e(TAG, "failed to set search view letters threshold");
}
searchView.setOnSearchClickListener(v -> {
// optional actions to search view expand
});
searchView.setOnCloseListener(() -> {
// optional actions to search view close
return false;
});
RxSearchView.queryTextChanges(searchView)
.doOnEach(notification -> {
CharSequence query = (CharSequence) notification.getValue();
searchAdapter.filter(query);
})
.debounce(300, TimeUnit.MILLISECONDS) // to skip intermediate letters
.flatMap(query -> MyWebService.search(query)) // make a search request
.retry(3)
.subscribe(results -> {
searchAdapter.populateAdapter(results);
});
//optional: collapse the searchView on close
searchView.setOnQueryTextFocusChangeListener((view, queryTextFocused) -> {
if (!queryTextFocused) {
collapseSearchView();
}
});
}
SearchAdapter.java
public class SearchAdapter extends CursorAdapter {
private List<SearchResult> items = Collections.emptyList();
public SearchAdapter(Activity activity) {
super(activity, null, CursorAdapter.FLAG_REGISTER_CONTENT_OBSERVER);
}
public void populateAdapter(List<SearchResult> items) {
this.items = items;
final MatrixCursor c = new MatrixCursor(new String[]{BaseColumns._ID});
for (int i = 0; i < items.size(); i++) {
c.addRow(new Object[]{i});
}
changeCursor(c);
notifyDataSetChanged();
}
public void filter(CharSequence query) {
final MatrixCursor c = new MatrixCursor(new String[]{BaseColumns._ID});
for (int i = 0; i < items.size(); i++) {
SearchResult result = items.get(i);
if (result.getText().startsWith(query.toString())) {
c.addRow(new Object[]{i});
}
}
changeCursor(c);
notifyDataSetChanged();
}
@Override
public void bindView(View view, Context context, Cursor cursor) {
ViewHolder holder = (ViewHolder) view.getTag();
int position = cursor.getPosition();
if (position < items.size()) {
SearchResult result = items.get(position);
// bind your view here
}
}
@Override
public View newView(Context context, Cursor cursor, ViewGroup parent) {
LayoutInflater inflater = (LayoutInflater) context
.getSystemService(Context.LAYOUT_INFLATER_SERVICE);
View v = inflater.inflate(R.layout.search_list_item, parent, false);
ViewHolder holder = new ViewHolder(v);
v.setTag(holder);
return v;
}
private static class ViewHolder {
public final TextView text;
public ViewHolder(View v) {
this.text= (TextView) v.findViewById(R.id.text);
}
}
}