現在、SearchView
内のActionBarcompat
ウィジェットを使用して、検索中にリストをフィルタリングしています。
ユーザーがテキストの入力を開始すると、メインレイアウトのListViewが結果をフィルターするアダプターで更新されます。これを行うには、OnQueryTextListener
を実装し、各キーストロークの結果をフィルター処理します。
代わりに、自動提案リストを生成し、基になるビューを変更しない、Gmailのような検索ボックスを作成します
SearchView
コンポーネントを使用する このチュートリアル を実行しましたが、searchableアクティビティが必要です。専用のアクティビティではなく、(Gmailアプリのように)リストビューがあるMainActivityの上にドロップダウンを配置します。
さらに、チュートリアルと同じ方法で実装することは、私が欲しいもの(単なるドロップダウン)に対してはやり過ぎのようです。
質問で説明されていることを行うコンポーネントだけが必要な場合は、これをお勧めします ライブラリ 。 out-of-the-bx searchable interface を実装することもできますが、UIの制限があることに注意してください。
Gmailアプリと同様のインターフェースを実装するには、次の概念を理解する必要があります。
最終結果は次のようになります:
同じ結果(またはそれ以上)を得るには(多くの)方法がたくさんあります。可能な方法の1つについて説明します。
新しいアクティビティでインターフェイス全体を管理することにしました。そのため、3つのXMLレイアウトを作成しました。
custom_searchable.xml :SearchActivityのコンテンツとして機能する1つのRelativeLayoutにすべてのUI要素をアセンブリします。
<include
Android:id="@+id/cs_header"
layout="@layout/custom_searchable_header_layout" />
<Android.support.v7.widget.RecyclerView
Android:id="@+id/cs_result_list"
Android:layout_width="match_parent"
Android:layout_height="wrap_content"
Android:stackFromBottom="true"
Android:transcriptMode="normal"/>
custom_searchable_header_layout.xml :ユーザーがクエリを入力する検索バーを保持します。また、mic、erase、およびbtnも含まれます。
<RelativeLayout
Android:id="@+id/custombar_return_wrapper"
Android:layout_width="55dp"
Android:layout_height="fill_parent"
Android:gravity="center_vertical"
Android:background="@drawable/right_oval_ripple"
Android:focusable="true"
Android:clickable="true" >
<ImageView
Android:id="@+id/custombar_return"
Android:layout_width="wrap_content"
Android:layout_height="wrap_content"
Android:layout_centerVertical="true"
Android:layout_centerHorizontal="true"
Android:background="#00000000"
Android:src="@drawable/arrow_left_icon"/>
</RelativeLayout>
<Android.support.design.widget.TextInputLayout
Android:layout_width="match_parent"
Android:layout_height="match_parent"
Android:layout_toRightOf="@+id/custombar_return_wrapper"
Android:layout_marginRight="60dp"
Android:layout_marginLeft="10dp"
Android:layout_marginTop="10dp"
Android:layout_marginBottom="10dp">
<EditText
Android:id="@+id/custombar_text"
Android:layout_width="match_parent"
Android:layout_height="match_parent"
Android:hint="Search..."
Android:textColor="@color/textPrimaryColor"
Android:singleLine="true"
Android:imeOptions="actionSearch"
Android:background="#00000000">
<requestFocus/>
</EditText>
</Android.support.design.widget.TextInputLayout>
<RelativeLayout
Android:id="@+id/custombar_mic_wrapper"
Android:layout_width="55dp"
Android:layout_height="fill_parent"
Android:layout_alignParentRight="true"
Android:gravity="center_vertical"
Android:background="@drawable/left_oval_ripple"
Android:focusable="true"
Android:clickable="true" >
<ImageView
Android:id="@+id/custombar_mic"
Android:layout_width="wrap_content"
Android:layout_height="wrap_content"
Android:layout_centerVertical="true"
Android:layout_centerHorizontal="true"
Android:background="#00000000"
Android:src="@drawable/mic_icon"/>
</RelativeLayout>
custom_searchable_row_details.xml :ユーザークエリに応答して表示される結果リストに表示されるUI要素を保持します。
<ImageView
Android:id="@+id/rd_left_icon"
Android:layout_width="wrap_content"
Android:layout_height="wrap_content"
Android:layout_marginTop="3dp"
Android:layout_centerVertical="true"
Android:layout_marginLeft="5dp"
Android:src="@drawable/clock_icon" />
<LinearLayout
Android:id="@+id/rd_wrapper"
Android:orientation="vertical"
Android:layout_width="match_parent"
Android:layout_height="match_parent"
Android:gravity="center_vertical"
Android:layout_toRightOf="@+id/rd_left_icon"
Android:layout_marginLeft="20dp"
Android:layout_marginRight="50dp">
<TextView
Android:id="@+id/rd_header_text"
Android:layout_width="wrap_content"
Android:layout_height="wrap_content"
Android:textColor="@color/textPrimaryColor"
Android:text="Header"
Android:textSize="16dp"
Android:textStyle="bold"
Android:maxLines="1"/>
<TextView
Android:id="@+id/rd_sub_header_text"
Android:layout_width="match_parent"
Android:layout_height="wrap_content"
Android:textColor="@color/textPrimaryColor"
Android:text="Sub Header"
Android:textSize="14dp"
Android:maxLines="1" />
</LinearLayout>
<ImageView
Android:id="@+id/rd_right_icon"
Android:layout_width="wrap_content"
Android:layout_height="wrap_content"
Android:layout_marginTop="3dp"
Android:layout_alignParentRight="true"
Android:layout_centerVertical="true"
Android:src="@drawable/arrow_left_up_icon"/>
これは、ユーザーが検索ボタン(任意の場所に配置できる)を入力すると、この SearchActivity が呼び出されるという考え方です。それにはいくつかの主な責任があります:
custom_searchable_header_layout.xml のUI要素にバインドします。これにより、次のことが可能になります。
editTextのリスナーを提供するには(ユーザーがクエリを入力する場所):
TextView.OnEditorActionListener searchListener = new TextView.OnEditorActionListener() {
public boolean onEditorAction(TextView exampleView, int actionId, KeyEvent event) {
// do processing
}
}
searchInput.setOnEditorActionListener(searchListener);
searchInput.addTextChangedListener(new TextWatcher() {
public void onTextChanged(final CharSequence s, int start, int before, int count) {
// Do processing
}
}
戻るボタンのリスナーを追加します(順番に単にfinish()を呼び出し、呼び出し元のアクティビティに戻ります):
this.dismissDialog.setOnClickListener(new View.OnClickListener() {
public void onClick(View v) {
finish();
}
google speech-to-text APIのインテントを呼び出します。
private void implementVoiceInputListener () {
this.voiceInput.setOnClickListener(new View.OnClickListener() {
public void onClick(View v) {
if (micIcon.isSelected()) {
searchInput.setText("");
query = "";
micIcon.setSelected(Boolean.FALSE);
micIcon.setImageResource(R.drawable.mic_icon);
} else {
Intent intent = new Intent(RecognizerIntent.ACTION_RECOGNIZE_SPEECH);
intent.putExtra(RecognizerIntent.EXTRA_LANGUAGE_MODEL, RecognizerIntent.LANGUAGE_MODEL_FREE_FORM);
intent.putExtra(RecognizerIntent.EXTRA_Prompt, "Speak now");
SearchActivity.this.startActivityForResult(intent, VOICE_RECOGNITION_CODE);
}
}
});
}
検索インターフェースを構築する場合、開発者には通常2つのオプションがあります。
どちらの場合も、回答は、結果リストに表示されたときにコンテンツが表示されるCursorオブジェクトとして返されます。このプロセス全体は、コンテンツプロバイダーAPIを使用して実装できます。コンテンツプロバイダーの使用方法の詳細については、この link を参照してください。
開発者が1.で説明した動作を実装したい場合は、SearchRecentSuggestionsProviderクラスを拡張する戦略を使用すると便利です。それを行う方法の詳細は、この link で到達できます。
このインターフェースは、次の動作を提供します。
ユーザーが文字を入力すると、取得したコンテンツプロバイダークラスのクエリメソッドを呼び出すと、塗りつぶされたカーソルが返され、リストに表示される候補が表示されます。UIスレッドがフリーズしないようにする必要があるため、これを実行することをお勧めしますAsyncTaskで検索:
public void onTextChanged(final CharSequence s, int start, int before, int count) {
if (!"".equals(searchInput.getText().toString())) {
query = searchInput.getText().toString();
setClearTextIcon();
if (isRecentSuggestionsProvider) {
// Provider is descendant of SearchRecentSuggestionsProvider
mapResultsFromRecentProviderToList(); // query is performed in this method
} else {
// Provider is custom and shall follow the contract
mapResultsFromCustomProviderToList(); // query is performed in this method
}
} else {
setMicIcon();
}
}
AsyncTaskのonPostExecute()メソッド内で、ResultListに表示される結果を含むリスト(doInBackground()メソッドからのリスト)を取得する必要があります(POJOクラスにマッピングしてカスタムに渡すことができます)アダプタ、またはこのタスクに最適なCursorAdapterを使用できます)。
protected void onPostExecute(List resultList) {
SearchAdapter adapter = new SearchAdapter(resultList);
searchResultList.setAdapter(adapter);
}
protected List doInBackground(Void[] params) {
Cursor results = results = queryCustomSuggestionProvider();
List<ResultItem> resultList = new ArrayList<>();
Integer headerIdx = results.getColumnIndex(SearchManager.SUGGEST_COLUMN_TEXT_1);
Integer subHeaderIdx = results.getColumnIndex(SearchManager.SUGGEST_COLUMN_TEXT_2);
Integer leftIconIdx = results.getColumnIndex(SearchManager.SUGGEST_COLUMN_ICON_1);
Integer rightIconIdx = results.getColumnIndex(SearchManager.SUGGEST_COLUMN_ICON_2);
while (results.moveToNext()) {
String header = results.getString(headerIdx);
String subHeader = (subHeaderIdx == -1) ? null : results.getString(subHeaderIdx);
Integer leftIcon = (leftIconIdx == -1) ? 0 : results.getInt(leftIconIdx);
Integer rightIcon = (rightIconIdx == -1) ? 0 : results.getInt(rightIconIdx);
ResultItem aux = new ResultItem(header, subHeader, leftIcon, rightIcon);
resultList.add(aux);
}
results.close();
return resultList;
ユーザーがソフトキーボードの検索ボタンに触れたときを特定します。その際、検索可能なアクティビティ(検索結果の処理を担当するアクティビティ)にインテントを送信し、インテントに追加情報としてクエリを追加します
protected void onActivityResult(int requestCode, int resultCode, Intent data) {
super.onActivityResult(requestCode, resultCode, data);
switch (requestCode) {
case VOICE_RECOGNITION_CODE: {
if (resultCode == RESULT_OK && null != data) {
ArrayList<String> text = data.getStringArrayListExtra(RecognizerIntent.EXTRA_RESULTS);
searchInput.setText(text.get(0));
}
break;
}
}
}
ユーザーが表示された提案の1つをクリックし、アイテム情報を含むインテントを送信するタイミングを特定します(このインテントは前のステップのインテントとは異なる必要があります)
private void sendSuggestionIntent(ResultItem item) {
try {
Intent sendIntent = new Intent(this, Class.forName(searchableActivity));
sendIntent.setAction(Intent.ACTION_VIEW);
sendIntent.setFlags(Intent.FLAG_ACTIVITY_CLEAR_TOP | Intent.FLAG_ACTIVITY_SINGLE_TOP);
Bundle b = new Bundle();
b.putParcelable(CustomSearchableConstants.CLICKED_RESULT_ITEM, item);
sendIntent.putExtras(b);
startActivity(sendIntent);
finish();
} catch (ClassNotFoundException e) {
e.printStackTrace();
}
}
記述されたステップは、あなた自身のインターフェースを実装するのに十分でなければなりません。すべてのコード例は here から取得されました。上記で説明したことのほとんどを行うライブラリを作成しました。まだ十分にテストされておらず、UI構成の一部はまだ使用できない場合があります。
この答えが困っている人の助けになることを願っています。
それを行うための小さなチュートリアルを設定しました
http://drzon.net/how-to-create-a-clearable-autocomplete-dropdown-with-autocompletetextview/
概要
提案どおり、SearchView
をAutoCompleteTextView
に置き換える必要がありました。
まず、アダプターを作成します。私の場合、それはJSONObject
ArrayAdapterでした。ドロップダウンに表示したいデータは、会場名と会場アドレスでした。アダプターはFiltarable
でなければならず、getFilter()
をオーバーライドする必要があることに注意してください。
// adapter for the search dropdown auto suggest
ArrayAdapter<JSONObject> searchAdapter = new ArrayAdapter<JSONObject>(this, Android.R.id.text1) {
private Filter filter;
public View getView(final int position, View convertView, ViewGroup parent) {
if (convertView == null) {
convertView = this.getLayoutInflater().inflate(R.layout.search_item, parent, false);
}
TextView venueName = (TextView) convertView.findViewById(R.id.search_item_venue_name);
TextView venueAddress = (TextView) convertView.findViewById(R.id.search_item_venue_address);
final JSONObject venue = this.getItem(position);
convertView.setTag(venue);
try {
CharSequence name = highlightText(venue.getString("name"));
CharSequence address = highlightText(venue.getString("address"));
venueName.setText(name);
venueAddress.setText(address);
}
catch (JSONException e) {
Log.i(Consts.TAG, e.getMessage());
}
return convertView;
}
@Override
public Filter getFilter() {
if (filter == null) {
filter = new VenueFilter();
}
return filter;
}
};
これがカスタムVenueFilter
です:
private class VenueFilter extends Filter {
@Override
protected FilterResults performFiltering(CharSequence constraint) {
List<JSONObject> list = new ArrayList<JSONObject>(venues);
FilterResults result = new FilterResults();
String substr = constraint.toString().toLowerCase();
if (substr == null || substr.length() == 0) {
result.values = list;
result.count = list.size();
} else {
final ArrayList<JSONObject> retList = new ArrayList<JSONObject>();
for (JSONObject venue : list) {
try {
if (venue.getString("name").toLowerCase().contains(constraint) || venue.getString("address").toLowerCase().contains(constraint) ||
{
retList.add(venue);
}
} catch (JSONException e) {
Log.i(Consts.TAG, e.getMessage());
}
}
result.values = retList;
result.count = retList.size();
}
return result;
}
@SuppressWarnings("unchecked")
@Override
protected void publishResults(CharSequence constraint, FilterResults results) {
searchAdapter.clear();
if (results.count > 0) {
for (JSONObject o : (ArrayList<JSONObject>) results.values) {
searchAdapter.add(o);
}
}
}
}
次に、検索ボックスのレイアウトを設定します(actionbar_search.xml
):
<?xml version="1.0" encoding="utf-8"?>
<RelativeLayout xmlns:Android="http://schemas.Android.com/apk/res/Android"
Android:layout_width="fill_parent"
Android:layout_height="match_parent"
Android:layout_gravity="fill_horizontal"
Android:focusable="true" >
<AutoCompleteTextView
Android:id="@+id/search_box"
Android:layout_width="fill_parent"
Android:layout_height="wrap_content"
Android:layout_gravity="center"
Android:dropDownVerticalOffset="5dp"
Android:dropDownWidth="wrap_content"
Android:inputType="textAutoComplete|textAutoCorrect"
Android:popupBackground="@color/white"
Android:textColor="#FFFFFF" >
</AutoCompleteTextView>
</RelativeLayout>
また、個々のドロップダウンアイテムのレイアウト(会場名と会場の住所)。これは見た目が悪いので、カスタマイズする必要があります:
<?xml version="1.0" encoding="UTF-8"?>
<RelativeLayout xmlns:Android="http://schemas.Android.com/apk/res/Android"
Android:layout_width="match_parent"
Android:layout_height="match_parent"
Android:textAlignment="gravity" >
<TextView
Android:id="@+id/search_item_venue_name"
Android:layout_width="wrap_content"
Android:layout_height="wrap_content"
Android:textColor="@color/cyan"
Android:layout_gravity="right" />
<TextView
Android:id="@+id/search_item_venue_address"
Android:layout_width="match_parent"
Android:layout_height="wrap_content"
Android:layout_toStartOf="@+id/search_item_venue_name"
Android:gravity="right"
Android:textColor="@color/white" />
</RelativeLayout>
次に、アクションバーに配置します
@Override
public void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
ActionBar actionBar = getSupportActionBar();
actionBar.setDisplayOptions(ActionBar.DISPLAY_SHOW_CUSTOM | ActionBar.DISPLAY_USE_LOGO | ActionBar.DISPLAY_SHOW_HOME
| ActionBar.DISPLAY_HOME_AS_UP);
LayoutInflater inflater = (LayoutInflater)this.getSystemService(Context.LAYOUT_INFLATER_SERVICE);
View v = inflater.inflate(R.layout.actionbar_search, null);
AutoCompleteTextView textView = (AutoCompleteTextView) v.findViewById(R.id.search_box);
textView.setAdapter(searchAdapter);
textView.setOnClickListener(new View.OnClickListener() {
@Override
public void onClick(View v) {
// do something when the user clicks
}
});
actionBar.setCustomView(v);
}
それはそれについてです、私はまだ理解することがいくつかあります:
SearchView
ウィジェット-クリックすると検索ボックスが開く拡大鏡(およびX
ボタンを押して閉じ、通常に戻ります)全体的に、これはsearchableアクティビティを作成するオーバーヘッドをすべて節約します。カスタマイズ方法等ご存知の方は追加してください。
ListPopupWindow を使用して、検索ビューウィジェットにアンカーする必要があります。
ドロップダウン効果のみを実装したい場合は、 AutoCompleteTextView
そして、あなたは素敵なチュートリアルを見つけることができます ここ
次に、正確な設計を実装する場合は ActionBar を実装する必要があります。下位バージョンに実装する場合は、ActionBarの代わりに ActionBarCombat を実装する必要があります。
私はMichaelsの返信( https://stackoverflow.com/a/18894726/24080 )をうまく使用しましたが、手動で操作するのが好きではありませんでした。ビューを膨らませてアクションバーに追加しました。状態の切り替えなど.
ビューを手動でactionbar/toolbarに追加する代わりに、ActionBar ActionViewを使用するように変更しました。
追加されたリンクのtoggleSearchメソッドの例で行ったように、ビューの開閉状態と非表示を管理する必要がないため、これがはるかにうまく機能することがわかりました。また、戻るボタンと完全に連携します。
私のmenu.xmlで
<item
Android:id="@+id/global_search"
Android:icon="@Android:drawable/ic_menu_search"
Android:title="Search"
app:actionLayout="@layout/actionbar_search"
app:showAsAction="ifRoom|collapseActionView" />
私のonCreateOptionsMenuで
View actionView = menu.findItem(R.id.global_search).getActionView();
searchTextView = (ClearableAutoCompleteTextView) actionView.findViewById(R.id.search_box);
searchTextView.setAdapter(searchAdapter);
あなたは私のプロジェクトで実装の完全に機能するバージョンを見つけることができます。実際のSearchViewを使用してlistViewをフィルター処理したため、2つの検索ビューがあることに注意してください。
あなたが要求したものを正確に実装するこの例を参照してください: http://wptrafficanalyzer.in/blog/Android-searchview-widget-with-actionbarcompat-library/