我目前正在SearchView
内使用ActionBarcompat
小部件来搜索时过滤列表。
当用户开始输入文本时,主布局中的ListView会更新并使用适配器来过滤结果。我这样做是通过实现OnQueryTextListener
并过滤每个键击的结果。
相反,我想创建一个类似Gmail的搜索框,其中包含生成的自动推荐列表,并且没有对基础视图进行任何更改
我已使用this tutorial使用SearchView
组件,但需要可搜索的活动。我希望下拉菜单位于MainActivity上,我有ListView(就像在Gmail应用程序中一样),而不是专用的活动。
此外,以与教程相同的方式实现它似乎对我想要的东西(只是一个下拉列表)来说太过分了
答案 0 :(得分:26)
如果您只想要一个能够解决问题所述内容的组件,我建议library。您也可以实现out-of-the-bx searchable interface,但请注意它确实有UI限制:
要实现类似于Gmail应用的界面,您必须了解以下内容:
最终结果应如下所示:
有很多(很多)方法可以得到相同的结果(或更好),我会描述一种可能的方式。
我决定在新的Activity中管理整个界面,因为我已经创建了三个XML布局:
custom_searchable.xml:汇集一个RelativeLayout中的所有UI元素,用作SearchActivity的内容;
<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:保存用户将在其中键入查询的搜索栏。它还包含麦克风,擦除和返回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();
}
调用谷歌语音转文本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);
}
}
});
}
在构建搜索界面时,开发人员有两个选择:
在这两种情况下,答案都应作为Cursor对象传回,该对象的内容在结果列表中显示为itens。可以使用Content Provider API实现整个过程。有关如何使用内容提供商的详细信息,请参阅此link。
如果开发人员想要实现1.中描述的行为,那么使用exventing 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()方法中,您应该检索一个列表(应该来自doInBackground()方法),其中包含要在ResultList中显示的结果(您可以将它映射到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;
}
}
}
确定用户点击其中一个显示的建议并发送和包含项目信息的意图(此意图应与上一步骤不同)
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配置可能还没有。
我希望这个答案可以帮助有需要的人。
答案 1 :(得分:5)
我已经设置了一个小教程
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
小部件一样 - 放大镜玻璃,当您点击它时会打开一个搜索框(并且有一点{ {1}}按钮关闭它并恢复正常)总体而言,这可以节省创建可搜索活动的所有开销。如果您知道如何自定义等,请添加。
答案 2 :(得分:2)
您应该使用ListPopupWindow并将其锚定到搜索视图小部件。
答案 3 :(得分:2)
如果您只想实施下拉效果请转到AutoCompleteTextView
你可以找到一个很好的教程here
然后,如果要实现精确设计,则必须实施ActionBar 如果你想实现更低版本,你需要实现ActionBarCombat 而不是ActionBar
答案 4 :(得分:2)
我已经成功使用了Michaels的回复(https://stackoverflow.com/a/18894726/2408033),但我不喜欢它的手册,夸大视图并将其添加到操作栏,切换其状态等。
我将其修改为使用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。
答案 5 :(得分:1)
请参阅此示例,该示例完全符合您的要求: http://wptrafficanalyzer.in/blog/android-searchview-widget-with-actionbarcompat-library/
答案 6 :(得分:-1)
您需要使用collapseActionView
属性。
collapseActionView属性允许您的SearchView扩展为 占据整个动作栏并折叠回正常状态 不使用时的操作栏项目。
例如:
<?xml version="1.0" encoding="utf-8"?>
<menu xmlns:android="http://schemas.android.com/apk/res/android">
<item android:id="@+id/search"
android:title="@string/search_title"
android:icon="@drawable/ic_search"
android:showAsAction="collapseActionView|ifRoom"
android:actionViewClass="android.widget.SearchView" />
</menu>