我收到以下崩溃报告:
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);
}
答案 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;
}