与 Uber 一样,在 ListView 中显示带有 Google Places 的 AutoCompleteTextView

Posted

技术标签:

【中文标题】与 Uber 一样,在 ListView 中显示带有 Google Places 的 AutoCompleteTextView【英文标题】:AutoCompleteTextView with Google Places shown in ListView just like Uber 【发布时间】:2015-08-01 18:19:37 【问题描述】:

我需要制作与此类似的屏幕。我认为它有 autocompletetextview 和 listview 来显示返回的结果。此处使用 Google Place API 自动建议地点,并相应更新列表视图适配器。 请任何形式的帮助表示赞赏。 提前致谢。

也检查了 AutoComplete 上的 android 示例项目的位置。但它没有任何列表视图来显示结果。相反,它会在 autocompletetextview 微调器中显示结果。我们可以对该项目进行的任何修改

Link to google sample project

【问题讨论】:

试试这个链接wptrafficanalyzer.in/blog/… 也发布您的代码以展示您的努力 @AnjaliTripathi 也检查了这个链接。他们都运作良好。但是你可以看到他们的下拉菜单来显示结果。我需要像屏幕截图一样在列表视图中显示它。谢谢 @RahulSood 如果符合您的要求,请接受答案。 【参考方案1】:

您完全可以通过使用EditTextListView 而不是AutoCompleteTextView 来实现这一点。在EditText 中输入字符,在此基础上通过调用GooglePlacesAutomplete 网络服务过滤ListView 中的结果。以下是代码:

这是您的布局文件(EditTextListView

<RelativeLayout xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:tools="http://schemas.android.com/tools"
xmlns:app="http://schemas.android.com/apk/res-auto"
android:layout_
android:layout_
android:background="#ffffff"
tools:context="com.example.siddarthshikhar.liftsharesample.EnterLocationActivity">

    <EditText
        android:paddingLeft="@dimen/activity_horizontal_margin"
        android:layout_
        android:layout_
        android:textColorHint="#ffffff"
        android:id="@+id/edEnterLocation"
        android:textColor="#ffffff"
        android:textSize="@dimen/abc_text_size_medium_material"
        android:layout_alignParentLeft="true"
        android:backgroundTint="#00000000"
        android:gravity="start|center">
        <requestFocus />
    </EditText>

<ListView android:id="@+id/listView1" android:layout_
    android:layout_
    android:layout_below="@+id/filterLayout"/>

</RelativeLayout>

在您相应的 Activity 中,访问此 EditText 并应用 Filterable。为此,您必须使用 GooglePlacesAutompleteAdapter

以下是GooglePlacesAutompleteAdapter

public class GooglePlacesAutocompleteAdapter extends ArrayAdapter implements Filterable 
private static final String LOG_TAG = "Google Places Autocomplete";
private static final String PLACES_API_BASE = "https://maps.googleapis.com/maps/api/place";
private static final String TYPE_AUTOCOMPLETE = "/autocomplete";
private static final String OUT_JSON = "/json";
private static final String API_KEY = "your_api_key";
private ArrayList<String> resultList;
private Context context = null;
public GooglePlacesAutocompleteAdapter(Context context, int textViewResourceId) 
    super(context, textViewResourceId);
    this.context = context;



@Override
public int getCount() 
    if(resultList != null)
        return resultList.size();
    else
        return 0;


@Override
public String getItem(int index) 
    return resultList.get(index);



public ArrayList<String> autocomplete(String input) 
    ArrayList<String> resultList = null;
    ArrayList<String> descriptionList = null;
    HttpURLConnection conn = null;
    StringBuilder jsonResults = new StringBuilder();
    try 
        StringBuilder sb = new StringBuilder(PLACES_API_BASE + TYPE_AUTOCOMPLETE + OUT_JSON);
        sb.append("?key=" + API_KEY);
        sb.append("&components=country:in");
        sb.append("&input=" + URLEncoder.encode(input, "utf8"));

        URL url = new URL(sb.toString());
        conn = (HttpURLConnection) url.openConnection();
        InputStreamReader in = new InputStreamReader(conn.getInputStream());

        // Load the results into a StringBuilder
        int read;
        char[] buff = new char[1024];
        while ((read = in.read(buff)) != -1) 
            jsonResults.append(buff, 0, read);
        
     catch (MalformedURLException e) 
        Log.e(LOG_TAG, "Error processing Places API URL", e);
        return resultList;
     catch (IOException e) 
        Log.e(LOG_TAG, "Error connecting to Places API", e);
        return resultList;
     finally 
        if (conn != null) 
            conn.disconnect();
        
    

    try 
        // Create a JSON object hierarchy from the results
        Log.d("yo",jsonResults.toString());
        JSONObject jsonObj = new JSONObject(jsonResults.toString());
        JSONArray predsJsonArray = jsonObj.getJSONArray("predictions");

        // Extract the Place descriptions from the results
        resultList = new ArrayList(predsJsonArray.length());
        descriptionList = new ArrayList(predsJsonArray.length());
        for (int i = 0; i < predsJsonArray.length(); i++) 
            resultList.add(predsJsonArray.getJSONObject(i).toString());
            descriptionList.add(predsJsonArray.getJSONObject(i).getString("description"));
        
        saveArray(resultList.toArray(new String[resultList.size()]), "predictionsArray", getContext());
     catch (JSONException e) 
        Log.e(LOG_TAG, "Cannot process JSON results", e);
    

    return descriptionList;



@Override
public Filter getFilter() 
    Filter filter = new Filter() 
        @Override
        protected FilterResults performFiltering(CharSequence constraint) 
            FilterResults filterResults = new FilterResults();
            if (constraint != null) 
                // Retrieve the autocomplete results.
                resultList = autocomplete(constraint.toString());

                // Assign the data to the FilterResults
                filterResults.values = resultList;
                filterResults.count = resultList.size();
            
            return filterResults;
        

        @Override
        protected void publishResults(CharSequence constraint, FilterResults results) 
            if (results != null && results.count > 0) 
                setImageVisibility();
                notifyDataSetChanged();
             else 
                notifyDataSetInvalidated();
            
        
    ;
    return filter;


访问适配器并将getFilter()应用于对应Activity中的EditText。在你之前创建的布局对应的 Activity 中添加以下内容:

dataAdapter = new   GooglePlacesAutocompleteAdapter(EnterLocationActivity.this, R.layout.adapter_google_places_autocomplete)

listView = (ListView) findViewById(R.id.listView1);
    // Assign adapter to ListView
    listView.setAdapter(dataAdapter);

    //enables filtering for the contents of the given ListView
    listView.setTextFilterEnabled(true);

etEnterLocation.addTextChangedListener(new TextWatcher() 

        public void afterTextChanged(Editable s) 
        

        public void beforeTextChanged(CharSequence s, int start, int count, int after) 
        

        public void onTextChanged(CharSequence s, int start, int before, int count) 

            dataAdapter.getFilter().filter(s.toString());
        
    );

这应该会让你继续前进。您可以根据需要修改布局。这基本上会在ListView 中加载自动完成数据。

【讨论】:

我喜欢你的方法,但是 'saveArray' 有什么作用呢? @SteveKamau 嗨。这与这个答案无关。我正在使用“saveArray”存储我进入 SharedPref 的列表。感谢您的支持!【参考方案2】:

Here 是如何做到这一点的一个例子。

您可以从适配器获得任何AutocompletePrediction。例如,只需从AutoCompleteTextViewAdapterView.OnItemClickListener 中调用getItem(int position),然后根据需要使用点击预测中的任何数据。

链接相关代码:

/**
 * Adapter that handles Autocomplete requests from the Places Geo Data API.
 * @link AutocompletePrediction results from the API are frozen and stored directly in this
 * adapter. (See @link AutocompletePrediction#freeze().)
 * <p>
 * Note that this adapter requires a valid @link com.google.android.gms.common.api.GoogleApiClient.
 * The API client must be maintained in the encapsulating Activity, including all lifecycle and
 * connection states. The API client must be connected with the @link Places#GEO_DATA_API API.
 */
public class PlaceAutocompleteAdapter
        extends ArrayAdapter<AutocompletePrediction> implements Filterable 

    private static final String TAG = "PlaceAutocompleteAdapter";
    private static final CharacterStyle STYLE_BOLD = new StyleSpan(Typeface.BOLD);
    /**
     * Current results returned by this adapter.
     */
    private ArrayList<AutocompletePrediction> mResultList;

    /**
     * Handles autocomplete requests.
     */
    private GoogleApiClient mGoogleApiClient;

    /**
     * The bounds used for Places Geo Data autocomplete API requests.
     */
    private LatLngBounds mBounds;

    /**
     * The autocomplete filter used to restrict queries to a specific set of place types.
     */
    private AutocompleteFilter mPlaceFilter;

    /**
     * Initializes with a resource for text rows and autocomplete query bounds.
     *
     * @see android.widget.ArrayAdapter#ArrayAdapter(android.content.Context, int)
     */
    public PlaceAutocompleteAdapter(Context context, GoogleApiClient googleApiClient,
            LatLngBounds bounds, AutocompleteFilter filter) 
        //change the layout nex for your own if you'd like
        super(context, android.R.layout.simple_expandable_list_item_2, android.R.id.text1);
        mGoogleApiClient = googleApiClient;
        mBounds = bounds;
        mPlaceFilter = filter;
    

    /**
     * Sets the bounds for all subsequent queries.
     */
    public void setBounds(LatLngBounds bounds) 
        mBounds = bounds;
    

    /**
     * Returns the number of results received in the last autocomplete query.
     */
    @Override
    public int getCount() 
        return mResultList.size();
    

    /**
     * Returns an item from the last autocomplete query.
     */
    @Override
    public AutocompletePrediction getItem(int position) 
        return mResultList.get(position);
    

    @Override
    public View getView(int position, View convertView, ViewGroup parent) 
        View row = super.getView(position, convertView, parent);

        // Sets the primary and secondary text for a row.
        // Note that getPrimaryText() and getSecondaryText() return a CharSequence that may contain
        // styling based on the given CharacterStyle.

        AutocompletePrediction item = getItem(position);

        TextView textView1 = (TextView) row.findViewById(android.R.id.text1);
        TextView textView2 = (TextView) row.findViewById(android.R.id.text2);
        textView1.setText(item.getPrimaryText(STYLE_BOLD));
        textView2.setText(item.getSecondaryText(STYLE_BOLD));

        return row;
    

    /**
     * Returns the filter for the current set of autocomplete results.
     */
    @Override
    public Filter getFilter() 
        return new Filter() 
            @Override
            protected FilterResults performFiltering(CharSequence constraint) 
                FilterResults results = new FilterResults();

                // We need a separate list to store the results, since
                // this is run asynchronously.
                ArrayList<AutocompletePrediction> filterData = new ArrayList<>();

                // Skip the autocomplete query if no constraints are given.
                if (constraint != null) 
                    // Query the autocomplete API for the (constraint) search string.
                    filterData = getAutocomplete(constraint);
                

                results.values = filterData;
                if (filterData != null) 
                    results.count = filterData.size();
                 else 
                    results.count = 0;
                

                return results;
            

            @Override
            protected void publishResults(CharSequence constraint, FilterResults results) 

                if (results != null && results.count > 0) 
                    // The API returned at least one result, update the data.
                    mResultList = (ArrayList<AutocompletePrediction>) results.values;
                    notifyDataSetChanged();
                 else 
                    // The API did not return any results, invalidate the data set.
                    notifyDataSetInvalidated();
                
            

            @Override
            public CharSequence convertResultToString(Object resultValue) 
                // Override this method to display a readable result in the AutocompleteTextView
                // when clicked.
                if (resultValue instanceof AutocompletePrediction) 
                    return ((AutocompletePrediction) resultValue).getFullText(null);
                 else 
                    return super.convertResultToString(resultValue);
                
            
        ;
    

    /**
     * Submits an autocomplete query to the Places Geo Data Autocomplete API.
     * Results are returned as frozen AutocompletePrediction objects, ready to be cached.
     * objects to store the Place ID and description that the API returns.
     * Returns an empty list if no results were found.
     * Returns null if the API client is not available or the query did not complete
     * successfully.
     * This method MUST be called off the main UI thread, as it will block until data is returned
     * from the API, which may include a network request.
     *
     * @param constraint Autocomplete query string
     * @return Results from the autocomplete API or null if the query was not successful.
     * @see Places#GEO_DATA_API#getAutocomplete(CharSequence)
     * @see AutocompletePrediction#freeze()
     */
    private ArrayList<AutocompletePrediction> getAutocomplete(CharSequence constraint) 
        if (mGoogleApiClient.isConnected()) 
            Log.i(TAG, "Starting autocomplete query for: " + constraint);

            // Submit the query to the autocomplete API and retrieve a PendingResult that will
            // contain the results when the query completes.
            PendingResult<AutocompletePredictionBuffer> results =
                    Places.GeoDataApi
                            .getAutocompletePredictions(mGoogleApiClient, constraint.toString(),
                                    mBounds, mPlaceFilter);

            // This method should have been called off the main UI thread. Block and wait for at most 60s
            // for a result from the API.
            AutocompletePredictionBuffer autocompletePredictions = results
                    .await(60, TimeUnit.SECONDS);

            // Confirm that the query completed successfully, otherwise return null
            final Status status = autocompletePredictions.getStatus();
            if (!status.isSuccess()) 
                Toast.makeText(getContext(), "Error contacting API: " + status.toString(),
                        Toast.LENGTH_SHORT).show();
                Log.e(TAG, "Error getting autocomplete prediction API call: " + status.toString());
                autocompletePredictions.release();
                return null;
            

            Log.i(TAG, "Query completed. Received " + autocompletePredictions.getCount()
                    + " predictions.");

            // Freeze the results immutable representation that can be stored safely.
            return DataBufferUtils.freezeAndClose(autocompletePredictions);
        
        Log.e(TAG, "Google API client is not connected for autocomplete query.");
        return null;
    

【讨论】:

【参考方案3】:

您可以通过将以下代码添加到布局中以更简单的方式实现自动完成文本视图

<fragment
     android:id="@+id/place_autocomplete_fragment"
     android:layout_
     android:layout_
android:name="com.google.android.gms.location.places.ui.PlaceAutocompleteFragment"
/>

要使用上述代码,您需要在 Google 开发者控制台中为您的应用程序配置一些设置。完整示例请参考 Android Places autocomplete example

【讨论】:

以上是关于与 Uber 一样,在 ListView 中显示带有 Google Places 的 AutoCompleteTextView的主要内容,如果未能解决你的问题,请参考以下文章

Android 利用ListView制作带竖线的多彩表格

制作带复选框的ListView控件

如何定义地理边界并限制应用程序的使用,就像在 Uber 中一样

android - 搜索列表视图?

iOS 实时位置跟踪器就像在 uber 中一样?

C# ListView 设计