如何建立Gmail的类似操作栏中的搜索框?栏中、类似、操作、Gmail

2023-09-12 01:42:26 作者:永远不联络

我目前使用搜索查看插件里面 ActionBarcompat 来筛选列表,而搜索。

I am currently using SearchView widget inside ActionBarcompat to filter a list while searching.

当用户开始输入文本的主要布局更新的ListView一个适配器,可以筛选结果。我这样做是通过实施 OnQueryTextListener 和过滤器上的每个击键的结果。

When the user starts entering text the ListView in the main layout updates with an Adapter to filter the results. I do this by implementing a OnQueryTextListener and filter the results on each key stroke.

相反,我想创建Gmail一样,支持自动搜索框中显示生成列表,并没有改变基本的观点

Instead, I want to create a Gmail like search box with auto suggest list generated and no changes to the underlying view

我已经去了搜索查看成分,但它需要的搜索的活动。我想下拉是在MainActivity在那里我有ListView控件(例如在Gmail应用),并没有一个专门的活动。 此外,实现用同样的方式作为教程好像我想要什么矫枉过正(只是一个下拉列表)

I have went through this tutorial that uses the SearchView component but it requires a searchable activity. I want the drop-down to be over the MainActivity where I have the ListView (like in the Gmail app) and not a dedicated Activity. Besides, implementing it the same way as in the tutorial seems like an overkill for what I want (just a dropdown)

推荐答案

如果你只是想,做什么discribed问题中一个组成部分,我建议这个的库。您也可以实现超出的-BX 搜索界面的,但是,是注意,它也有UI限制:

If you just want a component that does what is discribed in the question, I suggest this library. You can also implement the out-of-the-bx searchable interface, however, be aware that it does have UI limitations:

要实现类似Gmail应用程序的接口,你就必须了解conceps:

To implement an interface similar to Gmail App, you will have to understand conceps of:

在内容提供商; 在SQLite的持久化数据 在列表视图或RecyclerView及其适配器; 在活动之间传递数据;

最终的结果应该是这个样子:的

有很多(很多)的方法来获得同样的结果(或更好),我会discribed一种可能的方式。

There are many (many) ways to get to the same result (or better), I'll discribed one possible way.

我决定来管理整个界面在一个新的活动,因为,我已经创建了三个XML布局:

I decided to manage the entire interface in a new Activity, for that I've created three XML layouts:

custom_searchable.xml:大会的一个RelativeLayout的,将作为对SearchActivity内容的所有UI元素;

custom_searchable.xml: assemblys all UI elements in one RelativeLayout that will serve as content for the 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;

custom_searchable_header_layout.xml: holds the search bar where the user will type his query. It will also contain the mic, erase and return 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元素被显示在结果列表显示在响应于用户的查询;

custom_searchable_row_details.xml: holds the UI elements to be displayed in the result list to be displayed in response to the user query;

<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将被调用。它有一些主要的responsabilities:

The idea is that, when the user types the search button (which you can place any where you want), this SearchActivity will be called. It has some main responsabilities:

绑定到custom_searchable_header_layout.xml:通过这样做,有可能:

Bind to the UI elements in the custom_searchable_header_layout.xml: by doing that, it is possible:

提供的侦听器的EditText(其中用户输入自己的查询):

to provide listeners for the EditText (where the user will type his query):

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(),并返回给调用者的活动):

学会这13种寻找Facebook广告兴趣受众的方法,你可以多找到100 兴趣词

add listener for the return button (which by its turn will just call finish() and return to the caller activity):

this.dismissDialog.setOnClickListener(new View.OnClickListener() {
    public void onClick(View v) {
    finish();
}    

要求的意图谷歌语音到文本的API:

calls the intent for 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);
                }
            }
        });
    }

在建立一个搜索界面的开发者tipically两种选择:

When building the a search interface the developer has tipically two options:

推荐最近的查询向用户:这意味着每次用户进行搜索,所述输入查询将在数据库中被持久要检索后者上作为建议以供将来搜索; 推荐自定义选项给用户:开发者将尝试predict用户想要通过处理已键入的字母是什么;

在这两种情况下,答案须送交回因为这将有它的内容显示在结果列表中itens一个Cursor对象。这整个过程可以实现使用内容提供商的API。有关如何使用内容提供商详细信息可以在此链接即可到达。

In both cases the answers shall be delivered back as a Cursor object that will have its content displayed as itens in the result list. This whole process can be implement using the Content Provider API. Details about how to use Content Providers can be reached in this link.

在其中的显影剂希望实现在1中描述的行为的情况下,它可以是有用的使用exteding所述SearchRecentSuggestionsProvider类的策略。有关如何做到这一点的详细信息可以在此link.

In the case where the developer wants to implement the behavior described in 1., it can be usefull to use the strategy of exteding the SearchRecentSuggestionsProvider class. Details about how to do it can be reached in this link.

该接口应提供下列行为:

This interface shall provide the following behavior:

当用户键入一个字母调用检索的内容提供者类应返回一个充满光标将被显示在列表中建议的查询方法 - 你应该不冻结UI线程,所以它,我建议执行中的AsyncTask此搜索:

When the user types a letter a call to the query method of the retrieved content provider class should return a filled cursor with the suggestion to be displayed in the list - you should take to not freeze the UI thread, so it I recommend to perform this search in an 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这将是这个任务的最佳practive):

Inside the onPostExecute() method of your AsyncTask, you should retrieve a list (that should come from the doInBackground() method) containing the results to be displayed in the ResultList (you can map it in a POJO class and pass it to your custom adapter or you can use a CursorAdapter which would be the best practive for this task):

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;

当用户触摸的软键盘搜索按钮确定。当他这样做,发送意图的搜索活动(一个负责处理搜索结果),并添加查询作为额外信息的意图

Identify when the user touches the search button from the soft keyboard. When he does that, send an intent to the searchable activity (the one responsible for handling the search result) and add the query as extra information in the intent

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

确定当用户点击所显示的建议之一,并发送包含该项目的信息意图(这个意图应该是从previous步骤中的一个不同)

Identify when the user clicks in one of the displayed suggestions and send and intent containing the item information (this intent should be different from the one of the previous step)

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();
    }
}

在discribed步骤应该够实现接口yourseld。所有code例子取自这里。我做了这个库,做pretty的大部分以上所述。它没有很好地测试过,有些用户界面的配置可能不可用呢。

The discribed steps should be enough for implementing an interface yourseld. All code examples were taken from here. I've made this library that does pretty much of what is described above. It is not well tested yet and some of the UI configuration might not be available yet.

我希望这个答案可以帮助有需要的人。

I hope this answer might help someone in need.