【问题标题】:Implementing SearchView in action bar在操作栏中实现 SearchView
【发布时间】:2014-03-02 08:39:30
【问题描述】:

我需要从我的arrayList<String> 创建SearchView 并在下拉列表中显示与此相同的建议

我正在寻找逐步解释如何在操作栏中构建SearchView 的教程。

我已阅读 the documentation 并按照示例 google 但它对我没有用。

我已经创建了搜索

<?xml version="1.0" encoding="utf-8"?>

<menu xmlns:android="http://schemas.android.com/apk/res/android">
    <item android:id="@+id/action_search"
          android:title="Search"
          android:icon="@android:drawable/ic_menu_search"
          android:showAsAction="always"
          android:actionViewClass="android.widget.SearchView" />
</menu>`

但是我不知道如何设置字符串数组的参数。 我试图在不同的 Activity 中检索结果,但不起作用。

【问题讨论】:

  • 您应该尽可能详细地发布您尝试过的所有内容。带有示例代码的具体问题往往会得到更快、更有意义的答案。

标签: android android-actionbar autocompletetextview searchview


【解决方案1】:

为此制定解决方案需要一段时间,但发现这是使其以您描述的方式工作的最简单方法。可能有更好的方法来做到这一点,但由于您尚未发布您的活动代码,我将不得不即兴发挥并假设您在活动开始时有这样的列表:

private List<String> items = db.getItems();

ExampleActivity.java

private List<String> items;

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
        String[] columns = new String[] { "_id", "text" };
        Object[] temp = new Object[] { 0, "default" };

        MatrixCursor cursor = new MatrixCursor(columns);

        for(int i = 0; i < items.size(); i++) {

            temp[0] = i;
            temp[1] = items.get(i);

            cursor.addRow(temp);

        }

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

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

        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(items.get(cursor.getPosition()));

    }

    @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;

    }

}

更好的方法是,如果您的列表数据来自数据库,您可以将数据库函数返回的Cursor 直接传递给ExampleAdapter,并使用相关的列选择器在@987654329 中显示列文本@ 在适配器中引用。

请注意:导入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 的引用。

【讨论】:

  • 您好,但有一个疑问,我希望您能帮助我解决这个问题。如果我在过滤器上键入一个字母,虽然我有过滤结果,但如果超过一个,则不会显示任何内容。然后只有它显示。为什么?
  • 您好,请检查您的样式是否有任何与 EditText 或 AutoCompleteTextView 相关的内容,因为您可以设置在显示自动完成之前需要输入的默认字符数。实际上我认为默认值是 2 个字符,因此您可能需要将其添加到样式中才能更改它。
  • 您好,在开发过程中,我又遇到了一期 searchview。如果我在下拉列表中显示更多项目,例如:3 个文本视图,则项目的大小会增加,因此如果我像 i.stack.imgur.com/d7t3A.png 一样滚动,搜索项目会在键盘上流动。如果我增加文本大小或在项目中添加更多视图,就会发生这种情况。这是默认行为吗?
  • 再次嗨,不,这不应该发生。听起来您的项目中已经有很多代码干扰了 SearchView 中的 AutoCompleteTextView,或者键盘软件、应用程序或插件可能有问题导致了这种行为。默认行为应该是这样的 stackoverflow.com/questions/22116237/… 除了这个人实际上是在问如何让它隐藏在键盘后面。
  • @tpbapp 我已经弄清楚了。没有出现建议列表的原因是因为在您的代码中,您在 onCreateOptionsMenu()loadHistory() 方法中调用了两次 search.setSearchableInfo(manager.getSearchableInfo(getComponentName()));。所以我创建了 SearchView 和 SearchManager 全局变量,并且只在 onCreateOptionsMenu() 中调用了一次 getSearchableInfo()。我的建议清单现在运行良好。我建议你修改你的代码。
【解决方案2】:

如果其他人在 searchview 变量上有一个 nullptr,我发现项目设置有点不同:

旧:

android:showAsAction="ifRoom"
android:actionViewClass="android.widget.SearchView"

新:

app:showAsAction="ifRoom|collapseActionView"
app:actionViewClass="androidx.appcompat.widget.SearchView"

pre-android x:

app:showAsAction="ifRoom|collapseActionView"
app:actionViewClass="android.support.v7.widget.SearchView"

更多信息,它的更新文档是located here

【讨论】:

  • 谢谢。正是我所缺少的。 :-)
  • 谢谢你,这很简单而且很有帮助。
  • app:actionViewClass="androidx.appcompat.widget.SearchView" for androidx
【解决方案3】:

SearchDialog 还是 SearchWidget?

在实现搜索功能时,官方 Android 开发者文档中有 two suggested approach
您可以使用 SearchDialogSearchWidget
我将解释使用 SearchWidget 实现搜索功能。

如何使用搜索小部件?

我将使用 SearchWidget 在 RecyclerView 中解释搜索功能。这很简单。

只需遵循这 5 个简单步骤

1) 在菜单中添加 searchView 项

您可以在菜单中添加SearchView 可以添加为actionView 使用

app:useActionClass = "android.support.v7.widget.SearchView" .

<menu xmlns:android="http://schemas.android.com/apk/res/android"
   xmlns:app="http://schemas.android.com/apk/res-auto"
   xmlns:tools="http://schemas.android.com/tools"
   tools:context="rohksin.com.searchviewdemo.MainActivity">
   <item
       android:id="@+id/searchBar"
       app:showAsAction="always"
       app:actionViewClass="android.support.v7.widget.SearchView"
   />
</menu>

2) 设置 SerchView 提示文本、监听器等

您应该在 onCreateOptionsMenu(Menu menu) 方法中初始化 SearchView。

  @Override
  public boolean onCreateOptionsMenu(Menu menu) {
     // Inflate the menu; this adds items to the action bar if it is present.
        getMenuInflater().inflate(R.menu.menu_main, menu);

        MenuItem searchItem = menu.findItem(R.id.searchBar);

        SearchView searchView = (SearchView) searchItem.getActionView();
        searchView.setQueryHint("Search People");
        searchView.setOnQueryTextListener(this);
        searchView.setIconified(false);

        return true;
   }

3) 在 Activity 中实现 SearchView.OnQueryTextListener

OnQueryTextListener 有两个抽象方法

  1. onQueryTextSubmit(String query)
  2. onQueryTextChange(String newText

所以你的 Activity 骨架应该是这样的

YourActivity extends AppCompatActivity implements SearchView.OnQueryTextListener{

     public boolean onQueryTextSubmit(String query)

     public boolean onQueryTextChange(String newText) 

}

4) 实现 SearchView.OnQueryTextListener

你可以像这样提供抽象方法的实现

public boolean onQueryTextSubmit(String query) {

    // This method can be used when a query is submitted eg. creating search history using SQLite DB

    Toast.makeText(this, "Query Inserted", Toast.LENGTH_SHORT).show();
    return true;
}

@Override
public boolean onQueryTextChange(String newText) {

    adapter.filter(newText);
    return true;
}

5) 在您的 RecyclerView Adapter 中编写一个过滤器方法。

最重要的部分。您可以编写自己的逻辑来执行搜索。
这是我的。此 sn-p 显示名称列表,其中包含在 SearchView 中键入的文本

public void filter(String queryText)
{
    list.clear();

    if(queryText.isEmpty())
    {
       list.addAll(copyList);
    }
    else
    {

       for(String name: copyList)
       {
           if(name.toLowerCase().contains(queryText.toLowerCase()))
           {
              list.add(name);
           }
       }

    }

   notifyDataSetChanged();
}

相关链接:

Music App 中使用 SQLite 数据库的 SearchView 上的完整工作代码

【讨论】:

    【解决方案4】:

    Searchview 使用这些代码

    1. 对于 XML

      <android.support.v7.widget.SearchView
          android:layout_width="match_parent"
          android:layout_height="wrap_content"
          android:id="@+id/searchView">
      
      </android.support.v7.widget.SearchView>
      

    2. 在您的片段或活动中

      package com.example.user.salaryin;
      
      import android.app.ProgressDialog;
      import android.os.Bundle;
      import android.support.v4.app.Fragment;
      import android.support.v4.view.MenuItemCompat;
      import android.support.v7.widget.GridLayoutManager;
      import android.support.v7.widget.LinearLayoutManager;
      import android.support.v7.widget.RecyclerView;
      import android.support.v7.widget.SearchView;
      import android.view.LayoutInflater;
      import android.view.Menu;
      import android.view.MenuInflater;
      import android.view.MenuItem;
      import android.view.View;
      import android.view.ViewGroup;
      import android.widget.Toast;
      import com.example.user.salaryin.Adapter.BusinessModuleAdapter;
      import com.example.user.salaryin.Network.ApiClient;
      import com.example.user.salaryin.POJO.ProductDetailPojo;
      import com.example.user.salaryin.Service.ServiceAPI;
      import java.util.ArrayList;
      import java.util.List;
      import retrofit2.Call;
      import retrofit2.Callback;
      import retrofit2.Response;
      
      
      public class OneFragment extends Fragment implements SearchView.OnQueryTextListener {
      
      RecyclerView recyclerView;
      RecyclerView.LayoutManager layoutManager;
      ArrayList<ProductDetailPojo> arrayList;
      BusinessModuleAdapter adapter;
      private ProgressDialog pDialog;
      GridLayoutManager gridLayoutManager;
      SearchView searchView;
      
      public OneFragment() {
          // Required empty public constructor
      }
      
      @Override
      public void onCreate(Bundle savedInstanceState) {
          super.onCreate(savedInstanceState);
      }
      
      @Override
      public View onCreateView(LayoutInflater inflater, ViewGroup container,
                               Bundle savedInstanceState) {
      
          View rootView = inflater.inflate(R.layout.one_fragment,container,false);
      
          pDialog = new ProgressDialog(getActivity());
          pDialog.setMessage("Please wait...");
      
      
          searchView=(SearchView)rootView.findViewById(R.id.searchView);
          searchView.setQueryHint("Search BY Brand");
          searchView.setOnQueryTextListener(this);
      
          recyclerView = (RecyclerView) rootView.findViewById(R.id.recyclerView);
          layoutManager = new LinearLayoutManager(this.getActivity());
          recyclerView.setLayoutManager(layoutManager);
          gridLayoutManager = new GridLayoutManager(this.getActivity().getApplicationContext(), 2);
          recyclerView.setLayoutManager(gridLayoutManager);
          recyclerView.setHasFixedSize(true);
          getImageData();
      
      
          // Inflate the layout for this fragment
          //return inflater.inflate(R.layout.one_fragment, container, false);
          return rootView;
      }
      
      
      private void getImageData() {
          pDialog.show();
          ServiceAPI service = ApiClient.getRetrofit().create(ServiceAPI.class);
          Call<List<ProductDetailPojo>> call = service.getBusinessImage();
      
          call.enqueue(new Callback<List<ProductDetailPojo>>() {
              @Override
              public void onResponse(Call<List<ProductDetailPojo>> call, Response<List<ProductDetailPojo>> response) {
                  if (response.isSuccessful()) {
                      arrayList = (ArrayList<ProductDetailPojo>) response.body();
                      adapter = new BusinessModuleAdapter(arrayList, getActivity());
                      recyclerView.setAdapter(adapter);
                      pDialog.dismiss();
                  } else if (response.code() == 401) {
                      pDialog.dismiss();
                      Toast.makeText(getActivity(), "Data is not found", Toast.LENGTH_SHORT).show();
                  }
      
              }
      
              @Override
              public void onFailure(Call<List<ProductDetailPojo>> call, Throwable t) {
                  Toast.makeText(getActivity(), t.getMessage(), Toast.LENGTH_SHORT).show();
                  pDialog.dismiss();
      
              }
          });
      }
      
         /* @Override
          public void onCreateOptionsMenu(Menu menu, MenuInflater inflater) {
          getActivity().getMenuInflater().inflate(R.menu.menu_search, menu);
          MenuItem menuItem = menu.findItem(R.id.action_search);
          SearchView searchView = (SearchView) MenuItemCompat.getActionView(menuItem);
          searchView.setQueryHint("Search Product");
          searchView.setOnQueryTextListener(this);
      }*/
      
      @Override
      public boolean onQueryTextSubmit(String query) {
          return false;
      }
      
      @Override
      public boolean onQueryTextChange(String newText) {
          newText = newText.toLowerCase();
          ArrayList<ProductDetailPojo> newList = new ArrayList<>();
          for (ProductDetailPojo productDetailPojo : arrayList) {
              String name = productDetailPojo.getDetails().toLowerCase();
      
              if (name.contains(newText) )
                  newList.add(productDetailPojo);
              }
          adapter.setFilter(newList);
          return true;
         }
      }
      
    3. 在适配器类中

       public void setFilter(List<ProductDetailPojo> newList){
          arrayList=new ArrayList<>();
          arrayList.addAll(newList);
          notifyDataSetChanged();
      }
      

    【讨论】: