web-dev-qa-db-de.com

So aktualisieren Sie die Android-Listenansicht

Wie kann ein Android ListView nach dem Hinzufügen/Löschen dynamischer Daten aktualisiert werden?

407
UMAR

Rufen Sie notifyDataSetChanged() für Ihr Adapter-Objekt auf, sobald Sie die Daten in diesem Adapter geändert haben. 

In diesem Google-E/A-Video sind einige zusätzliche Informationen dazu enthalten, wie/wann notifyDataSetChanged() aufgerufen werden soll.

509
Christopher Orr

Auch Sie können dies verwenden:

myListView.invalidateViews();
197
Bobs

Bitte ignorieren Sie alle Antworten auf diese Frage invalidate(), invalidateViews(), requestLayout(), ....

Das Richtige (und glücklicherweise auch als richtige Antwort markiert) ist , um notifyDataSetChanged() auf Ihrem Adapter aufzurufen.

Fehlerbehebung

Wenn der Aufruf von notifyDataSetChanged() nicht funktioniert, helfen auch nicht alle Layout-Methoden. Glauben Sie mir, die ListView wurde ordnungsgemäß aktualisiert. Wenn Sie den Unterschied nicht finden, müssen Sie überprüfen, woher die Daten in Ihrem Adapter stammen.

Wenn es sich nur um eine Sammlung handelt, die Sie im Speicher aufbewahren, prüfen Sie, ob Sie die Elemente tatsächlich gelöscht oder der Sammlung hinzugefügt haben, bevor Sie notifyDataSetChanged() aufrufen. 

Wenn Sie mit einem Datenbank- oder Service-Backend arbeiten, müssen Sie die Methode aufrufen, um die Informationen erneut abzurufen (oder die Daten im Arbeitsspeicher zu bearbeiten), bevor Sie notifyDataSetChanged() aufrufen.

Die Sache ist, dass notifyDataSetChanged nur funktioniert, wenn sich die Datenmenge geändert hat. Das ist also der Ort, an dem Sie nachsehen, wenn Sie keine Änderungen feststellen. Bei Bedarf debuggen. 

ArrayAdapter vs. BaseAdapter

Ich fand, dass das Arbeiten mit einem Adapter, mit dem Sie die Sammlung verwalten können, wie ein BaseAdapter besser funktioniert. Einige Adapter wie der ArrayAdapter verwalten bereits ihre eigene Sammlung, wodurch es schwieriger wird, die richtige Sammlung für Updates aufzurufen. In den meisten Fällen ist es wirklich nur eine unnötige zusätzliche Schwierigkeitsstufe.

UI-Thread

Es ist richtig, dass dies vom UI-Thread aus aufgerufen werden muss. Andere Antworten enthalten Beispiele, wie dies erreicht werden kann. Dies ist jedoch nur erforderlich, wenn Sie an diesen Informationen außerhalb des UI-Threads arbeiten. Das ist von einem Dienst oder einem Nicht-UI-Thread. In einfachen Fällen aktualisieren Sie Ihre Daten mit einem Klick oder einem anderen Vorgang/Fragment. Also noch im UI-Thread. Es ist nicht nötig, das runOnUiTrhead immer in ein Popup-Fenster zu laden.

Schnelles Beispielprojekt

Kann unter https://github.com/hanscappelle/so-2250770.git gefunden werden. Einfach klonen und das Projekt in Android Studio (Gradle) öffnen. Dieses Projekt verfügt über eine MainAcitivity, die eine ListView mit allen zufälligen Daten aufbaut. Diese Liste kann mit dem Aktionsmenü aktualisiert werden. 

Die Adapterimplementierung, die ich für dieses Beispiel ModelObject erstellt habe, macht die Datenerfassung verfügbar

public class MyListAdapter extends BaseAdapter {

    /**
     * this is our own collection of data, can be anything we 
     * want it to be as long as we get the abstract methods 
     * implemented using this data and work on this data 
     * (see getter) you should be fine
     */
    private List<ModelObject> mData;

    /**
     * our ctor for this adapter, we'll accept all the things 
     * we need here
     *
     * @param mData
     */
    public MyListAdapter(final Context context, final List<ModelObject> mData) {
        this.mData = mData;
        this.mContext = context;
    }

    public List<ModelObject> getData() {
        return mData;
    }

    // implement all abstract methods here
}

Code aus der MainActivity 

public class MainActivity extends Activity {

    private MyListAdapter mAdapter;

    @Override
    protected void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        setContentView(R.layout.activity_main);

        ListView list = (ListView) findViewById(R.id.list);

        // create some dummy data here
        List<ModelObject> objects = getRandomData();
        // and put it into an adapter for the list
        mAdapter = new MyListAdapter(this, objects);
        list.setAdapter(mAdapter);

        // mAdapter is available in the helper methods below and the 
        // data will be updated based on action menu interactions

        // you could also keep the reference to the Android ListView 
        // object instead and use the {@link ListView#getAdapter()} 
        // method instead. However you would have to cast that adapter 
        // to your own instance every time
    }

    /**
     * helper to show what happens when all data is new
     */
    private void reloadAllData(){
        // get new modified random data
        List<ModelObject> objects = getRandomData();
        // update data in our adapter
        mAdapter.getData().clear();
        mAdapter.getData().addAll(objects);
        // fire the event
        mAdapter.notifyDataSetChanged();
    }

    /**
     * helper to show how only changing properties of data 
     * elements also works
     */
    private void scrambleChecked(){
        Random random = new Random();
        // update data in our adapter, iterate all objects and 
        // resetting the checked option
        for( ModelObject mo : mAdapter.getData()) {
            mo.setChecked(random.nextBoolean());
        }
        // fire the event
        mAdapter.notifyDataSetChanged();
    }
}

Mehr Informationen

Ein weiterer schöner Beitrag über die Leistungsfähigkeit von listViews ist hier zu finden: http://www.vogella.com/articles/AndroidListView/article.html

147
hcpl

Rufen Sie an, wann immer Sie wollen:

runOnUiThread(run);

OnCreate(), Sie setzen Ihren ausführbaren Thread:

run = new Runnable() {
    public void run() {
        //reload content
        arraylist.clear();
        arraylist.addAll(db.readAll());
        adapter.notifyDataSetChanged();
        listview.invalidateViews();
        listview.refreshDrawableState();
    }
};
42
Marckaraujo

ich habe einige Probleme mit der dynamischen Aktualisierung meiner Listenansicht.

Rufen Sie notifyDataSetChanged () auf Ihrem Adapter auf.

In diesem E/A-Video von Google können einige zusätzliche Details zum Aufruf von notifyDataSetChanged () angezeigt werden.

notifyDataSetChanged () funktionierte in meinem Fall nicht ordnungsgemäß [Ich habe notifyDataSetChanged von einer anderen Klasse aufgerufen]. Nur in dem Fall habe ich die ListView in der laufenden Aktivität (Thread) bearbeitet. Dieses Video dank Christopher gab den letzten Hinweis.

In meiner zweiten Klasse habe ich gebraucht

Runnable run = new Runnable(){
     public void run(){
         contactsActivity.update();
     }
};
contactsActivity.runOnUiThread(run);

um auf das Update () von meiner Aktivität zuzugreifen. Dieses Update enthält

myAdapter.notifyDataSetChanged();

um dem Adapter mitzuteilen, dass er die Ansicht auffrischen soll . Hat soweit gut funktioniert.

11
Wille

Wenn Sie SimpleCursorAdapter verwenden, versuchen Sie, requery () für das Cursor-Objekt aufzurufen.

9
freehacker

wenn Sie mit ListView Refreshment immer noch nicht zufrieden sind, können Sie sich dieses Snippet ansehen. Hierfür wird das ListView aus der DB geladen. Eigentlich müssen Sie einfach das ListView erneut laden, nachdem Sie eine CRUD-Operation ausgeführt haben Der beste Weg zum Code, aber die ListView wird nach Belieben aktualisiert.

Es funktioniert für mich .... wenn Sie eine bessere Lösung finden, bitte teilen ...

.......
......
 Führen Sie Ihre CRUD-Operationen aus ..
......
.....
 DBAdapter. open (); 
 DBAdapter.insert_into_SingleList (); 
 // Bring die DB_Ergebnisse und füge sie der Liste als Inhalt hinzu ....
 ls2.setAdapter (neuer ArrayAdapter (DynTABSample.this, 
 Android.R.layout.simple_list_item_1, DBAdapter.DB_ListView)); 
 DBAdapter.close (); 
5
Nandagopal T

Die von Personen in diesem Beitrag vorgeschlagenen Lösungen funktionieren oder hängen hauptsächlich von der Android-Version Ihres Geräts ab. Um beispielsweise die AddAll-Methode verwenden zu können, müssen Sie Android: minSdkVersion = "10" in Ihr Android-Gerät einfügen.

Um diese Fragen für alle Geräte zu lösen, habe ich meine eigene Methode in meinem Adapter erstellt und verwende in der Methode zum Hinzufügen und Entfernen von ArrayAdapter die Daten ohne Probleme zu aktualisieren.

Mein Code: Mit meiner eigenen Datenklasse RaceResult verwenden Sie Ihr eigenes Datenmodell.

ResultGpRowAdapter.Java

public class ResultGpRowAdapter extends ArrayAdapter<RaceResult> {

    Context context;
    int resource;
    List<RaceResult> data=null;

        public ResultGpRowAdapter(Context context, int resource, List<RaceResult> objects)           {
        super(context, resource, objects);

        this.context = context;
        this.resource = resource;
        this.data = objects;
    }

    @Override
    public View getView(int position, View convertView, ViewGroup parent) {

        ........
        }

        //my own method to populate data           
        public void myAddAll(List<RaceResult> items) {

        for (RaceResult item:items){
            super.add(item);
        }
    }

ErgebnisseGp.Java

public class ResultsGp extends Activity {

@Override
protected void onCreate(Bundle savedInstanceState) {

    ...........
    ...........
    ListView list = (ListView)findViewById(R.id.resultsGpList); 

    ResultGpRowAdapter adapter = new ResultGpRowAdapter(this,  R.layout.activity_result_gp_row, new ArrayList<RaceResult>()); //Empty data

   list.setAdapter(adapter);

   .... 
   ....
   ....
   //LOAD a ArrayList<RaceResult> with data

   ArrayList<RaceResult> data = new ArrayList<RaceResult>();
   data.add(new RaceResult(....));
   data.add(new RaceResult(....));
   .......

   adapter.myAddAll(data); //Your list will be udpdated!!!
2
AntuanSoft

Verwenden Sie einfach myArrayList.remove(position); in einem Listener:

  myListView.setOnItemClickListener(new AdapterView.OnItemClickListener() {
        @Override public void onItemClick(AdapterView<?> parent, Android.view.View view, int position, long id) {
           myArrayList.remove(position);
           myArrayAdapter.notifyDataSetChanged();
        }
    });
1
Steffo Dimfelt

Wenn Sie die Bildlaufposition bei der Aktualisierung beibehalten möchten, können Sie Folgendes tun:

if (mEventListView.getAdapter() == null) {
    EventLogAdapter eventLogAdapter = new EventLogAdapter(mContext, events);
    mEventListView.setAdapter(eventLogAdapter);
} else {
    ((EventLogAdapter)mEventListView.getAdapter()).refill(events);
}

public void refill(List<EventLog> events) {
    mEvents.clear();
    mEvents.addAll(events);
    notifyDataSetChanged();
}

Weitere Informationen finden Sie unter Android ListView: Behalten Sie Ihre Bildlaufposition bei, wenn Sie aktualisieren.

1
Brandon Yang

Sie müssen ein einzelnes Objekt dieser Liste verwenden, dessen Daten Sie auf ListView aufblasen. Wenn der Verweis geändert wird, funktioniert notifyDataSetChanged() nicht. Wenn Sie Elemente aus der Listenansicht löschen, löschen Sie sie auch aus der Liste, die Sie verwenden, unabhängig davon, ob es sich um eine ArrayList <> oder etwas anderes handelt. Rufen Sie notifyDataSetChanged() für das Objekt Ihrer Adapterklasse auf .

Hier also, wie ich es in meinem Adapter geschafft habe, siehe unten

public class CountryCodeListAdapter extends BaseAdapter implements OnItemClickListener{

private Context context;
private ArrayList<CountryDataObject> dObj;
private ViewHolder holder;
private Typeface itemFont;
private int selectedPosition=-1;
private ArrayList<CountryDataObject> completeList;

public CountryCodeListAdapter(Context context, ArrayList<CountryDataObject> dObj) {
    this.context = context;
    this.dObj=dObj;
    completeList=new  ArrayList<CountryDataObject>();
    completeList.addAll(dObj);
    itemFont=Typeface.createFromAsset(context.getAssets(), "CaviarDreams.ttf");
}

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

@Override
public Object getItem(int position) {
    return dObj.get(position);
}

@Override
public long getItemId(int position) {
    return position;
}
@Override
public View getView(int position, View view, ViewGroup parent) {
    if(view==null){
        holder = new ViewHolder();
        LayoutInflater inflater = (LayoutInflater) context.getSystemService(Context.LAYOUT_INFLATER_SERVICE);
        view = inflater.inflate(R.layout.states_inflator_layout, null);
        holder.textView = ((TextView)view.findViewById(R.id.stateNameInflator));
        holder.checkImg=(ImageView)view.findViewById(R.id.checkBoxState);
        view.setTag(holder);
    }else{
        holder = (ViewHolder) view.getTag();
    }
    holder.textView.setText(dObj.get(position).getCountryName());
    holder.textView.setTypeface(itemFont);

    if(position==selectedPosition)
     {
         holder.checkImg.setImageResource(R.drawable.check);
     }
     else
     {
         holder.checkImg.setImageResource(R.drawable.uncheck);
     }
    return view;
}
private class ViewHolder{
    private TextView textView;
    private ImageView checkImg;
}

public void getFilter(String name) {
    dObj.clear();
    if(!name.equals("")){
    for (CountryDataObject item : completeList) {
        if(item.getCountryName().toLowerCase().startsWith(name.toLowerCase(),0)){
            dObj.add(item);
        }
    }
    }
    else {
        dObj.addAll(completeList);
    }
    selectedPosition=-1;
    notifyDataSetChanged();
    notifyDataSetInvalidated(); 
}

@Override
public void onItemClick(AdapterView<?> parent, View view, int position,
        long id) {
    Registration reg=(Registration)context;
    selectedPosition=position;
    reg.setSelectedCountryCode("+"+dObj.get(position).getCountryCode());
    notifyDataSetChanged();
}
}
1
ADM

Wenn Sie die Listenansicht der Benutzeroberfläche von einem Dienst aus aktualisieren möchten, machen Sie den Adapter in Ihrer Main-Aktivität statisch und führen Sie folgende Schritte aus: 

@Override
public void onDestroy() {
    if (MainActivity.isInFront == true) {
        if (MainActivity.adapter != null) {
            MainActivity.adapter.notifyDataSetChanged();
        }

        MainActivity.listView.setAdapter(MainActivity.adapter);
    }
}    
0
Mayank Saini

Ich hatte eine ArrayList, die ich in einer Listenansicht anzeigen wollte. ArrayList enthielt Elemente aus MySQL. Ich habe die onRefresh-Methode überschrieben und in dieser Methode habe ich tablelayout.removeAllViews () verwendet. und wiederholte dann den Vorgang zum erneuten Abrufen von Daten aus der Datenbank . Bevor Sie dies tun, müssen Sie jedoch Ihre ArrayList oder eine andere Datenstruktur löschen. Andernfalls werden neue Daten an die alte angefügt.

0
Kshitij G

Nach dem Ändern von Informationen in der SQL-Datenbank konnte die Listenansicht nicht aktualisiert werden (dh um eine erweiterbare Listenansicht). Wenn also notifyDataSetChanged () nicht hilft, können Sie zunächst versuchen, Ihre Liste zu löschen und sie nach diesem Aufruf notifyDataSetChanged () erneut hinzuzufügen. Zum Beispiel

private List<List<SomeNewArray>> arrayList;
List<SomeNewArray> array1= getArrayList(...);
List<SomeNewArray> array2= getArrayList(...);
arrayList.clear();
arrayList.add(array1);
arrayList.add(array2);
notifyDataSetChanged();

Hoffe es macht für dich Sinn.

0
Aivus

Wenn ich hier über mein Szenario gesprochen habe, wird keine der oben genannten Antworten funktionieren, da ich Aktivitäten hatte, die eine Liste von Db-Werten zusammen mit einer Löschschaltfläche anzeigen. Wenn eine Löschschaltfläche gedrückt wird, wollte ich das Element aus der Liste löschen.

Das Coole war, ich habe keine Recycler-Ansicht verwendet, sondern eine einfache Listenansicht und diese Listenansicht, die in der Adapterklasse initialisiert wurde. Der Aufruf von notifyDataSetChanged() führt also nichts innerhalb der Adapterklasse und sogar in der Aktivitätsklasse aus, in der das Adapterobjekt initialisiert wird, da die Löschmethode in der Adapterklasse enthalten war.

Die Lösung bestand also darin, das Objekt in der Adapterklasse getView aus dem Adapter zu entfernen (um nur dieses bestimmte Objekt zu löschen, aber wenn Sie alle löschen möchten, rufen Sie clear() auf).

Um Ihnen eine Vorstellung zu geben, wie sah mein Code aus,

public class WordAdapter extends ArrayAdapter<Word> {
  Context context;

  public WordAdapter(Activity context, ArrayList<Word> words) {}
    //.......

    @NonNull
    @Override
    public View getView(final int position, View convertView, ViewGroup group) {
      //.......
     ImageButton deleteBt = listItemView.findViewById(R.id.Word_delete_bt);
     deleteBt.setOnClickListener(new View.OnClickListener() {
         @Override
         public void onClick(View v) {
             if (vocabDb.deleteWord(currentWord.id)) {
                //.....
             } else{
                //.....
             }
             remove(getItem(position)); // <---- here is the trick ---<
             //clear() // if you want to clear everything
         }
    });
 //....

Hinweis: Hier werden remove()- und getItem()-Methoden von der Adapter-Klasse übernommen.

  • remove() - zum Entfernen eines bestimmten Elements, auf das geklickt wird
  • getItem(position) - soll das Element (hier ist mein Word-Objekt , das ich der Liste hinzugefügt habe) von der angeklickten Position erhalten.

So setze ich den Adapter in der Aktivitätsklasse auf die Listenansicht.

    ArrayList<Word> wordList = new ArrayList();
    WordAdapter adapter = new WordAdapter(this, wordList);

    ListView list_view = (ListView) findViewById(R.id.activity_view_words);
    list_view.setAdapter(adapter);
0
Blasanka

Wenn Sie Android-Richtlinien verwenden und die ContentProviders verwenden, um Daten von Database abzurufen, und Sie diese mit ListView und CursorLoader in CursorAdapters anzeigen, werden alle Änderungen an den zugehörigen Daten automatisch in der ListView angezeigt.

Ihr getContext().getContentResolver().notifyChange(uri, null); auf dem Cursor in der ContentProvider wird ausreichen, um die Änderungen widerzuspiegeln.

Wenn Sie diese nicht alle verwenden, müssen Sie dem Adapter mitteilen, wenn sich das Dataset ändert. Außerdem müssen Sie Ihr Dataset erneut auffüllen/neu laden (z. B. list) und anschließend notifyDataSetChanged() auf dem Adapter aufrufen.

notifyDataSetChanged()wird nicht funktionieren, wenn keine Änderungen im Datensatz vorliegen . Hier ist der Kommentar über der Methode in docs-

/**
 * Notifies the attached observers that the underlying data has been changed
 * and any View reflecting the data set should refresh itself.
 */
0
Sanjeet A

Ich war das gleiche, als ich in einem Fragment ein ListView (in einem einzigen TextView) mit der MAC-Adresse von über einen längeren Zeitraum gescannten BLE-Geräten füllen wollte.

Was ich getan habe war folgendes: 

public class Fragment01 extends Android.support.v4.app.Fragment implements ...
{
    private ListView                listView;
    private ArrayAdapter<String>    arrayAdapter_string;

...

@Override
public void onActivityCreated(Bundle savedInstanceState)
{
    ...
    this.listView= (ListView) super.getActivity().findViewById(R.id.fragment01_listView);
    ...
    this.arrayAdapter_string= new ArrayAdapter<String>(super.getActivity(), R.layout.dispositivo_ble_item, R.id.fragment01_item_textView_titulo);
    this.listView.setAdapter(this.arrayAdapter_string);
}


@Override
public void onLeScan(BluetoothDevice device, int rssi, byte[] scanRecord)
{
    ...
    super.getActivity().runOnUiThread(new RefreshListView(device));
}


private class RefreshListView implements Runnable
{
    private BluetoothDevice bluetoothDevice;

    public RefreshListView(BluetoothDevice bluetoothDevice)
    {
        this.bluetoothDevice= bluetoothDevice;
    }

    @Override
    public void run()
    {
        Fragment01.this.arrayAdapter_string.add(new String(bluetoothDevice.toString()));
        Fragment01.this.arrayAdapter_string.notifyDataSetChanged();
    }
}

Dann begann sich die ListView dynamisch mit der MAC-Adresse der gefundenen Geräte zu füllen.

0
jsanmarb

während der Verwendung von SimpleCursorAdapter kann changeCursor (newCursor) auf dem Adapter aufgerufen werden.

0
Vihaan Verma

Angenommen, Sie haben eine Liste an Ihren Adapter übergeben.
Verwenden:

list.getAdapter().notifyDataSetChanged()

um Ihre Liste zu aktualisieren.

0
Mahdi-Malv

Ich konnte notifyDataSetChanged () nicht dazu bringen, meinen SimpleAdapter zu aktualisieren. Ich versuchte stattdessen, zunächst alle Ansichten zu entfernen, die mit removeAllViews () an das übergeordnete Layout angehängt waren UI:

LinearLayout results = (LinearLayout)findViewById(R.id.results);
ListView lv = new ListView(this);
ArrayList<HashMap<String,String>> list = new ArrayList<HashMap<String,String>>();
SimpleAdapter adapter = new SimpleAdapter( this, list, R.layout.directory_row, 
                new String[] { "name", "dept" }, new int[] { R.id.name, R.id.dept } );

for (...) { 
    HashMap<String, String> map = new HashMap<String, String>();
    map.put("name", name);
    map.put("dept", dept);
    list.add(map);
}

lv.setAdapter(adapter);
results.removeAllViews();     
results.addView(lv);
0
Chasden

eine andere Option ist die onWindowFocusChanged-Methode, deren Sensitivität jedoch sicher ist und zusätzliche Codierung erfordert, für wen sich dies interessiert

 override fun onWindowFocusChanged(hasFocus: Boolean) {
        super.onWindowFocusChanged(hasFocus)

       // some controls needed
        programList = usersDBHelper.readProgram(model.title!!)
        notesAdapter = DailyAdapter(this, programList)
        notesAdapter.notifyDataSetChanged()
        listview_act_daily.adapter = notesAdapter
    }
0
Sam

NotifyDataSetChanged konnte ich nur abrufen, indem ich neue Adapterdaten abrief, den Adapter für die Listenansicht zurücksetzte und dann den Aufruf so machte: 

    expandableAdapter = baseFragmentParent.setupEXLVAdapter();
    baseFragmentParent.setAdapter(expandableAdapter);
    expandableAdapter.notifyDataSetChanged(); 
0
Kristy Welsh

Ich denke, es hängt davon ab, was Sie mit Refresh meinen. Meinen Sie, dass die GUI-Anzeige aktualisiert werden muss, oder bedeuten Sie, dass die untergeordneten Ansichten so aktualisiert werden sollten, dass Sie getChildAt (int) programmgesteuert aufrufen können und die Ansicht erhalten, die dem im Adapter enthaltenen entspricht.

Wenn Sie möchten, dass die GUI aktualisiert wird, rufen Sie notifyDataSetChanged () für den Adapter auf. Die GUI wird beim nächsten Zeichnen aktualisiert.

Wenn Sie getChildAt (int) aufrufen und eine Ansicht erhalten möchten, die den Inhalt des Adapters widerspiegelt, rufen Sie layoutChildren () auf. Dadurch wird die untergeordnete Ansicht aus den Adapterdaten rekonstruiert.

0
dazed

Nach dem Löschen von Daten aus der Listenansicht müssen Sie refreshDrawableState()..__ aufrufen. Hier das Beispiel:

final DatabaseHelper db = new DatabaseHelper (ActivityName.this);

db.open();

db.deleteContact(arg3);

mListView.refreshDrawableState();

db.close();

und deleteContact-Methode in DatabaseHelper-Klasse sieht ein bisschen so aus 

public boolean deleteContact(long rowId) {

   return db.delete(TABLE_NAME, BaseColumns._ID + "=" + rowId, null) > 0;

}
0
user1031852