-->

Is it possible to pass search context data using t

2020-06-06 05:44发布

问题:

According to the official documentation, there are two ways of providing a search interface: using either the search dialog or a SearchView widget. I'd like to pay attention to passing search context data using these two ways.

So, the documentation says:

..you can provide additional data in the intent that the system sends to your searchable activity. You can pass the additional data in the APP_DATA Bundle, which is included in the ACTION_SEARCH intent.

To pass this kind of data to your searchable activity, override the onSearchRequested() method for the activity from which the user can perform a search, create a Bundle with the additional data, and call startSearch() to activate the search dialog. For example:

@Override
public boolean onSearchRequested() {
     Bundle appData = new Bundle();
     appData.putBoolean(SearchableActivity.JARGON, true);
     startSearch(null, false, appData, false);
     return true;
}

..Once the user submits a query, it's delivered to your searchable activity along with the data you've added. You can extract the extra data from the APP_DATA Bundle to refine the search. For example:

Bundle appData = getIntent().getBundleExtra(SearchManager.APP_DATA);
if (appData != null) {
    boolean jargon = appData.getBoolean(SearchableActivity.JARGON);
}

This refers to the search dialog. And what about the search widget?

Is it possible to pass search context data using the SearchView widget only?

Hope, someone could give clear explanation and/or suggest another or similar way to accomplish the goal.

Thanks!

回答1:

I've discovered the solution. Even two solutions!

They don't need to invoke onSearchRequested() thus there is no search dialog at all :)

First, I provide some common steps to create the search interface and then give the solutions of the source problem.

We add the Search View to the App Bar by creating res/menu/options_menu.xml file with the following code:

<?xml version="1.0" encoding="utf-8"?>
<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=".MainActivity" >

    <item
        android:id="@+id/action_search"
        android:icon="@drawable/ic_action_search"
        android:title="@string/search_string"
        app:showAsAction="collapseActionView|ifRoom"
        app:actionViewClass="android.support.v7.widget.SearchView" />

</menu>

Create a Searchable Configuration in res/xml/searchable.xml file:

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

<searchable xmlns:android="http://schemas.android.com/apk/res/android"
        android:label="@string/app_name"
        android:hint="@string/search_hint" />

Declare two activities in AndroidManifest.xml:

<?xml version="1.0" encoding="utf-8"?>
<manifest xmlns:android="http://schemas.android.com/apk/res/android"
    package="com.example.searchinterface">

    <application
        android:icon="@mipmap/ic_launcher"
        android:label="@string/app_name"
        android:theme="@style/AppTheme">
        <activity
            android:name=".MainActivity"
            android:label="@string/app_name">
            <intent-filter>
                <action android:name="android.intent.action.MAIN" />

                <category android:name="android.intent.category.LAUNCHER" />
            </intent-filter>
        </activity>

        <activity android:name=".SearchableActivity"
            android:label="@string/app_name"
            android:launchMode="singleTop">
            <intent-filter>
                <action android:name="android.intent.action.SEARCH"/>
            </intent-filter>
            <meta-data android:name="android.app.searchable"
                android:resource="@xml/searchable"/>
        </activity>

    </application>

</manifest>

Create a Searchable Activity where we handle the ACTION_SEARCH intent and the search context data, passed from the MainActivity. We extract the extra data from the APP_DATA Bundle to refine the search:

public class SearchableActivity extends AppCompatActivity {
    public static final String JARGON = "com.example.searchinterface.jargon";

    @Override
    protected void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        setContentView(R.layout.activity_searchable);

        handleIntent(getIntent());
    }

    @Override
    protected void onNewIntent(Intent intent) {
        super.onNewIntent(intent);

        handleIntent(intent);
    }

    private void handleIntent(Intent intent) {

        if (Intent.ACTION_SEARCH.equals(intent.getAction())) {
            String query = intent.getStringExtra(SearchManager.QUERY);
            // use the query to search the data somehow

            Bundle appData = intent.getBundleExtra(SearchManager.APP_DATA);
            if (appData != null) {
                boolean jargon = appData.getBoolean(SearchableActivity.JARGON);
                // use the context data to refine our search
            }
        }
    }
}

Now, we need to implement our MainActivity class. So, we inflate our menu and configure the SearchView element as well. We also need to set the SearchView.OnQueryTextListener and implement its methods, especially onQueryTextSubmit(). It is invoked when the user presses the submit button and it contains the main logic of passing the search context data to the SearchableActivity. Finally, we reached the main answer section. As I said, there are two solutions:

1. Create an intent with Bundle extra and send it to the SearchableActivity manually;

Here is the MainActivity with all necessary contents:

public class MainActivity extends AppCompatActivity implements SearchView.OnQueryTextListener {
    private SearchView mSearchView;

    @Override
    public boolean onCreateOptionsMenu(Menu menu) {
        getMenuInflater().inflate(R.menu.options_menu, menu);

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

        // associate searchable configuration with the SearchView
        SearchManager searchManager = (SearchManager) getSystemService(Context.SEARCH_SERVICE);
        mSearchView.setSearchableInfo(searchManager.getSearchableInfo(
                new ComponentName(this, SearchableActivity.class)));

        mSearchView.setOnQueryTextListener(this);

        return true;
    }

    @Override
    public boolean onQueryTextSubmit(String query) {
        Intent searchIntent = new Intent(this, SearchableActivity.class);
        searchIntent.putExtra(SearchManager.QUERY, query);

        Bundle appData = new Bundle();
        appData.putBoolean(SearchableActivity.JARGON, true); // put extra data to Bundle
        searchIntent.putExtra(SearchManager.APP_DATA, appData); // pass the search context data
        searchIntent.setAction(Intent.ACTION_SEARCH);

        startActivity(searchIntent);

        return true; // we start the search activity manually
    }

    @Override
    public boolean onQueryTextChange(String newText) {
        return false;
    }
}

Thanks to https://stackoverflow.com/a/22184137/6411150.

The second solution is also put in onQueryTextSubmit() (but it's not necessary):

2. Create search context data Bundle and pass it to the setAppSearchData() method of the SearchView.

So, we don't need to create and pass the whole search intent and launch respective searchable activity, the system will take care of it.

Here is another code snippet:

/*
 You may need to suppress the “restrictedApi” error that you could possibly
 receive from this method "setAppSearchData(appData)”.I had to
 I’m targetSdkVersion 26. I’m also using Android Studio 3 
 with the new gradle plugin, which might be causing this.

 If you’re not running Android Studio 3 you can simply put
 “//noinspection RestrictedApi" 
  right above the line: mSearchView.setAppSearchData(appData);
 */
@SuppressWarnings("RestrictedApi")
@Override
public boolean onQueryTextSubmit(String query) {
    Bundle appData = new Bundle();
    appData.putBoolean(SearchableActivity.JARGON, true); // put extra data to Bundle
    mSearchView.setAppSearchData(appData); // pass the search context data

    return false; // we do not need to start the search activity manually, the system does it for us 
}

Thanks to https://stackoverflow.com/a/38295904/6411150.

Note: Only support library's version of SearchView (android.support.v7.widget.SearchView) contains the setAppSearchData() method, so be attentive.



回答2:

If the design works for your needs, you can use the SearchView by itself, and add an OnQueryTextListener to it, and deal with it there. There is no need for anything else, no Intents, Meta-tags, nor XML files. I have done this a few times, and the docs are a bit not clear on this.