Android RecyclerView: notifyDataSetChanged () IllegalStateException


130

Ich versuche, die Elemente einer Recyclingansicht mit notifyDataSetChanged () zu aktualisieren.

Dies ist meine onBindViewHolder () -Methode im recycleview-Adapter.

@Override
public void onBindViewHolder(ViewHolder viewHolder, int position) {

     //checkbox view listener
    viewHolder.getCheckbox().setOnCheckedChangeListener(new CompoundButton.OnCheckedChangeListener() {
        @Override
        public void onCheckedChanged(CompoundButton buttonView, boolean isChecked) {

            //update list items
            notifyDataSetChanged();
        }
    });
}

Ich möchte die Listenelemente aktualisieren, nachdem ich ein Kontrollkästchen aktiviert habe. Ich bekomme jedoch eine illegale Ausnahme:"Cannot call this method while RecyclerView is computing a layout or scrolling"

java.lang.IllegalStateException: Cannot call this method while RecyclerView is computing a layout or scrolling
    at android.support.v7.widget.RecyclerView.assertNotInLayoutOrScroll(RecyclerView.java:1462)
    at android.support.v7.widget.RecyclerView$RecyclerViewDataObserver.onChanged(RecyclerView.java:2982)
    at android.support.v7.widget.RecyclerView$AdapterDataObservable.notifyChanged(RecyclerView.java:7493)
    at android.support.v7.widget.RecyclerView$Adapter.notifyDataSetChanged(RecyclerView.java:4338)
    at com.app.myapp.screens.RecycleAdapter.onRowSelect(RecycleAdapter.java:111)

Ich habe auch notifyItemChanged () verwendet, dieselbe Ausnahme. Gibt es eine geheime Möglichkeit zum Aktualisieren, um den Adapter darüber zu informieren, dass sich etwas geändert hat?


Ich habe jetzt das gleiche Problem. Wenn ich den setoncheckchanged-Listener in den Viewholder-Konstruktor setze, erhalte ich den gleichen Fehler
filthy_wizard

Antworten:


145

Sie sollten die Methode 'setOnCheckedChangeListener ()' nach ViewHolder verschieben, der inneren Klasse Ihres Adapters.

onBindViewHolder()ist keine Methode, die initialisiert wird ViewHolder. Diese Methode ist ein Schritt zum Aktualisieren jedes Recyclingartikels. Wenn Sie anrufen notifyDataSetChanged(), onBindViewHolder()wird als die Nummer jedes Artikels mal angerufen.

Wenn Sie also notifyDataSetChanged()in setzen onCheckChanged()und initialisieren checkBox in onBindViewHolder(), erhalten Sie Illegal wegen Kreismethodenaufruf.

Klicken Sie auf das Kontrollkästchen -> onCheckedChanged () -> notifyDataSetChanged () -> onBindViewHolder () -> Kontrollkästchen setzen -> onChecked ...

Sie können dies einfach beheben, indem Sie ein Flag in den Adapter setzen.

Versuche dies,

private boolean onBind;

public ViewHolder(View itemView) {
    super(itemView);
    mCheckBox = (CheckBox) itemView.findViewById(R.id.checkboxId);
    mCheckBox.setOnCheckChangeListener(this);
}

@Override
public void onCheckedChanged(CompoundButton buttonView, boolean isChecked) {
    if(!onBind) {
        // your process when checkBox changed
        // ...

        notifyDataSetChanged();
    }
}

...

@Override
public void onBindViewHolder(YourAdapter.ViewHolder viewHolder, int position) {
    // process other views 
    // ...

    onBind = true;
    viewHolder.mCheckBox.setChecked(trueOrFalse);
    onBind = false;
}

Ich verstehe, macht Sinn. Ich wünschte, die Plattform würde solch ein einfaches Verhalten vorhersagen und eine Lösung geben, anstatt sich auf Flaggen verlassen zu müssen.
Arthur

Es spielt keine Rolle , wo Sie den Hörer gesetzt , solange Sie das nicht benachrichtigen , AdapterViewObserverwährend onBindViewHolder()im Gange.
Jaroslaw Mytkalyk

6
Ich bevorzuge diese Lösung stackoverflow.com/a/32373999/1771194 mit einigen Verbesserungen im Kommentar. Außerdem konnte ich "RadioGroup" in RecyclerView erstellen.
Artem

Wie bekomme ich die Artikel Position in meiner Liste?
filthy_wizard

2
Dies funktioniert bei mir im Viewholder nicht. bekomme immer noch den Absturzfehler. Ich muss Vars in der Arrayliste ändern. sehr eigenartig. nicht sicher, wo ich den Listener anhängen kann.
filthy_wizard

45

Sie können den vorherigen Listener einfach zurücksetzen, bevor Sie Änderungen vornehmen, und diese Ausnahme wird nicht angezeigt.

private CompoundButton.OnCheckedChangeListener checkedListener = new CompoundButton.OnCheckedChangeListener() {                      
                        @Override
                        public void onCheckedChanged(CompoundButton buttonView, boolean isChecked) {
                            //Do your stuff
                    });;

    @Override
    public void onBindViewHolder(final ViewHolder holder, final int position) {
        holder.checkbox.setOnCheckedChangeListener(null);
        holder.checkbox.setChecked(condition);
        holder.checkbox.setOnCheckedChangeListener(checkedListener);
    }

2
Gute Antwort, aber es ist besser, nicht bei jedem Aufruf von onBindViewHolder einen Listener zu erstellen. Mach es als Feld.
Artem

1
Die Verwendung eines Feldes ist natürlich besser, ich habe nur ein Beispiel gegeben, das funktioniert. Aber danke für die Warnung, ich werde die Antwort aktualisieren.
JoniDS

1
Ich muss sowieso jedes Mal einen neuen Listener binden, da der Listener jedes Mal eine aktualisierte Positionsvariable benötigt. Das ist also eine großartige Antwort, sodass ich keinen Handler verwenden muss.
Rock Lee

Dies ist definitiv der beste Weg, da es niemals empfohlen wird, den globalen Status beizubehalten , den die akzeptierte Antwort ( stackoverflow.com/a/31069171/882251 ) empfiehlt.
Darwind

Einfachste !! Vielen Dank !!
DalveerSinghDaiya

39

Die Verwendung von a Handlerzum Hinzufügen von Elementen und Aufrufen notify...()von hier aus hat Handlerdas Problem für mich behoben.


3
Das ist die richtige Antwort. Sie können das Element während der Einstellung nicht ändern (beim Aufrufen von onBindViewHolder). In diesem Fall müssen Sie notifyDataSetChanged am Ende der aktuellen Schleife aufrufen, indem Sie Handler.post ()
pjanecze

1
@ user1232726 Wenn Sie den Handler im Hauptthread erstellen, müssen Sie keinen Looper angeben (standardmäßig der Looper des aufrufenden Threads). Also ja, das ist mein Rat. Andernfalls können Sie den Looper auch manuell angeben.
Cybergen

Leider bleiben meine Kontrollkästchen nicht aktiviert, wenn ich nach unten und wieder nach oben scrolle. Boo. lol
filthy_wizard

@ user1232726 Suchen Sie nach einer Antwort oder stellen Sie eine neue Frage, die Ihr Problem beschreibt.
Cybergen

2
Ich würde stark entmutigen diese Antwort , da dies ein hacky Weg, um das Problem zu lösen. Je mehr Sie dies tun, desto komplexer wird Ihr Code. Siehe Moonsoo Antwort das Problem und verstehen JoniDS die Antwort , das Problem zu lösen.
Kalpesh Patel

26

Ich weiß es nicht gut, aber ich hatte auch das gleiche Problem. Ich habe das mit onClickListneron gelöstcheckbox

viewHolder.mCheckBox.setOnClickListener(new OnClickListener() {
        @Override
        public void onClick(View v) {
            // TODO Auto-generated method stub
            if (model.isCheckboxBoolean()) {
                model.setCheckboxBoolean(false);
                viewHolder.mCheckBox.setChecked(false);
            } else {
                model.setCheckboxBoolean(true);
                viewHolder.mCheckBox.setChecked(true);
            }
            notifyDataSetChanged();
        }
    });

Versuchen Sie dies, dies kann helfen!


1
Gute Arbeit) ABER nur auf Klick (wenn ich das Widget (SwitchCompat) langsam bewege, wird diese Aktion übersehen. Dies ist das einzige Problem
Vlad

12
protected void postAndNotifyAdapter(final Handler handler, final RecyclerView recyclerView, final RecyclerView.Adapter adapter) {
        handler.post(new Runnable() {
            @Override
            public void run() {
                if (!recyclerView.isComputingLayout()) {
                    adapter.notifyDataSetChanged();
                } else {
                    postAndNotifyAdapter(handler, recyclerView, adapter);
                }
            }
        });
    }

Ich nehme an, dass Sie den Adapter problemlos zweimal benachrichtigen können.
12аксим Петлюк

8

Wenn Sie den Nachrichtenfehler haben:

Cannot call this method while RecyclerView is computing a layout or scrolling

Einfach, machen Sie einfach, was die Ausnahme verursacht in:

RecyclerView.post(new Runnable() {
    @Override
    public void run() {
        /** 
        ** Put Your Code here, exemple:
        **/
        notifyItemChanged(position);
    }
});

1
Das hat bei mir funktioniert. Sie fragen sich, ob es ein Problem mit dieser Lösung gibt?
Sayooj Valsan

7

Eine einfache Lösung gefunden -

public class MyAdapter extends RecyclerView.Adapter<RecyclerView.ViewHolder>{

    private RecyclerView mRecyclerView; 

    @Override
    public void onAttachedToRecyclerView(RecyclerView recyclerView) {
        super.onAttachedToRecyclerView(recyclerView);
        mRecyclerView = recyclerView;
    }

    private CompoundButton.OnCheckedChangeListener checkedChangeListener 
    = (compoundButton, b) -> {
        final int position = (int) compoundButton.getTag();
        // This class is used to make changes to child view
        final Event event = mDataset.get(position);
        // Update state of checkbox or some other computation which you require
        event.state = b;
        // we create a runnable and then notify item changed at position, this fix crash
        mRecyclerView.post(new Runnable() {
            @Override public void run() {
                notifyItemChanged(position));
            }
        });
    }
}

Hier erstellen wir eine ausführbare Datei, um ItemChanged für eine Position zu benachrichtigen, wenn recyclerview bereit ist, damit umzugehen.


5

Ihr CheckBox-Element ändert sich beim Aufruf in Drawable, notifyDataSetChanged();sodass diese Ausnahme auftreten würde. Versuchen Sie, notifyDataSetChanged();nach Ihrer Ansicht anzurufen . Beispielsweise:

buttonView.post(new Runnable() {
                    @Override
                    public void run() {
                        notifyDataSetChanged();
                    }
                });

4

Zuerst dachte ich, Moonsoos Antwort (die akzeptierte Antwort) würde für mich nicht funktionieren, da ich meine setOnCheckedChangeListener()im ViewHolder-Konstruktor nicht initialisieren kann, weil ich sie jedes Mal binden muss, damit sie eine aktualisierte Positionsvariable erhält. Aber ich habe lange gebraucht, um zu begreifen, was er sagte.

Hier ist ein Beispiel für den "zirkulären Methodenaufruf", von dem er spricht:

public void onBindViewHolder(final ViewHolder holder, final int position) {
    SwitchCompat mySwitch = (SwitchCompat) view.findViewById(R.id.switch);
    mySwitch.setOnCheckedChangeListener(new CompoundButton.OnCheckedChangeListener() {
                @Override
                public void onCheckedChanged(CompoundButton buttonView, boolean isChecked) {
                       if (isChecked) {
                           data.delete(position);
                           notifyItemRemoved(position);
                           //This will call onBindViewHolder, but we can't do that when we are already in onBindViewHolder!
                           notifyItemRangeChanged(position, data.size());
                       }
                   }
            });
    //Set the switch to how it previously was.
    mySwitch.setChecked(savedSwitchState); //If the saved state was "true", then this will trigger the infinite loop.
}

Das einzige Problem dabei ist, dass, wenn wir den Schalter so initialisieren müssen, dass er ein- oder ausgeschaltet ist (z. B. aus dem zuvor gespeicherten Zustand), der Listener angerufen wird, der möglicherweise nofityItemRangeChangedanruft und welche onBindViewHoldererneut anruft . Sie können nicht anrufen, onBindViewHolderwenn Sie bereits in onBindViewHolder] sind, da Sie dies nicht können, notifyItemRangeChangedwenn Sie bereits benachrichtigt werden, dass sich der Artikelbereich geändert hat.Aber ich musste nur die Benutzeroberfläche aktualisieren, um sie ein- oder auszuschalten, und wollte eigentlich nichts auslösen.

Hier ist die Lösung, die ich aus JoniDS 'Antwort gelernt habe und die die Endlosschleife verhindert. Solange wir den Listener auf "null" setzen, bevor wir "Checked" setzen, wird die Benutzeroberfläche aktualisiert, ohne den Listener auszulösen, wodurch die Endlosschleife vermieden wird. Dann können wir den Listener danach einstellen.

JoniDS Code:

holder.checkbox.setOnCheckedChangeListener(null);
holder.checkbox.setChecked(condition);
holder.checkbox.setOnCheckedChangeListener(checkedListener);

Vollständige Lösung für mein Beispiel:

public void onBindViewHolder(final ViewHolder holder, final int position) {
    SwitchCompat mySwitch = (SwitchCompat) view.findViewById(R.id.switch);

    //Set it to null to erase an existing listener from a recycled view.
    mySwitch.setOnCheckedChangeListener(null);

    //Set the switch to how it previously was without triggering the listener.
    mySwitch.setChecked(savedSwitchState); //If the saved state was "true", then this will trigger the infinite loop.

    //Set the listener now.
    mySwitch.setOnCheckedChangeListener(new CompoundButton.OnCheckedChangeListener() {
        @Override
        public void onCheckedChanged(CompoundButton buttonView, boolean isChecked) {
            if (isChecked) {
                data.delete(position);
                notifyItemRemoved(position);
                //This will call onBindViewHolder, but we can't do that when we are already in onBindViewHolder!
                notifyItemRangeChanged(position, data.size());
            }
        }
    });
}

Sie sollten vermeiden, den OnCheckedChangeListener immer wieder im onBindViewHolder zu initialisieren (auf diese Weise wird weniger GC benötigt). Dies soll in onCreateViewHolder aufgerufen werden, und Sie erhalten die Position, indem Sie Inhaber.getAdapterPosition () aufrufen.
Android-Entwickler

4

Warum nicht den RecyclerView.isComputingLayout()Status wie folgt überprüfen ?

public class MyAdapter extends RecyclerView.Adapter<RecyclerView.ViewHolder>{

    private RecyclerView mRecyclerView; 

    @Override
    public void onAttachedToRecyclerView(RecyclerView recyclerView) {
        super.onAttachedToRecyclerView(recyclerView);
        mRecyclerView = recyclerView;
    }

    @Override
    public void onBindViewHolder(ViewHolder viewHolder, int position) {

        viewHolder.getCheckbox().setOnCheckedChangeListener(new CompoundButton.OnCheckedChangeListener() {
            @Override
            public void onCheckedChanged(CompoundButton buttonView, boolean isChecked) {
                if (mRecyclerView != null && !mRecyclerView.isComputingLayout()) {
                    notifyDataSetChanged();
                }
            }
        });
    }
}

2

Während das Element vom Layout-Manager gebunden wird, ist es sehr wahrscheinlich, dass Sie den aktivierten Status Ihres Kontrollkästchens festlegen, das den Rückruf auslöst.

Dies ist natürlich eine Vermutung, da Sie nicht den vollständigen Stack-Trace veröffentlicht haben.

Sie können den Adapterinhalt nicht ändern, während RV das Layout neu berechnet. Sie können dies vermeiden, indem Sie notifyDataSetChanged nicht aufrufen, wenn der aktivierte Status des Elements dem im Rückruf gesendeten Wert entspricht (was der Fall ist, wenn der Aufruf checkbox.setCheckedden Rückruf auslöst).


Danke @yigit! Mein Problem hatte nicht mit einem Kontrollkästchen zu tun, sondern mit einer komplexeren Situation, in der ich ein anderes Element im Adapter benachrichtigen musste, aber einen ähnlichen Absturz bekam. Ich habe meine Benachrichtigungslogik so aktualisiert, dass sie nur aktualisiert wird, wenn sich die Daten tatsächlich ändern, und mein Absturz wurde behoben. Meine neue Regel bei RecyclerViews: Benachrichtigen Sie nicht, dass sich etwas geändert hat, wenn sich nichts geändert hat. Vielen Dank für diese Antwort!
CodyEngel

2

Verwenden Sie das Kontrollkästchen onClickListner on anstelle von OnCheckedChangeListener. Dadurch wird das Problem behoben

viewHolder.myCheckBox.setOnClickListener(new OnClickListener() {
        @Override
        public void onClick(View v) {
            if (viewHolder.myCheckBox.isChecked()) {
                // Do something when checkbox is checked
            } else {
                // Do something when checkbox is unchecked                
            }
            notifyDataSetChanged();
        }
    });


1

Einfache Verwendung Beitrag:

new Handler().post(new Runnable() {
        @Override
        public void run() {
                mAdapter.notifyItemChanged(mAdapter.getItemCount() - 1);
            }
        }
    });

0

Ich bin genau auf dieses Problem gestoßen! Nachdem Moonsoos Antwort mein Boot nicht wirklich schwebte, spielte ich ein bisschen herum und fand eine Lösung, die für mich funktionierte.

Hier ist zunächst ein Teil meines Codes:

    @Override
    public void onBindViewHolder(ViewHolder holder, final int position) {

    final Event event = mDataset.get(position);

    //
    //  .......
    //

    holder.mSwitch.setOnCheckedChangeListener(new CompoundButton.OnCheckedChangeListener() {
        @Override
        public void onCheckedChanged(CompoundButton buttonView, boolean isChecked) {
            event.setActive(isChecked);
            try {
                notifyItemChanged(position);
            } catch (Exception e) {
                Log.e("onCheckChanged", e.getMessage());
            }
        }
    });

Sie werden feststellen, dass ich den Adapter speziell über die Position benachrichtige, die ich ändere, anstatt über den gesamten Datensatz, wie Sie es tun. Obwohl ich nicht garantieren kann, dass dies für Sie funktioniert, habe ich das Problem gelöst, indem ich meinen notifyItemChanged()Anruf in einen Try / Catch-Block eingeschlossen habe. Dies hat einfach die Ausnahme abgefangen, aber mein Adapter konnte trotzdem die Statusänderung registrieren und die Anzeige aktualisieren!

Hoffe das hilft jemandem!

EDIT: Ich gebe zu, dies ist wahrscheinlich nicht die richtige / ausgereifte Art, das Problem zu behandeln, aber da es keine Probleme zu verursachen scheint, wenn die Ausnahme unbehandelt bleibt, dachte ich, ich würde sie teilen, falls es gut wäre genug für jemand anderen.


0

Dies geschieht, weil Sie wahrscheinlich den "Listener" festlegen, bevor Sie den Wert für diese Zeile konfigurieren. Dadurch wird der Listener ausgelöst, wenn Sie den Wert für das Kontrollkästchen "konfigurieren".

Was Sie tun müssen, ist:

@Override
public void onBindViewHolder(YourAdapter.ViewHolder viewHolder, int position) {
   viewHolder.mCheckBox.setOnCheckedChangeListener(null);
   viewHolder.mCheckBox.setChecked(trueOrFalse);
   viewHolder.setOnCheckedChangeListener(yourCheckedChangeListener);
}

0
        @Override
        public void onBindViewHolder(final MyViewHolder holder, final int position) {
            holder.textStudentName.setText(getStudentList.get(position).getName());
            holder.rbSelect.setChecked(getStudentList.get(position).isSelected());
            holder.rbSelect.setTag(position); // This line is important.
            holder.rbSelect.setOnClickListener(onStateChangedListener(holder.rbSelect, position));

        }

        @Override
        public int getItemCount() {
            return getStudentList.size();
        }
        private View.OnClickListener onStateChangedListener(final RadioButton checkBox, final int position) {
            return new View.OnClickListener() {
                @Override
                public void onClick(View v) {
                    if (checkBox.isChecked()) {
                        for (int i = 0; i < getStudentList.size(); i++) {

                            getStudentList.get(i).setSelected(false);

                        }
                        getStudentList.get(position).setSelected(checkBox.isChecked());

                        notifyDataSetChanged();
                    } else {

                    }

                }
            };
        }

0

verwenden Sie einfach isPressed()Verfahren CompoundButtonin onCheckedChanged(CompoundButton compoundButton, boolean isChecked)
zB

public void onCheckedChanged(CompoundButton compoundButton, boolean isChecked) {   
                      ... //your functionality    
                            if(compoundButton.isPressed()){
                                notifyDataSetChanged();
                            }
                        }  });

0

Ich hatte das gleiche Problem mit der Checkbox und dem RadioButton. Ersetzen notifyDataSetChanged()durch notifyItemChanged(position)gearbeitet. Ich isCheckedhabe dem Datenmodell ein Boolesches Feld hinzugefügt . Dann habe ich den Booleschen Wert aktualisiert und in onCheckedChangedListeneraufgerufen notifyItemChanged(adapterPosition). Dies ist vielleicht nicht der beste Weg, hat aber für mich funktioniert. Der boolesche Wert wird verwendet, um zu überprüfen, ob das Element aktiviert ist.


0

meist geschieht es beacause notifydatasetchanged Berufung onCheckedchanged Ereignis von Kontrollkästchen , und in diesem Fall wieder dort notifydatasetchanged .

Um es zu lösen, können Sie einfach überprüfen, ob das Kontrollkästchen programmgesteuert aktiviert ist oder der Benutzer es gedrückt hat. Es gibt eine Methode, die dafür gedrückt wird.

Wickeln Sie also den gesamten Listener-Code in die isPressed-Methode ein. und es ist geschafft.

 holder.mBinding.cbAnnual.setOnCheckedChangeListener(new CompoundButton.OnCheckedChangeListener() {
            @Override
            public void onCheckedChanged(CompoundButton compoundButton, boolean b) {

                if(compoundButton.isPressed()) {


                       //your code
                        notifyDataSetChanged();   

            }
        });

0

Ich habe stundenlang unter diesem Problem gelitten und so können Sie es beheben. Aber bevor Sie begonnen haben, gibt es einige Bedingungen für diese Lösung.

MODELLKLASSE

public class SelectUserModel {

    private String userName;
    private String UserId;
    private Boolean isSelected;


    public String getUserName() {
        return userName;
    }

    public void setUserName(String userName) {
        this.userName = userName;
    }

    public String getUserId() {
        return UserId;
    }

    public void setUserId(String userId) {
        UserId = userId;
    }

    public Boolean getSelected() {
        return isSelected;
    }

    public void setSelected(Boolean selected) {
        isSelected = selected;
    }
}

CHECKBOX in der ADAPTERKLASSE

CheckBox cb;

ADAPTER CLASS CONSTRUCTOR & LISTE DES MODELLS

private List<SelectUserModel> userList;

public StudentListAdapter(List<SelectUserModel> userList) {
        this.userList = userList;

        for (int i = 0; i < this.userList.size(); i++) {
            this.userList.get(i).setSelected(false);
        }
    }

ONBINDVIEW [Bitte verwenden Sie onclick anstelle von onCheckChange]

public void onBindViewHolder(@NonNull final StudentListAdapter.ViewHolder holder, int position) {
    holder.cb.setChecked(user.getSelected());
    holder.cb.setOnClickListener(new View.OnClickListener() {
        @Override
        public void onClick(View view) {

            int pos = (int) view.getTag();
            Log.d(TAG, "onClick: " + pos);
            for (int i = 0; i < userList.size(); i++) {
                if (i == pos) {
                    userList.get(i).setSelected(true);
// an interface to listen to callbacks
                    clickListener.onStudentItemClicked(userList.get(i));
                } else {
                    userList.get(i).setSelected(false);
                }
            }
            notifyDataSetChanged();
        }
    });

}}


-1

Bei mir trat ein Problem auf, als ich EditText durch Fertig, Zurück oder externe Eingabeberührung verließ. Dadurch wird das Modell mit dem eingegebenen Text aktualisiert und anschließend die Recycler-Ansicht über die Beobachtung von Live-Daten aktualisiert.

Das Problem war, dass Cursor / Fokus in EditText bleiben.

Wenn ich den Fokus gelöscht habe, indem ich:

editText.clearFocus() 

Benachrichtigungsdaten geändert Methode der Recycler-Ansicht hat diesen Fehler nicht mehr ausgelöst.

Ich denke, dies ist einer der möglichen Gründe / Lösungen für dieses Problem. Es ist möglich, dass diese Ausnahme auf andere Weise behoben werden kann, da sie aus ganz anderen Gründen verursacht werden kann.

Durch die Nutzung unserer Website bestätigen Sie, dass Sie unsere Cookie-Richtlinie und Datenschutzrichtlinie gelesen und verstanden haben.
Licensed under cc by-sa 3.0 with attribution required.