質問

I found interesting bug in Android Searchable dictionary sample application.

Follow these steps to reproduce it:

  • type "bo" in a search box
  • click go
  • click "bogus" from the results
  • when you see result and a definition, invoke search again
  • type "coa" which should give you "coalition"
  • Instead of that, nothing happens...

but this magically works :

  • type "bo" in a search box
  • select bogus from custom suggestion list
  • when you see result and a definition, invoke search again
  • type "coa" which should give you "coalition"
  • "coalition" is really found.

I'd like to ask you, why is this happening and how to repair it, so it'll function normally.

Here is some code regarding topic, I rather post more...

public class SearchableDictionary extends Activity {

private TextView mTextView;
private ListView mListView;

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

    mTextView = (TextView) findViewById(R.id.text);
    mListView = (ListView) findViewById(R.id.list);

    handleIntent(getIntent());
}

@Override
protected void onNewIntent(Intent intent) {
    // Because this activity has set launchMode="singleTop", the system calls this method
    // to deliver the intent if this activity is currently the foreground activity when
    // invoked again (when the user executes a search from this activity, we don't create
    // a new instance of this activity, so the system delivers the search intent here)
    handleIntent(intent);
}

private void handleIntent(Intent intent) {
    if (Intent.ACTION_VIEW.equals(intent.getAction())) {
        // handles a click on a search suggestion; launches activity to show word
        Intent wordIntent = new Intent(this, WordActivity.class);
        wordIntent.setData(intent.getData());
        startActivity(wordIntent);
    } else if (Intent.ACTION_SEARCH.equals(intent.getAction())) {
        // handles a search query
        String query = intent.getStringExtra(SearchManager.QUERY);
        showResults(query);
    }
}

/**
 * Searches the dictionary and displays results for the given query.
 * @param query The search query
 */
private void showResults(String query) {

    Cursor cursor = managedQuery(DictionaryProvider.CONTENT_URI, null, null,
                            new String[] {query}, null);

    if (cursor == null) {
        // There are no results
        mTextView.setText(getString(R.string.no_results, new Object[] {query}));
    } else {
        // Display the number of results
        int count = cursor.getCount();
        String countString = getResources().getQuantityString(R.plurals.search_results,
                                count, new Object[] {count, query});
        mTextView.setText(countString);

        // Specify the columns we want to display in the result
        String[] from = new String[] { DictionaryDatabase.KEY_WORD,
                                       DictionaryDatabase.KEY_DEFINITION };

        // Specify the corresponding layout elements where we want the columns to go
        int[] to = new int[] { R.id.word,
                               R.id.definition };

        // Create a simple cursor adapter for the definitions and apply them to the ListView
        SimpleCursorAdapter words = new SimpleCursorAdapter(this,
                                      R.layout.result, cursor, from, to);
        mListView.setAdapter(words);

        // Define the on-click listener for the list items
        mListView.setOnItemClickListener(new OnItemClickListener() {

            @Override
            public void onItemClick(AdapterView<?> parent, View view, int position, long id) {
                // Build the Intent used to open WordActivity with a specific word Uri
                Intent wordIntent = new Intent(getApplicationContext(), WordActivity.class);
                Uri data = Uri.withAppendedPath(DictionaryProvider.CONTENT_URI,
                                                String.valueOf(id));
                wordIntent.setData(data);
                startActivity(wordIntent);
            }
        });
    }
}

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

    if(Build.VERSION.SDK_INT >= Build.VERSION_CODES.HONEYCOMB){
        SearchManager searchManager = (SearchManager) getSystemService(Context.SEARCH_SERVICE);
        SearchView searchView = (SearchView) menu.findItem(R.id.search).getActionView();
        searchView.setSearchableInfo(searchManager.getSearchableInfo(getComponentName()));
        searchView.setIconifiedByDefault(false);
    }

    return true;
}

@Override
public boolean onOptionsItemSelected(MenuItem item) {
    switch (item.getItemId()) {
        case R.id.search:
            onSearchRequested();
            return true;
        default:
            return false;
    }
}
}

And this activity presents results:

public class WordActivity extends Activity {
@Override
protected void onCreate(Bundle savedInstanceState) {
    super.onCreate(savedInstanceState);
    setContentView(R.layout.word);

    if(Build.VERSION.SDK_INT >= Build.VERSION_CODES.HONEYCOMB){
        ActionBar actionBar = getActionBar();
        actionBar.setDisplayHomeAsUpEnabled(true);
    }

    Uri uri = getIntent().getData();
    Cursor cursor = managedQuery(uri, null, null, null, null);

    if (cursor == null) {
        finish();
    } else {
        cursor.moveToFirst();

        TextView word = (TextView) findViewById(R.id.word);
        TextView definition = (TextView) findViewById(R.id.definition);

        int wIndex = cursor.getColumnIndexOrThrow(DictionaryDatabase.KEY_WORD);
        int dIndex = cursor.getColumnIndexOrThrow(DictionaryDatabase.KEY_DEFINITION);

        word.setText(cursor.getString(wIndex));
        definition.setText(cursor.getString(dIndex));
    }
}

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

    if(Build.VERSION.SDK_INT >= Build.VERSION_CODES.HONEYCOMB){
        SearchManager searchManager = (SearchManager) getSystemService(Context.SEARCH_SERVICE);
        SearchView searchView = (SearchView) menu.findItem(R.id.search).getActionView();
        searchView.setSearchableInfo(searchManager.getSearchableInfo(getComponentName()));
        searchView.setIconifiedByDefault(false);
    }

    return true;
}

@Override
public boolean onOptionsItemSelected(MenuItem item) {
    switch (item.getItemId()) {
        case R.id.search:
            onSearchRequested();
            return true;
        case android.R.id.home:
            Intent intent = new Intent(this, SearchableDictionary.class);
            intent.addFlags(Intent.FLAG_ACTIVITY_CLEAR_TOP);
            startActivity(intent);
            return true;
        default:
            return false;
    }
}
}
役に立ちましたか?

解決

I think I have figured out the error. Well, it's not necessarily an error, but just a different way of implementing it. This implementation makes it so you can only do ACTION_SEARCH (non-suggestions) in the SearchableActivity. It took me many days to figure this out too because it was hard to replicate. It has to do with singleTop launchMode for SearchableActivity. The problem is that when you search with ACTION_SEARCH (non-suggestions) in WordActivity, it does not set FLAG_ACTIVITY_CLEAR_TOP with the ACTION_SEARCH intent that it sends to SearchableActivity onHandleIntent. This makes you get the error that is like ActivityManager START SearchableActivity but never ActivityManager DISPLAY SearchableActivity. It never goes into the onCreate() when this error happens, which makes it a doozy to figure out how to solve it. In order to solve this problem, I think you would either have to delete singleTop to make it create another activity every time ACTION_SEARCH is done (and put handleIntent(Intent) method call in onCreate()), or override onSearchRequested()'s startSearch(..., ..., ..., ...) method to get the intent and set the intent flag. Of course, if you hit the back button though when using CLEAR_TOP, you can't go back to the activity though. I have yet to figure out how to do the latter though because I dunno how to override startSearch and get the intent :X

ライセンス: CC-BY-SA帰属
所属していません StackOverflow
scroll top