2012-10-31 6 views
5

Ich benutze einen benutzerdefinierten ArrayAdapter, um den Adapter auf eine AutocompleteTextView (AddressAdapter erweitert ArrayAdapter) festzulegen.IllegalStateException: Der Inhalt des Adapters hat sich geändert, aber ListView hat keine Benachrichtigung erhalten

public class AutoCompleteAdapter extends ArrayAdapter<String> implements Filterable { 
private ArrayList<String> mData; 
ArrayList<String> listTempPrefix = new ArrayList<String>(); 
ArrayList<String> listTemp = new ArrayList<String>(); 
String valueText; 
String[] words; 
String ulcase; 

public AutoCompleteAdapter(Context context, int textViewResourceId, ArrayList<String> bS) { 
    super(context, textViewResourceId); 
    mData = bS;//new ArrayList<String>(); 
} 

@Override 
public int getCount() 
{ 
    synchronized (listTempPrefix) 
    { 
     return listTempPrefix.size(); 
    } 
} 

@Override 
public String getItem(int index) 
{ 
    synchronized (listTempPrefix) 
    { 
     try { 
      //Log.e("Error", listTempPrefix.get(index)); 
      return listTempPrefix.get(index); 
     } catch(IndexOutOfBoundsException e) { 
      Log.e("Error", "IndexOutOfBoundsException"); 
      return ""; 
     } 
    } 

} 

@Override 
public Filter getFilter() 
{ 
    Filter myFilter = new Filter() { 
     @Override 
     protected FilterResults performFiltering(CharSequence constraint) 
     { 

      FilterResults filterResults = new FilterResults(); 
      synchronized (filterResults) 
      { 
       listTempPrefix.clear(); 
       listTemp.clear(); 
       //Log.e("1", "1"); 

       try { 
       if(constraint != null) { 
        // A class that queries a web API, parses the data and returns an ArrayList<Style> 
        //StyleFetcher fetcher = new StyleFetcher(); 
        //try { 
         //mData = fetcher.retrieveResults(constraint.toString()); 
        //} 
        //catch(Exception e) {} 
        // Now assign the values and count to the FilterResults object 


        for(String value: mData) { 
         valueText = value.toLowerCase(); 

         //System.out.println("constraintH - " + constraint); 

         constraint.toString().toLowerCase(); 
         ulcase = constraint.toString().toLowerCase(); 
         //System.out.println("ulcase - " + ulcase); 

         if (valueText.startsWith(ulcase)) { 
          listTempPrefix.add(value); 
         } else { 
          words = valueText.split(" "); 
          //final int wordCount = words.length; 

          // Start at index 0, in case valueText starts with space(s) 
          for (int k = 0; k < words.length; k++) { 
           if (words[k].startsWith(ulcase)) { 
            listTemp.add(value); 
            break; 
           } 
          } 
         } 

         ///listTemp.add(mData.get(i)); 
         //filterResults.count = mData.size(); 
      //   System.out.println("mData" + i + mData.get(i)); 
        } 
        //Log.e("2", "2"); 
      //  System.out.println("size " + listTemp.size() + " value" + listTemp); 

        listTempPrefix.addAll(listTemp); 

        filterResults.values = listTempPrefix; 

        filterResults.count = listTempPrefix.size(); 
        //System.out.println("size " + filterResults.count + " value" + filterResults.values); 

        //System.out.println("constraint" + constraint); 

       } 
       } catch (Exception e) { 
        // TODO Auto-generated catch block 
        e.printStackTrace(); 
        } 
       return filterResults; 
      } 
     } 

     @Override 
     protected void publishResults(CharSequence contraint, FilterResults filterResults) 
     { 
      synchronized (filterResults) 
      { 
       if(filterResults != null && filterResults.count > 0) { 
       notifyDataSetChanged(); 
       //Log.e("notifyDataSetChanged", "notifyDataSetChanged"); 
       } 
       else { 
        notifyDataSetInvalidated(); 
        //Log.e("notifyDataSetInvalidated", "notifyDataSetInvalidated"); 
       } 
      } 
     } 
    }; 
    return myFilter; 
} 

}

Was ich manchmal bekam: Bitte beachten Sie, es passiert wirklich selten. Aber ich möchte diesen Bug komplett beseitigen. Hier ist teilweise Stacktrace:

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. [in ListView(-1, class android.widget.AutoCompleteTextView$DropDownListView) with Adapter(class com.example.test.AutoCompleteAdapter)]. 

Das Problem vielleicht, dass die schnelle Eingabe von keybord, Methode notifyDataSetChanged() issn't Anruf. Aber ich bin mir nicht sicher.

Antwort

0

Sie müssen die notifyDataSetChanged() Methode, sobald die Datenmenge aufrufen (ArrayList, Array, Cursor etc.) Sichern Ihrer Adapter Änderungen. Oder sonst wirst du mit dieser Exception enden.

+0

Könnten Sie schreiben eine Probe, wie die Illegal zu bekommen? – user1528799

7

Sie ändern das Array listTempPrefix auf performFiltering (mit clear und addAll), so müssten Sie notifyDataSetChanged anrufen, um diese Ausnahme zu vermeiden.

Aber performFiltering wird auf dem ui-Thread nicht aufgerufen, so würde das Aufrufen von notifyDataSetChanged auch eine Ausnahme auslösen.

Der beste Weg, um dieses Problem zu lösen, ist das Ändern der listTempPrefix-Array innerhalb publishResults, und rufen Sie dann notifyDataSetChanged.

Entfernen Sie die an listTempPrefix vorgenommenen Änderungen von der performFiltering-Methode (möglicherweise müssen Sie abhängig von Ihrer Filterlogik ein temporäres Array erstellen).

Aktualisieren Sie unter publishResults Ihr Array listTempPrefix mit den Werten filterResults und rufen Sie notifyDataSetChanged an. Hier

ist ein exemple basierend auf Ihren Code:

@Override 
public Filter getFilter() 
{ 
    Filter myFilter = new Filter() { 
     @Override 
     protected FilterResults performFiltering(CharSequence constraint) 
     { 

      FilterResults filterResults = new FilterResults(); 
      synchronized (filterResults) 
      { 
       //listTempPrefix.clear(); // Don't change listTempPrefix here 
       ... 
         ulcase = constraint.toString().toLowerCase(); 
         //System.out.println("ulcase - " + ulcase); 

         if (valueText.startsWith(ulcase)) { 
          //listTempPrefix.add(value); // Don't change listTempPrefix 
          // To keep your logic you might need an aux array 
          // for this part 
         } else { 
          ... 
         } 

        //listTempPrefix.addAll(listTemp); // Don't change it 

        filterResults.values = listTempPrefix; // No problem here 

        filterResults.count = listTempPrefix.size(); // No problem here 
        //System.out.println("size " + filterResults.count + " value" + filterResults.values); 

        //System.out.println("constraint" + constraint); 

       } 
       } catch (Exception e) { 
        // TODO Auto-generated catch block 
        e.printStackTrace(); 
        } 
       return filterResults; 
      } 
     } 

     @Override 
     protected void publishResults(CharSequence contraint, FilterResults filterResults) 
     { 
      // At this point, make the changes you need to listTempPrefix 
      // using filterResults.values 
      synchronized (filterResults) 
      { 
       if(filterResults != null && filterResults.count > 0) { 
       notifyDataSetChanged(); 
       //Log.e("notifyDataSetChanged", "notifyDataSetChanged"); 
       } 
       else { 
        notifyDataSetInvalidated(); 
        //Log.e("notifyDataSetInvalidated", "notifyDataSetInvalidated"); 
       } 
      } 
     } 
    }; 
    return myFilter; 
} 
+0

Ich weiß nicht viel Englisch. Ich habe Ihren Punkt nicht ganz verstanden, Sie können ein Beispiel schreiben. – user1528799

+0

Bearbeitete meine Antwort mit etwas Code, sehen Sie, ob das hilft – Marcelo

+0

Ihr Beispiel hilft nicht ( – user1528799

9

ändern publishResults zu

@Override 
protected void publishResults(final CharSequence contraint, final FilterResults filterResults) { 
    listTempPrefix = (List) results.values; 
    if(filterResults != null && filterResults.count > 0) { 
     notifyDataSetChanged(); 
    } else { 
     notifyDataSetInvalidated(); 
    } 
} 

diese Weise wird der GUI-Thread die Ergebnisse statt performFiltering aktualisiert, die in einem Hintergrund-Thread ausgeführt wird.

Entfernen listTempPrefix Referenzen aus performFiltering und eine lokale Variable verwenden, da die Ergebnisse zu speichern und bringt sie durch FilterResults

+1

Perfekte Antwort, danke! Das IllegalStateException-Problem mit dem Demo-Code gelöst, den Google für Places Autocompletion unter https://developers.google.com/places/training/autocomplete-android bereitgestellt hat – Price