适用于Google Places API的PlaceAutocompleteAdapter中的IllegalStateException

时间:2016-04-27 10:58:37

标签: android android-adapter autocompletetextview

我收到以下崩溃报告:

Fatal Exception: java.lang.IllegalStateException: The content of the adapter has changed but ListView did not receive a notification. Make sure the content of your adapter is not modified from a background thread, but only from the UI thread. Make sure your adapter calls notifyDataSetChanged() when its content changes. [in ListView(-1, class android.widget.ListPopupWindow$DropDownListView) with Adapter(class com.galleri5.android.adapters.PlaceAutocompleteAdapter)]
       at android.widget.ListView.layoutChildren(ListView.java:1562)
       at android.widget.AbsListView.onLayout(AbsListView.java:2151)
       at android.view.View.layout(View.java:15689)
       at android.view.ViewGroup.layout(ViewGroup.java:5040)
       at android.widget.FrameLayout.layoutChildren(FrameLayout.java:579)
       at android.widget.FrameLayout.onLayout(FrameLayout.java:514)
       at android.view.View.layout(View.java:15689)
       at android.view.ViewGroup.layout(ViewGroup.java:5040)
       at android.view.ViewRootImpl.performLayout(ViewRootImpl.java:2116)
       at android.view.ViewRootImpl.performTraversals(ViewRootImpl.java:1873)
       at android.view.ViewRootImpl.doTraversal(ViewRootImpl.java:1084)
       at android.view.ViewRootImpl$TraversalRunnable.run(ViewRootImpl.java:5990)
       at android.view.Choreographer$CallbackRecord.run(Choreographer.java:767)
       at android.view.Choreographer.doCallbacks(Choreographer.java:580)
       at android.view.Choreographer.doFrame(Choreographer.java:550)
       at android.view.Choreographer$FrameDisplayEventReceiver.run(Choreographer.java:753)
       at android.os.Handler.handleCallback(Handler.java:739)
       at android.os.Handler.dispatchMessage(Handler.java:95)
       at android.os.Looper.loop(Looper.java:135)
       at android.app.ActivityThread.main(ActivityThread.java:5343)
       at java.lang.reflect.Method.invoke(Method.java)
       at java.lang.reflect.Method.invoke(Method.java:372)
       at com.android.internal.os.ZygoteInit$MethodAndArgsCaller.run(ZygoteInit.java:905)
       at com.android.internal.os.ZygoteInit.main(ZygoteInit.java:700)

这是我的PlaceAutoCompleteAdapter

public class PlaceAutocompleteAdapter
        extends ArrayAdapter<AutocompletePrediction> implements Filterable {
    protected final String TAG = getClass().getSimpleName();
    private static final CharacterStyle STYLE_BOLD = new StyleSpan(Typeface.BOLD);
    private ArrayList<AutocompletePrediction> mResultList;
    private GoogleApiClient mGoogleApiClient;
    private LatLngBounds mBounds;
    private AutocompleteFilter mPlaceFilter;

    public PlaceAutocompleteAdapter(Context context, GoogleApiClient googleApiClient,
                                    LatLngBounds bounds, AutocompleteFilter filter) {
        super(context, android.R.layout.simple_expandable_list_item_2, android.R.id.text1);
        mGoogleApiClient = googleApiClient;
        mBounds = bounds;
        mPlaceFilter = filter;
    }

    public void setBounds(LatLngBounds bounds) {
        mBounds = bounds;
    }

    @Override
    public int getCount() {
        return mResultList != null ? mResultList.size() : 0;
    }

    @Override
    public AutocompletePrediction getItem(int position) {
        return position < mResultList.size() ? mResultList.get(position) : null;
    }

    @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);
        if((item != null) && (row != null)) {
            row.setBackgroundColor(Color.parseColor("#707070"));
            TextView textView1 = (TextView) row.findViewById(android.R.id.text1);
            textView1.setTextSize(14);
            TextView textView2 = (TextView) row.findViewById(android.R.id.text2);
            textView2.setTextSize(12);
            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();
                // Skip the autocomplete query if no constraints are given.
                if (constraint != null) {
                    // Query the autocomplete API for the (constraint) search string.
                    mResultList = getAutocomplete(constraint);
                    if ((mResultList != null) && (!mResultList.isEmpty())) {
                        // The API successfully returned results.
                        results.values = mResultList;
                        results.count = mResultList.size();
                    }
                }
                return results;
            }

            @Override
            protected void publishResults(CharSequence constraint, FilterResults results) {
                if (results != null) {
                    // The API returned at least one result, update the data.
                    if (results.count > 0) {
                        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);
                }
            }
        };
    }

    private ArrayList<AutocompletePrediction> getAutocomplete(CharSequence constraint) {
        if (mGoogleApiClient.isConnected()) {

            // 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();
                autocompletePredictions.release();
                return null;
            }

            // Freeze the results immutable representation that can be stored safely.
            return DataBufferUtils.freezeAndClose(autocompletePredictions);
        }
        return null;
    }
}

此代码取自Google的地方api autocomplete样本。我修改它用于UI更改,否则,或多或少它是相同的。我无法弄清楚这次事故是如何发生的以及可能的原因是什么?任何帮助都将受到高度赞赏。

修改:以下是我正在设置适配器的Activity的相关部分:

@Bind(R.id.loc)
    AutoCompleteTextView loc;
protected GoogleApiClient mGoogleApiClient;

    private PlaceAutocompleteAdapter mAdapter;

    private static final LatLngBounds BOUNDS_GREATER_SYDNEY = new LatLngBounds(
            new LatLng(-34.041458, 150.790100), new LatLng(-33.682247, 151.383362));

@Override
onCreate() {
mGoogleApiClient = new GoogleApiClient.Builder(this)
                .enableAutoManage(this, 0 /* clientId */, this)
                .addApi(Places.GEO_DATA_API)
                .build();

loc.setOnItemClickListener(mAutocompleteClickListener);
        mAdapter = new PlaceAutocompleteAdapter(this, mGoogleApiClient, BOUNDS_GREATER_SYDNEY, null);
        loc.setAdapter(mAdapter);
}

1 个答案:

答案 0 :(得分:1)

根据例外情况,您正在更改另一个线程中的列表内容。要解决此问题,您必须在UI线程中更改arraylist的内容。 从doc performFiltering方法运行在不同的线程中。要解决此问题,请在publishResults方法中将内容添加到arraylist并调用notifydatasetchange。

其他解决方案是:更新执行过滤,如下所示:

 protected FilterResults performFiltering(CharSequence constraint) {
                FilterResults results = new FilterResults();
                // Skip the autocomplete query if no constraints are given.
                if (constraint != null) {
                    final ArrayList<AutocompletePrediction> mResultList1 = getAutocomplete(constraint);

((Activity)mContext).runOnUiThread(new Runnable() {
            public void run() {

                    if ((mResultList1 != null) && (!mResultList1.isEmpty())) {
                        // The API successfully returned results.
                        results.values = mResultList1;
                        results.count = mResultList1.size();
                          mResultList.clear();
                           mResultList.addAll( mResultList1);
                    }
            }
        });
                }
                return results;
            }

For reference