Android动作栏SearchView如何实现自动完成功能?

43

我正在使用位于操作栏中的SearchView。我希望使用自动完成功能从数据库获取搜索结果。

这是否可能?还是我需要使用自定义文本框,然后添加自动完成功能?

7个回答

42

我刚刚为v7版本做了这个事情,但惊讶地发现我不能简单地使用ArrayAdapter设置适配器。

我不想使用默认的AutoCompleteTextView(正如这里的顶级评论者所做的那样),因为那样你就会错过SearchView的许多炫酷功能,比如小的搜索图标和x按钮。

所以我扩展了SearchView并得到了以下结果:

public class ArrayAdapterSearchView extends SearchView {

private SearchView.SearchAutoComplete mSearchAutoComplete;

public ArrayAdapterSearchView(Context context) {
    super(context);
    initialize();
}

public ArrayAdapterSearchView(Context context, AttributeSet attrs) {
    super(context, attrs);
    initialize();
}

public void initialize() {
    mSearchAutoComplete = (SearchAutoComplete) findViewById(android.support.v7.appcompat.R.id.search_src_text);
    this.setAdapter(null);
    this.setOnItemClickListener(null);
}

@Override
public void setSuggestionsAdapter(CursorAdapter adapter) {
    // don't let anyone touch this
}

public void setOnItemClickListener(OnItemClickListener listener) {
    mSearchAutoComplete.setOnItemClickListener(listener);
}

public void setAdapter(ArrayAdapter<?> adapter) {
    mSearchAutoComplete.setAdapter(adapter);
}

public void setText(String text) {
    mSearchAutoComplete.setText(text);
}

}

您可以在 ActionBar 的菜单 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:icon="@android:drawable/ic_menu_add"
    android:title="TITLE"
    app:actionViewClass="com.yourpackage.ArrayAdapterSearchView"
    app:showAsAction="ifRoom|collapseActionView"/>

</menu>

您可能还希望将点击功能添加到自动完成列表中(例如,将文本设置为EditText):
MenuItem searchItem = menu.findItem(R.id.action_search);
final ArrayAdapterSearchView searchView = (ArrayAdapterSearchView)searchItem.getActionView();
searchView.setOnItemClickListener(new OnItemClickListener() {
    @Override
    public void onItemClick(AdapterView<?> parent, View view, int position, long id) {

        searchView.setText(adapter.getItem(position).toString());

    }
});

以下是适用于常规的 android.widget.SearchView 的类似版本:

public class ArrayAdapterSearchView extends SearchView {

private AutoCompleteTextView mSearchAutoComplete;

public ArrayAdapterSearchView(Context context) {
    super(context);
    initialize();
}

public ArrayAdapterSearchView(Context context, AttributeSet attrs) {
    super(context, attrs);
    initialize();
}

public void initialize() {
    mSearchAutoComplete = (AutoCompleteTextView) findViewById(getResources().getIdentifier("android:id/search_src_text", null, null));
    setAutoCompleSuggestionsAdapter(null);
    setOnItemClickListener(null);
}

@Override
public void setSuggestionsAdapter(CursorAdapter adapter) {
    throw new UnsupportedOperationException("Please use setAutoCompleSuggestionsAdapter(ArrayAdapter<?> adapter) instead");
}

public void setOnItemClickListener(AdapterView.OnItemClickListener listener) {
    mSearchAutoComplete.setOnItemClickListener(listener);
}

public void setAutoCompleSuggestionsAdapter(ArrayAdapter<?> adapter) {
    mSearchAutoComplete.setAdapter(adapter);
}

public void setText(String text) {
    mSearchAutoComplete.setText(text);
}

}

那么我就需要在我的customSearchView类中添加一个新的方法(setText)。 - shaktiman_droid
是的:public void setText(String text) { mSearchAutoComplete.setText(text); } - user901309
为什么不使用默认的建议功能?就像这里:http://developer.android.com/guide/topics/search/adding-custom-suggestions.html - Vedant Agarwala
谢谢;我正在与onQueryTextChange() (SearchView.OnQueryTextListener)结合使用这种方法,并且很快就使事情正常化了。 - Jonik
1
可能是因为人们不想使用CursorAdapter - Mussa
显示剩余3条评论

36

我使用了自定义的AutoCompleteTextView,并将其添加到ActionBar中。

enter image description here

public class MainActivity extends Activity {

    private static final String[] COUNTRIES = new String[] { "Belgium",
            "France", "France_", "Italy", "Germany", "Spain" };

    @Override
    public void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        setContentView(R.layout.activity_main);

        ActionBar actionBar = getActionBar();
        actionBar.setDisplayHomeAsUpEnabled(true);
        actionBar.setDisplayShowCustomEnabled(true);
        // actionBar.setDisplayShowTitleEnabled(false);
        // actionBar.setIcon(R.drawable.ic_action_search);

        LayoutInflater inflator = (LayoutInflater) this
                .getSystemService(Context.LAYOUT_INFLATER_SERVICE);
        View v = inflator.inflate(R.layout.actionbar, null);

        actionBar.setCustomView(v);

        ArrayAdapter<String> adapter = new ArrayAdapter<String>(this,
                android.R.layout.simple_dropdown_item_1line, COUNTRIES);
        AutoCompleteTextView textView = (AutoCompleteTextView) v
                .findViewById(R.id.editText1);
        textView.setAdapter(adapter);

    }

}

您的布局:

<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
    android:layout_width="fill_parent"
    android:layout_height="fill_parent"
    android:gravity="center" >

    <TextView
        android:id="@+id/textView1"
        android:layout_width="wrap_content"
        android:layout_height="wrap_content"
        android:text="Action Bar:"
        android:textAppearance="?android:attr/textAppearanceMedium"
        android:textColor="#FFFFFF" />

    <AutoCompleteTextView
        android:id="@+id/editText1"
        android:layout_width="wrap_content"
        android:layout_height="wrap_content"
        android:ems="10"
        android:imeOptions="actionSearch"
        android:inputType="textAutoComplete|textAutoCorrect"
        android:textColor="#FFFFFF" >

        <requestFocus />
    </AutoCompleteTextView>

</LinearLayout>

编辑:

请查看这个这个链接,可能有所帮助。 代码在这里。


我该如何将数据源添加为数据库?使用有限的字符串更容易。但是我有接近250个,还有一个最多有4000行的列表。 - Harsha M V
3
好的,但这不是一个搜索视图。实际上,我想要一个搜索图标在左边,当有文本输入时,在右边显示X图标。 - tasomaniac
@tasomaniac:你想要的也不是一个搜索视图...无论如何,结合上述代码和这段代码---->https://dev59.com/zmw15IYBdhLWcg3w3ffN#6355178 - Dhaval Parmar
4
我解决了我的问题。实际上,SearchView自带自动完成功能,但需要ContentProvider支持。我没有设置AutoCompleteTextView及其必要的函数,而是简单地创建了一个仅执行查询而非插入、删除的ContentProvider。这样问题就解决了。 - tasomaniac
在一个活动的操作栏中添加两个搜索小部件是否可行? - Elyes Jlassi
ActionBar在哪里? - Rasshu

8
我曾经遇到过自动完成功能的搜索视图问题,但是没有使用任何额外的布局和自动完成文本视图来解决它。有一个叫做SearchAutoComplete的类,我使用了它来实现自动完成功能,只需将包含建议项的ArrayList的简单列表适配器放入搜索视图中即可。将适配器设置为您的SearchAutoComplete,自动完成功能就会起作用。下面是我的代码。请记住,您不必添加任何自定义布局。只需用我的代码替换您的onCreateOptionsMenu(...)方法即可:
    @Override
public boolean onCreateOptionsMenu(Menu menu) {
// Inflate the menu; this adds items to the action bar if it is present.
getMenuInflater().inflate(R.menu.main, menu);

MenuItem searchItem = menu.findItem(R.id.search);
SearchView mSearchView = (SearchView) MenuItemCompat.getActionView(searchItem);

SearchAutoComplete searchAutoComplete = (SearchAutoComplete)     mSearchView.findViewById(android.support.v7.appcompat.R.id.search_src_text);

ArrayAdapter<String> adapter = new ArrayAdapter<String>(this,
android.R.layout.simple_dropdown_item_1line, itemArrayList);
searchAutoComplete.setAdapter(adapter);

SearchManager searchManager =
(SearchManager) getSystemService(this.SEARCH_SERVICE);
mSearchView.setSearchableInfo(
searchManager.getSearchableInfo(getComponentName()));
return true;
}

点击建议的项目后,该项目应出现在搜索视图中,因此请在 onCreateOptionmenu(...) 方法内设置适配器为 searchAutoComplete 之后添加以下代码。
  searchAutoComplete.setOnItemClickListener(new OnItemClickListener() {

    @Override
    public void onItemClick(AdapterView<?> parent, View view, int position,
    long id) {
    // TODO Auto-generated method stub

    String searchString=(String)parent.getItemAtPosition(position);
    searchAutoComplete.setText(""+searchString);
    Toast.makeText(MainActivity.this, "you clicked "+searchString, Toast.LENGTH_LONG).show();

    }
    });

我尝试过这个,但是出现了以下异常:java.lang.NullPointerException: Attempt to invoke virtual method 'void android.support.v7.widget.SearchView$SearchAutoComplete.setTextColor(int)' on a null object reference。似乎 searchAutoComplete 对象总是返回 null。你有任何想法为什么会这样吗? - VIN
嗨@HAXM...这对我有用...但是我得到的建议列表是在灰色背景中,如何将其改为白色背景? - SoulRayder
@SoulRayder,更改您的工具栏主题,如果您没有使用工具栏,则使用工具栏,因为如果不使用工具栏,则必须更改活动的主题,这将更改活动的外观。 - HAXM

8

这里有一种使用 CursorAdapter 的替代方法:

ExampleActivity.java

private Menu menu;

@Override
@TargetApi(Build.VERSION_CODES.HONEYCOMB)
public boolean onCreateOptionsMenu(Menu menu) {

    getMenuInflater().inflate(R.menu.example, menu);

    this.menu = menu;

    if(Build.VERSION.SDK_INT >= Build.VERSION_CODES.HONEYCOMB) {

        SearchManager manager = (SearchManager) getSystemService(Context.SEARCH_SERVICE);

        SearchView search = (SearchView) menu.findItem(R.id.search).getActionView();

        search.setSearchableInfo(manager.getSearchableInfo(getComponentName()));

        search.setOnQueryTextListener(new OnQueryTextListener() { 

            @Override 
            public boolean onQueryTextChange(String query) {

                loadHistory(query);

                return true; 

            } 

        });

    }

    return true;

}

// History
@TargetApi(Build.VERSION_CODES.HONEYCOMB)
private void loadHistory(String query) {

    if(Build.VERSION.SDK_INT >= Build.VERSION_CODES.HONEYCOMB) {

        Cursor cursor = db.rawQuery("SELECT * FROM items", null); // Example database query

        SearchManager manager = (SearchManager) getSystemService(Context.SEARCH_SERVICE);

        final SearchView search = (SearchView) menu.findItem(R.id.search).getActionView();

        search.setSearchableInfo(manager.getSearchableInfo(getComponentName()));

        search.setSuggestionsAdapter(new ExampleAdapter(this, cursor, items));

    }

}

现在您需要创建一个扩展自CursorAdapter的适配器:

ExampleAdapter.java

public class ExampleAdapter extends CursorAdapter {

    private List<String> items;

    private TextView text;

    public ExampleAdapter(Context context, Cursor cursor, List<String> items) {

        super(context, cursor, false);

        this.items = items;

    }

    @Override
    public void bindView(View view, Context context, Cursor cursor) {

        text.setText(cursor.getString(cursor.getColumnIndex("text"))); // Example column index

    }

    @Override
    public View newView(Context context, Cursor cursor, ViewGroup parent) {

        LayoutInflater inflater = (LayoutInflater) context.getSystemService(Context.LAYOUT_INFLATER_SERVICE);

        View view = inflater.inflate(R.layout.item, parent, false);

        text = (TextView) view.findViewById(R.id.text);

        return view;

    }

}

请注意:当您导入CursorAdapter时,请勿导入Android支持版本,而是导入标准的android.widget.CursorAdapter
该适配器还需要自定义布局: res/layout/item.xml
<RelativeLayout xmlns:android="http://schemas.android.com/apk/res/android"
    android:layout_width="fill_parent"
    android:layout_height="fill_parent">

    <TextView
        android:id="@+id/item"
        android:layout_width="wrap_content"
        android:layout_height="wrap_content" />

</RelativeLayout>

现在您可以通过在布局中添加额外的文本或图像视图并在适配器中填充数据来自定义列表项。现在您需要一个SearchView菜单项: res/menu/example.xml
<menu xmlns:android="http://schemas.android.com/apk/res/android">

    <item
        android:id="@+id/search"
        android:title="@string/search"
        android:showAsAction="ifRoom"
        android:actionViewClass="android.widget.SearchView" />

</menu>

然后创建一个可搜索的配置: res/xml/searchable.xml
<searchable xmlns:android="http://schemas.android.com/apk/res/android"
    android:label="@string/search"
    android:hint="@string/search" >
</searchable>

最后,在清单文件的相关活动标记中添加以下内容:

AndroidManifest.xml

<intent-filter>
    <action android:name="android.intent.action.SEARCH" />
</intent-filter>

<meta-data
    android:name="android.app.default_searchable"
    android:value="com.example.ExampleActivity" />
<meta-data
    android:name="android.app.searchable"
    android:resource="@xml/searchable" />

请注意:示例中使用的@string/search字符串应该在values/strings.xml中定义,同时不要忘记为您的项目更新对com.example的引用。
以下是原始教程供参考: http://tpbapp.com/android-development/android-action-bar-searchview-tutorial

谢谢!这让我的一天都充满了阳光 :)对我来说有一个更改,我将通过向服务器请求数据,然后使用新的适配器更新SearchView。如果它起作用了,我会让你知道的。再次感谢! - Shirane85
@Shirane85 嘿,没问题,希望一切顺利。在我看来,这种方法是为了显示数据库结果而设计的,因为需要使用游标数据格式,这是处理数据库数据的标准格式。我在这里也将其改编为与ArrayList一起使用,以便与典型数据集通常更兼容,但是当处理大量数据时,这当然会限制性能。 - tpbapp
好的,毕竟我有问题。在填写搜索框中的字母后调用网络时,在 onQueryTextChange 中返回 true。到目前为止,一切都很好,没有发生任何事情。但是当响应回调并且我执行 search.setSuggestionsAdapter() 时,仍然没有发生任何事情,建议弹出窗口未显示。奇怪的是,在填写第二个字母后,它立即显示第一个适配器的建议。需要帮助... - Shirane85
@Shirane85,你是说当你创建静态示例列表而不是从Web资源加载时,会出现这种情况吗?听起来你需要把这个问题转化为另一个问题并发布你的代码,因为我没有遇到过这种情况。如果你发表一个问题,请在这里提供链接,我会尽力帮忙。 - tpbapp
代码有 bug。如果直接使用,它会显示奇怪(或者说是旧的)建议。解决方法是每次都创建并设置新的适配器,并且总是调用 notifyDataSetChanged - 0101100101
链接到原教程以供参考:已过时 - Kenny Dabiri

6

是的,这是可能的。建立一个表格(例如在SQLiteDatabase中)来存储您的建议,并使用所需的列格式化该表格。

请参考此链接


能否添加自动完成功能呢? - Harsha M V
请看这个链接:https://dev59.com/tuo6XIcBkEYKwwoYPSH7它提供了如何在搜索视图中实现自动完成的解释。 - buggydroid

3
    SearchAutoComplete searchAutoComplete = mSearchView.findViewById(androidx.appcompat.R.id.search_src_text);

对于androidx版本


1

您可以通过 CursorAdapter 实现此操作,例如:

MenuItem searchItem = menu.findItem(R.id.action_search);
SearchView searchView = (SearchView) MenuItemCompat.getActionView(searchItem);

searchView.setSuggestionsAdapter(adapter);

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接