How to use an Android SearchView without singleTop

2019-02-14 01:12发布

I have an activity that I normally want to exist in multiple tasks, so that the [Back] button restores a previous state; however, I also want to use a SearchView with an existing activity, without pushing a new one onto the task stack (since I want to search what's currently displayed). Here's my problem:

  • If I set the activity's launch mode to "singleTop", the SearchView works with the current activity, but Context.startActivity does not add new activities to the stack.
  • If I set the activity's launch mode to "standard", startActivity adds new activities to the stack, but SearchView also creates a new (empty) activity, instead of searching the existing one.

I can't find any way make SearchView use intent flags (e.g. FLAG_ACTIVITY_SINGLE_TOP. I tried going the other way around, setting launch mode to "singleTop" then adding FLAG_ACTIVITY_NEW_TASK to every intent, but that worked only when launching from a different activity class; when I try to launch a new instance of an activity from its own class, Android didn't respect FLAG_ACTIVITY_NEW_TASK and didn't push a new task onto the stack.

Perhaps there's something I can do in Activity.onNewIntent to force the activity to clone itself then.

I'm on the verge of giving up on SearchView and just writing my own custom widget, despite that fact that "Beginning in Android 3.0, using the SearchView widget as an item in the action bar is the preferred way to provide search in your app" (Source) — so far, it seems just too inflexible and non-configurable to be useful in non-trivial cases.

Has anyone found a solution to this problem?

2条回答
Summer. ? 凉城
2楼-- · 2019-02-14 01:19

After a lot of reflection and false starts, I realized that SearchView was simply the wrong thing for this task. Google designed SearchView with a very specific type of search activity in mind:

  • The user types in a search query.
  • The app pushes a new activity showing a list of results.

That's not the search model I was using; instead, I wanted to refine a list currently shown on the screen using the query — it was more of a filtering search than a synchronous query and response model. SearchView is not designed for that, so I went ahead and wrote my own widget (and bound it to the search menu item and the search button).

I'm surprised that no one had any comments on this question — usually Android questions generate a lot of discussion. I guess that this was was just too far out there.

查看更多
何必那么认真
3楼-- · 2019-02-14 01:40

I had the same problem as the OP, but have a solution that doesn't mean giving up on using SearchView. It's not the perfect solution, but it's fairly clean and seems to work well.

As the OP stated...

when I try to launch a new instance of an activity from its own class, Android didn't respect FLAG_ACTIVITY_NEW_TASK and didn't push a new task onto the stack

...so my solution is to go via an intermediary Activity. For this, I created a DummyActivity class which basically takes the original intent and forwards it on to your destination activity.

So, from my original activity, instead of calling...

Intent destinationIntent = new Intent(getActivity(), DestinationActivity.class);
startActivity(destinationIntent);

...I call...

/*
 * Create the intent as normal, but replace our destination Activity with DummyActivity
 */
Intent dummyIntent = new Intent(getActivity(), DummyActivity.class);

/*
 * This will make it so that if user clicks back from the next activity, they won't be shown DummyActivity
 */
dummyIntent.setFlags(Intent.FLAG_ACTIVITY_NO_HISTORY);

/*
 * Tell DummyActivity about the destination Activity
 */
dummyIntent.putExtra(DummyActivity.EXTRA_DESTINATION_ACTIVITY, YourDestinationActivity.class); //IMPORTANT - the destination Activity must implement Serializable

/*
 * If you want, you can add any other extras to dummyIntent (which will be passed through to the destination activity). Launch DummyActivity.
 */
startActivity(dummyIntent);

In my manifest, DesinationActivity is defined as singleTop.

And the only other thing you should need is the DummyActivity class which shouldn't need any major customising...

import android.app.Activity;
import android.content.Intent;
import android.os.Bundle;

public class DummyActivity extends Activity {

    public static final String EXTRA_DESTINATION_ACTIVITY = "uk.co.mobilewebexpert.EXTRA_DESTINATION_ACTIVITY";

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

        Intent intent = getIntent();

        /*
         * Get destination activity info
         */
        @SuppressWarnings("unchecked")
        Class<Activity> destinationActivity = (Class<Activity>) intent.getSerializableExtra(EXTRA_DESTINATION_ACTIVITY);
        Bundle destinationActivityExtras = intent.getExtras();

        /*
         * Launch destination activity
         */
        Intent destinationActivityIntent = new Intent(this, destinationActivity);
        destinationActivityIntent.putExtras(destinationActivityExtras);
        destinationActivityIntent.setFlags(Intent.FLAG_ACTIVITY_NEW_TASK);
        startActivity(destinationActivityIntent);
    }

    /**
     * Override super.onNewIntent() so that calls to getIntent() will return the latest
     * intent, rather than the first intent.
     */
    @Override
    public void onNewIntent(Intent intent){
        super.onNewIntent(intent);
        setIntent(intent);
    }

}

Hopefully this will work for you, too. Suggestions welcomed. Cheers.

查看更多
登录 后发表回答