Turn AutoCompleteTextView into a SearchView in ActionBar instead

前端 未结 2 1598
北海茫月
北海茫月 2020-12-07 08:45

I have a AutoCompleteTextView which gives user auto-completion search result from Google Places API. Once I was done I discovered S

相关标签:
2条回答
  • 2020-12-07 09:17

    AutoCompleteTextView with google search Api

    your xml

      <AutoCompleteTextView
        android:id="@+id/main_omnibox_input"
        android:layout_width="match_parent"
        android:layout_height="match_parent"
        android:layout_gravity="center"
        android:background="@null" 
        android:hint="Search"  
        android:focusable="true"
        android:focusableInTouchMode="true"                                  
        android:selectAllOnFocus="true"
        android:singleLine="true" 
        android:textSize="@dimen/_14sdp" />
    

    your java code

      this.inputBox = (AutoCompleteTextView) findViewById(R.id.main_omnibox_input);
      inputBox.setAdapter(new SearchAutocompleteAdapter(SearchActivity.this, new SearchAutocompleteAdapter.OnSearchCommitListener() {
            @Override
            public void onSearchCommit(String text) {
                inputBox.setText(text);
                inputBox.setSelection(text.length());
            }
        }));
    
    
        this.inputBox.setOnItemClickListener(new OnItemClickListener() {
            public void onItemClick(AdapterView<?> adapterView, View view, int i, long j) {
                String charSequence = ((TextView) view.findViewById(android.R.id.text1)).getText().toString();
                inputBox.setText(Html.fromHtml(BrowserUnit.urlWrapper(charSequence)), BufferType.SPANNABLE);
                inputBox.setSelection(charSequence.length());
               // your code
               // updateAlbum(charSequence);
               // hideSoftInput(SearchActivity.this.inputBox);
            }
        });
    

    SearchAutocompleteAdapter

    public class SearchAutocompleteAdapter extends BaseAdapter implements Filterable {
    
    interface OnSearchCommitListener {
        void onSearchCommit(String text);
    }
    
    private final Context mContext;
    private final OnSearchCommitListener commitListener;
    private List<String> completions = new ArrayList<>();
    static final String searchCompleteUrl = "https://www.google.com/complete/search?client=firefox&q=%s";
    
    SearchAutocompleteAdapter(Context context, OnSearchCommitListener commitListener) {
        mContext = context;
        this.commitListener = commitListener;
    }
    
    @Override
    public int getCount() {
        return completions.size();
    }
    
    @Override
    public Object getItem(int position) {
        return completions.get(position);
    }
    
    @Override
    public long getItemId(int position) {
        return position;
    }
    
    @SuppressLint("ClickableViewAccessibility")
    @Override
    @SuppressWarnings("ConstantConditions")
    public View getView(final int position, View convertView, ViewGroup parent) {
        if (convertView == null) {
            LayoutInflater inflater = (LayoutInflater) mContext.getSystemService(Context.LAYOUT_INFLATER_SERVICE);
            convertView = inflater.inflate(android.R.layout.simple_dropdown_item_1line, parent, false);
        }
        TextView textview = convertView.findViewById(android.R.id.text1);
        textview.setText(completions.get(position));
        Drawable d = ContextCompat.getDrawable(mContext, R.drawable.icon_goarrowsmall);
        final int size = (int) TypedValue.applyDimension(TypedValue.COMPLEX_UNIT_DIP, 32, mContext.getResources().getDisplayMetrics());
        d.setBounds(0, 0, size, size);
        textview.setCompoundDrawables(null, null, d, null);
    
        textview.setOnTouchListener(new View.OnTouchListener() {
            @Override
            public boolean onTouch(View view, MotionEvent event) {
                if (event.getAction() != MotionEvent.ACTION_DOWN) {
                    return false;
                }
                TextView t = (TextView) view;
                if (event.getX() > t.getWidth() - t.getCompoundPaddingRight()) {
                    commitListener.onSearchCommit(getItem(position).toString());
                    return true;
                }
                return false;
            }
        });
        parent.setOnTouchListener(new View.OnTouchListener() {
            @Override
            public boolean onTouch(View view, MotionEvent event) {
                if (event.getX() > view.getWidth() - size * 2) {
                    return true;
                }
                return false;
            }
        });
        return convertView;
    }
    
    @Override
    public Filter getFilter() {
        return new Filter() {
            @Override
            protected FilterResults performFiltering(CharSequence constraint) {
                // Invoked on a worker thread
                FilterResults filterResults = new FilterResults();
                if (constraint != null) {
                    List<String> results = getCompletions(constraint.toString());
                    filterResults.values = results;
                    filterResults.count = results.size();
                }
                return filterResults;
            }
    
            @Override
            @SuppressWarnings("unchecked")
            protected void publishResults(CharSequence constraint, FilterResults results) {
                if (results != null && results.count > 0) {
                    completions = (List<String>) results.values;
                    notifyDataSetChanged();
                } else {
                    notifyDataSetInvalidated();
                }
            }
        };
    }
    
    private List<String> getCompletions(String text) {
        int total = 0;
        byte[] data = new byte[16384];
        try {
            URL url = new URL(URLUtil.composeSearchUrl(text, searchCompleteUrl, "%s"));
            HttpURLConnection urlConnection = (HttpURLConnection) url.openConnection();
            try {
                InputStream in = new BufferedInputStream(urlConnection.getInputStream());
                while (total <= data.length) {
                    int count = in.read(data, total, data.length - total);
                    if (count == -1) {
                        break;
                    }
                    total += count;
                }
                if (total == data.length) {
                    // overflow
                    return new ArrayList<>();
                }
            } finally {
                urlConnection.disconnect();
            }
        } catch (IOException e) {
            return new ArrayList<>();
        }
    
        JSONArray jsonArray;
        try {
            jsonArray = new JSONArray(new String(data, StandardCharsets.UTF_8));
        } catch (JSONException e) {
            return new ArrayList<>();
        }
        jsonArray = jsonArray.optJSONArray(1);
        if (jsonArray == null) {
            return new ArrayList<>();
        }
        final int MAX_RESULTS = 10;
        List<String> result = new ArrayList<>(Math.min(jsonArray.length(), MAX_RESULTS));
        for (int i = 0; i < jsonArray.length() && result.size() < MAX_RESULTS; i++) {
            String s = jsonArray.optString(i);
            if (s != null && !s.isEmpty()) {
                result.add(s);
            }
        }
        return result;
    }
    

    }

    0 讨论(0)
  • 2020-12-07 09:35

    To get Places Autocomplete API results in a SearchView, you'll first need a ContentProvider for the API.

    import android.app.SearchManager;
    import android.content.ContentProvider;
    import android.content.ContentValues;
    import android.content.UriMatcher;
    import android.database.Cursor;
    import android.database.MatrixCursor;
    import android.net.Uri;
    import android.provider.BaseColumns;
    import android.util.Log;
    
    public class PlacesSuggestionProvider extends ContentProvider {
        private static final String LOG_TAG = "ExampleApp";
    
        public static final String AUTHORITY = "com.example.google.places.search_suggestion_provider";
        public static final Uri CONTENT_URI = Uri.parse("content://" + AUTHORITY + "/search");
    
        // UriMatcher constant for search suggestions
        private static final int SEARCH_SUGGEST = 1;
    
        private static final UriMatcher uriMatcher;
    
        private static final String[] SEARCH_SUGGEST_COLUMNS = {
                BaseColumns._ID,
                SearchManager.SUGGEST_COLUMN_TEXT_1,
                SearchManager.SUGGEST_COLUMN_TEXT_2,
                SearchManager.SUGGEST_COLUMN_INTENT_DATA_ID
        };
    
        static {
            uriMatcher = new UriMatcher(UriMatcher.NO_MATCH);
            uriMatcher.addURI(AUTHORITY, SearchManager.SUGGEST_URI_PATH_QUERY, SEARCH_SUGGEST);
            uriMatcher.addURI(AUTHORITY, SearchManager.SUGGEST_URI_PATH_QUERY + "/*", SEARCH_SUGGEST);
        }
    
        @Override
        public int delete(Uri uri, String arg1, String[] arg2) {
            throw new UnsupportedOperationException();
        }
    
        @Override
        public String getType(Uri uri) {
            switch (uriMatcher.match(uri)) {
                case SEARCH_SUGGEST:
                    return SearchManager.SUGGEST_MIME_TYPE;
                default:
                    throw new IllegalArgumentException("Unknown URL " + uri);
            }
        }
    
        @Override
        public Uri insert(Uri uri, ContentValues arg1) {
            throw new UnsupportedOperationException();
        }
    
        @Override
        public boolean onCreate() {
            return true;
        }
    
        @Override
        public Cursor query(Uri uri, String[] projection, String selection, String[] selectionArgs,
                String sortOrder) {
            Log.d(LOG_TAG, "query = " + uri);
    
            // Use the UriMatcher to see what kind of query we have
            switch (uriMatcher.match(uri)) {
                case SEARCH_SUGGEST:
                    Log.d(LOG_TAG, "Search suggestions requested.");
                    MatrixCursor cursor = new MatrixCursor(SEARCH_SUGGEST_COLUMNS, 1);
                    cursor.addRow(new String[] {
                            "1", "Search Result", "Search Result Description", "content_id"
                    });
                    return cursor;
                default:
                    throw new IllegalArgumentException("Unknown Uri: " + uri);
            }
        }
    
        @Override
        public int update(Uri uri, ContentValues arg1, String arg2, String[] arg3) {
            throw new UnsupportedOperationException();
        }
    }
    

    Then add your Places Autocomplete API client code into the query method on the content provider. You extract the user input as follows:

    String query = uri.getLastPathSegment().toLowerCase();
    

    Add the PlacesSuggestionProvider to your AndroidManifest, and make sure your activity has a searchable configuration.

        <application
            android:icon="@drawable/ic_launcher"
            android:label="@string/app_name" >
    
            <activity android:name=".PlacesSearchViewActivity" >
                <intent-filter>
                    <action android:name="android.intent.action.SEARCH" />
                    <action android:name="android.intent.action.MAIN" />
    
                    <category android:name="android.intent.category.LAUNCHER" />
                </intent-filter>
    
                <meta-data
                    android:name="android.app.searchable"
                    android:resource="@xml/searchable" />
            </activity>
    
            <provider
                android:name="com.example.google.places.PlacesSuggestionProvider"
                android:authorities="com.example.google.places.search_suggestion_provider"
                android:syncable="false" />
        </application>
    
    </manifest>
    

    And make sure your searchable configuration (res/xml/searchable.xml) has a search suggest authority.

    <?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"
        android:searchSuggestAuthority="com.example.google.places.search_suggestion_provider">
    </searchable>
    

    The authority should be the same in AndroidManifest.xml, searchable.xml, and your content provider.

    Create a options menu for your ActionBar that includes a SearchView (/res/menu/options_menu.xml).

    <menu xmlns:android="http://schemas.android.com/apk/res/android">
        <item android:id="@+id/menu_search"
              android:title="@string/menu_search"
              android:icon="@drawable/ic_menu_search"
              android:showAsAction="collapseActionView|ifRoom"
              android:actionViewClass="android.widget.SearchView" />
    </menu>
    

    Configure your Activity with a SearchView that's associated with your searchable configuration/

    @Override
    public boolean onCreateOptionsMenu(Menu menu) {
        // Inflate the options menu from XML
        MenuInflater inflater = getMenuInflater();
        inflater.inflate(R.menu.options_menu, menu);
    
        SearchManager searchManager = (SearchManager) getSystemService(Context.SEARCH_SERVICE);
        SearchView searchView = (SearchView) menu.findItem(R.id.menu_search).getActionView();
        // Tells your app's SearchView to use this activity's searchable configuration
        searchView.setSearchableInfo(searchManager.getSearchableInfo(getComponentName()));
        searchView.setIconifiedByDefault(false); // Do not iconify the widget; expand it by default
    
        return true;
    }
    

    A few key docs are:

    Adding Custom Suggestions: http://developer.android.com/guide/topics/search/adding-custom-suggestions.html

    Creating a Content Provider: http://developer.android.com/guide/topics/providers/content-provider-creating.html

    Using a Search Widget: http://developer.android.com/guide/topics/search/search-dialog.html#UsingSearchWidget

    0 讨论(0)
提交回复
热议问题