Ich habe einen Listeneintrag EditText
darin, ich weiß nicht, wie viele Einträge es geben wird. Ich habe ein Problem, wenn ich Text EditText
eingebe und dann nach unten scrolle RecyclerView
. Nachdem ich wieder nach oben gescrollt habe, befindet sich in meinem ersten Text kein Text EditText
.
Ich frage mich , was und wo soll ich Code schreiben , so dass während der Benutzer die Eingabe oder Eingabe beendet ist (ich dachte , das zu tun, mit ein TextWatcher
) in EditText
den Text in einer Datei gespeichert wird (ich es in eine sparen werde. txt-Datei im externen Speicher)
Soll ich das in der onCreate
Methode der Aktivität oder in der Adapterklasse oder anderswo tun ?
Hier ist ein Code
Hauptaktivitätscode
public class MainActivity extends Activity {
RecyclerView mRecyclerView;
MyAdapter mAdapter;
String[] mDataSet= new String[20];
@Override
protected void onCreate(Bundle savedInstanceState) {
// generating text for editText Views
for (int i = 0; i<=19; i++){
mDataSet[i]= "EditText n: "+i;
}
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_main);
mRecyclerView = (RecyclerView) findViewById(R.id.recycler_view);
mAdapter = new MyAdapter(mDataSet);
mRecyclerView.setLayoutManager(new LinearLayoutManager(this, LinearLayoutManager.VERTICAL, false));
mRecyclerView.setItemAnimator(new DefaultItemAnimator());
mRecyclerView.setAdapter(mAdapter);
mRecyclerView.setHasFixedSize(true);
}
Mein Adaptercode
public class MyAdapter extends RecyclerView.Adapter<MyAdapter.ViewHolder> {
private String[] mDataset;
public static class ViewHolder extends RecyclerView.ViewHolder {
// each data item is just a string in this case
public EditText mEditText;
public ViewHolder(View v) {
super(v);
mEditText = (EditText) v.findViewById(R.id.list_item_edittext);
}
}
public MyAdapter(String[] myDataset) {
mDataset = myDataset;
}
@Override
public MyAdapter.ViewHolder onCreateViewHolder(ViewGroup parent,
int viewType) {
View v = LayoutInflater.from(parent.getContext())
.inflate(R.layout.list_item, parent, false);
ViewHolder vh = new ViewHolder(v);
return vh;
}
@Override
public void onBindViewHolder(ViewHolder holder, final int position) {
holder.mEditText.setText(mDataset[position]);
//without this addtextChangedListener my code works fine ovbiusly
// not saving the content of the edit Text when scrolled
// If i add this code then when i scroll all textView that go of screen
// and than come back in have messed up content
holder.mEditText.addTextChangedListener(new TextWatcher() {
@Override
public void onTextChanged(CharSequence s, int start,
int before, int count) {
//setting data to array, when changed
// this is a semplified example in the actual app i save the text
// in a .txt in the external storage
mDataset[position] = s.toString();
}
@Override
public void beforeTextChanged(CharSequence s, int start,
int count, int after) {
}
@Override
public void afterTextChanged(Editable s) {
}
});
}
@Override
public int getItemCount() {
return mDataset.length;
}
Ohne diesen "addtextChangedListener" funktioniert mein Code offensichtlich einwandfrei, ohne den Inhalt des Bearbeitungstextes beim Scrollen zu speichern. Wenn ich diesen Code hinzufüge, haben beim Scrollen alle editText-Ansichten, die vom Bildschirm verschwinden und dann wieder eingehen, den Inhalt durcheinander gebracht.
Antworten:
Das Hauptproblem bei Ihrer Lösung besteht darin, TextWatcher in onBindViewHolder zuzuweisen und zuzuweisen. Dies ist eine teure Operation, die bei schnellen Bildläufen Verzögerungen verursacht und die Bestimmung der zu aktualisierenden Position in mAdapter zu beeinträchtigen scheint.
Das Ausführen aller Vorgänge in onCreateViewHolder ist eine vorzuziehende Option. Hier ist die vollständig getestete Arbeitslösung:
public class MyAdapter extends RecyclerView.Adapter<MyAdapter.ViewHolder> { private String[] mDataset; public MyAdapter(String[] myDataset) { mDataset = myDataset; } @Override public MyAdapter.ViewHolder onCreateViewHolder(ViewGroup parent, int viewType) { View v = LayoutInflater.from(parent.getContext()).inflate(R.layout.list_item_edittext, parent, false); // pass MyCustomEditTextListener to viewholder in onCreateViewHolder // so that we don't have to do this expensive allocation in onBindViewHolder ViewHolder vh = new ViewHolder(v, new MyCustomEditTextListener()); return vh; } @Override public void onBindViewHolder(ViewHolder holder, final int position) { // update MyCustomEditTextListener every time we bind a new item // so that it knows what item in mDataset to update holder.myCustomEditTextListener.updatePosition(holder.getAdapterPosition()); holder.mEditText.setText(mDataset[holder.getAdapterPosition()]); } @Override public int getItemCount() { return mDataset.length; } public static class ViewHolder extends RecyclerView.ViewHolder { // each data item is just a string in this case public EditText mEditText; public MyCustomEditTextListener myCustomEditTextListener; public ViewHolder(View v, MyCustomEditTextListener myCustomEditTextListener) { super(v); this.mEditText = (EditText) v.findViewById(R.id.editText); this.myCustomEditTextListener = myCustomEditTextListener; this.mEditText.addTextChangedListener(myCustomEditTextListener); } } // we make TextWatcher to be aware of the position it currently works with // this way, once a new item is attached in onBindViewHolder, it will // update current position MyCustomEditTextListener, reference to which is kept by ViewHolder private class MyCustomEditTextListener implements TextWatcher { private int position; public void updatePosition(int position) { this.position = position; } @Override public void beforeTextChanged(CharSequence charSequence, int i, int i2, int i3) { // no op } @Override public void onTextChanged(CharSequence charSequence, int i, int i2, int i3) { mDataset[position] = charSequence.toString(); } @Override public void afterTextChanged(Editable editable) { // no op } } }
quelle
onBindViewHolder
da sich die Position eines Elements ändern kann, ohne dass ein weitereronBindViewHolder
Anruf getätigt wird. StattdessenMyCustomEditTextListener
sollte ein Verweis auf aViewHolder
und call seinViewHolder.getAdapterPosition()
, um die richtige Position zu erhalten.Ich hatte das gleiche Problem, ich fügte die folgende Zeile hinzu und es scheint das Problem auf meiner Seite behoben zu haben.
mRecyclerview.setItemViewCacheSize(mDataset.size());
Hoffentlich löst dies das Problem auf Ihrer Seite.
quelle
Erstellen Sie ein String-Array mit der Größe Ihrer Adapterdaten.
Z.B:
String[] texts = new String[dataSize];
Fügen Sie in der onBindViewHolder-Methode in Ihrem Adapter der Textansicht einen TextChangedListener hinzu.
Z.B : -
@Override public void onBindViewHolder(Viewholder holder, int position) { //binding data from array holder.yourEditText.setText(texts [position]); holder.yourEditText.addTextChangedListener(new TextWatcher() { @Override public void onTextChanged(CharSequence s, int start, int before, int count) { //setting data to array, when changed texts [position] = s.toString(); } @Override public void beforeTextChanged(CharSequence s, int start, int count, int after) { //blank } @Override public void afterTextChanged(Editable s) { //blank } }); }
quelle
Ich habe die @ dkarmazi-Lösung implementiert, aber sie hat mir nicht geholfen. Also bin ich weiter gekommen und es gibt wirklich funktionierende Lösungen.
public class MyAdapter extends RecyclerView.Adapter<MyAdapter.ViewHolder> { private String[] mDataset; public MyAdapter(String[] myDataset) { mDataset = myDataset; } @Override public MyAdapter.ViewHolder onCreateViewHolder(ViewGroup parent, int viewType) { View v = LayoutInflater.from(parent.getContext()).inflate(R.layout.list_item_edittext, parent, false); // pass MyCustomEditTextListener to viewholder in onCreateViewHolder // so that we don't have to do this expensive allocation in onBindViewHolder ViewHolder vh = new ViewHolder(v, new MyCustomEditTextListener()); return vh; } @Override public void onBindViewHolder(ViewHolder holder, final int position) { // update MyCustomEditTextListener every time we bind a new item // so that it knows what item in mDataset to update holder.myCustomEditTextListener.updatePosition(holder.getAdapterPosition()); holder.mEditText.setText(mDataset[holder.getAdapterPosition()]); } @Override public int getItemCount() { return mDataset.length; } @Override public void onViewAttachedToWindow(@NonNull RecyclerView.ViewHolder holder) { ((ViewHolder) holder).enableTextWatcher(); } @Override public void onViewDetachedFromWindow(@NonNull RecyclerView.ViewHolder holder) { ((ViewHolder) holder).disableTextWatcher(); } public static class ViewHolder extends RecyclerView.ViewHolder { // each data item is just a string in this case public EditText mEditText; public MyCustomEditTextListener myCustomEditTextListener; public ViewHolder(View v, MyCustomEditTextListener myCustomEditTextListener) { super(v); this.mEditText = (EditText) v.findViewById(R.id.editText); this.myCustomEditTextListener = myCustomEditTextListener; } void enableTextWatcher() { mEditText.addTextChangedListener(myCustomEditTextListener); } void disableTextWatcher() { mEditText.removeTextChangedListener(myCustomEditTextListener); } } // we make TextWatcher to be aware of the position it currently works with // this way, once a new item is attached in onBindViewHolder, it will // update current position MyCustomEditTextListener, reference to which is kept by ViewHolder private class MyCustomEditTextListener implements TextWatcher { private int position; public void updatePosition(int position) { this.position = position; } @Override public void beforeTextChanged(CharSequence charSequence, int i, int i2, int i3) { // no op } @Override public void onTextChanged(CharSequence charSequence, int i, int i2, int i3) { mDataset[position] = charSequence.toString(); } @Override public void afterTextChanged(Editable editable) { // no op } } }
Das Hauptproblem bestand darin, dass der angewendete TextWatcher während des Artikelrecyclings weiter funktionierte.
Ich habe versucht, es vor dem Recycling zu deaktivieren, aber es gibt keine "beforeRecycle" -Ereignismethoden. Also habe ich die
onViewDetachedFromWindow
Methode verwendet und sie hat gut funktioniert.Es gibt nur ein Problem, nachdem die Liste zu EditText Custom Editable.Factory hinzugefügt wurde. Ich weiß nicht warum, vielleicht finde ich auch die Lösung für dieses Problem
quelle
Ich würde eine Schnittstelle erstellen und die aktuelle Adapterposition übergeben, um das Textänderungsereignis zu behandeln
public class MyAdapter extends RecyclerView.Adapter<MyAdapter.ViewHolder> { private String[] mDataset; public MyAdapter(String[] myDataset) { mDataset = myDataset; } @Override public MyAdapter.ViewHolder onCreateViewHolder(ViewGroup parent, int viewType) { View v = LayoutInflater.from(parent.getContext()).inflate(R.layout.list_item_edittext, parent, false); ViewHolder vh = new ViewHolder(v, new ViewHolder.ITextWatcher() { @Override public void beforeTextChanged(int position, CharSequence s, int start, int count, int after) { // do something } @Override public void onTextChanged(int position, CharSequence s, int start, int before, int count) { mDataset[position] = s.toString(); } @Override public void afterTextChanged(int position, Editable s) { // do something } }); return vh; } @Override public void onBindViewHolder(ViewHolder holder, final int position) { holder.mEditText.setText(mDataset[position]); } @Override public int getItemCount() { return mDataset.length; } public static class ViewHolder extends RecyclerView.ViewHolder { public EditText mEditText; private ITextWatcher mTextWatcher; public interface ITextWatcher { // you can add/remove methods as you please, maybe you dont need this much void beforeTextChanged(int position, CharSequence s, int start, int count, int after); void onTextChanged(int position, CharSequence s, int start, int before, int count); void afterTextChanged(int position, Editable s); } public ViewHolder(View v, ITextWatcher textWatcher) { super(v); this.mEditText = (EditText) v.findViewById(R.id.editText); this.mTextWatcher = textWatcher; this.mEditText.addTextChangedListener(new TextWatcher() { @Override public void beforeTextChanged(CharSequence s, int start, int count, int after) { mTextWatcher.beforeTextChanged(getAdapterPosition(), s, start, count, after); } @Override public void onTextChanged(CharSequence s, int start, int before, int count) { mTextWatcher.onTextChanged(getAdapterPosition(), s, start, before, count); } @Override public void afterTextChanged(Editable s) { mTextWatcher.afterTextChanged(getAdapterPosition(), s); } }); } } }
quelle
Hallo @mikwee, stellen Sie sicher, dass Sie den Listener für Textänderungen in der folgenden Methode hinzufügen, anstatt ihn zu onBindViewHolder () hinzuzufügen.
public ViewHolder(View v) { super(v); yourEditText.addTextChangedListener(new TextWatcher() { @Override public void onTextChanged(CharSequence s, int start, int before, int count) { //setting data to array, when changed texts [position] = s.toString(); } @Override public void beforeTextChanged(CharSequence s, int start, int count, int after) { } @Override public void afterTextChanged(Editable s) { } }); }
quelle
Überschreiben Sie die onViewRecycled-Methode im RecyclerView-Adapter wie folgt:
@Override public void onViewRecycled(@NonNull ViewHolder holder) { mDataSet[holder.getAdapterPosition()] = holder.mEditText.getText().toString(); }
quelle
holder.getAdapterPosition()
im TextWatcher ist die beste Lösung - dann müssen Sie die Position nicht im Auge behalten, da der Recycler dies für Sie erledigt. Stellen Sie einfach sicher, dass nur ein TextWatcher im View Holder-Konstruktor hinzugefügt wird.onViewRecycled()
wird nicht aufgerufen werden, so können Sie Pickup die Änderungen gewohnt. Mit dem werdenTextWatcher
Sie immer über Änderungen informiert.Meiner Meinung nach ist dies eine Optimierung der Antwort von @ dkarmazi
public class UploadPhotoAdapter extends RecyclerView.Adapter<UploadPhotoAdapter.MyViewHolder> { ArrayList<Feed> feeds; Activity activity; public UploadPhotoAdapter(Activity activity, ArrayList<Feed> feeds) { this.feeds = feeds; this.activity = activity; } @Override public UploadPhotoAdapter.MyViewHolder onCreateViewHolder(ViewGroup parent, int viewType) { View itemView = LayoutInflater.from(parent.getContext()).inflate(R.layout.upload_feeds_recycler, parent, false); return new UploadPhotoAdapter.MyViewHolder(itemView); } @Override public void onBindViewHolder(final UploadPhotoAdapter.MyViewHolder holder, int position) { Feed feed = feeds.get(holder.getAdapterPosition()); holder.captionEditText.setText(feed.getDescription()); holder.captionEditText.addTextChangedListener(new TextWatcher() { @Override public void beforeTextChanged(CharSequence s, int start, int count, int after) {} @Override public void onTextChanged(CharSequence s, int start, int before, int count) { feeds.get(holder.getAdapterPosition()).setDescription(s.toString()); } @Override public void afterTextChanged(Editable s) {} }); } @Override public int getItemCount() { return feeds.size(); } public class MyViewHolder extends RecyclerView.ViewHolder { EditText captionEditText; public MyViewHolder(View view) { super(view); captionEditText = (EditText) view.findViewById(R.id.captionEditText); } } }
quelle
Für mich haben die oben genannten Lösungen nicht funktioniert. Für einige von ihnen hat der Listener nicht aufgerufen, und als der Listener in der onBindViewHolder-Methode aufgerufen wurde, scheint es, dass selbst beim Scrollen die Listener-Ereignisse aufgerufen werden. 'Text' ändert sich, also habe ich Key Listener ausprobiert und es hat gut funktioniert. Während des Bildlaufs werden vermutlich keine Tasten gedrückt.
holder.ticketNo.setOnKeyListener(new View.OnKeyListener() { @Override public boolean onKey(View v, int keyCode, KeyEvent event) { results.get(position).TicketNo = holder.ticketNo.getText().toString(); return false; } });
Der Code, der für mich funktioniert hat.
quelle
Ich bin mit RecyclerView-Objekten nicht so vertraut, aber ich hatte das gleiche Problem mit ListView. Für diese erstelle ich normalerweise eine Ad-hoc-Klasse, die Werte darstellt, die in meine Ansichten eingefügt wurden (dies funktioniert mit EditTexts, Checkboxes, RadioButtons ...), und erhalte über diese aktualisierte Daten. Anschließend erstelle ich einen benutzerdefinierten ArrayAdapter, der aus den Containerobjekten besteht, rufe Werte ab, die bei jedem Rückruf von getView () in die edittexts eingefügt werden sollen, und implementiere einen Textwatcher, um diese Objekte auf dem neuesten Stand zu halten. Auch hier erinnere ich mich nicht genau, wie RecyclerViews funktionieren, aber wenn es sich um Adapter handelt, könnte dies ein guter Hack für Sie sein, um es zu versuchen.
quelle
Ich habe die
getItemViewType
für mich gelöste Methode überschriebenoverride fun getItemViewType(position: Int): Int { return position }
quelle
LinearLayout
Verwenden Sie die bidirektionale Bindung, wenn Sie mit der Datenbindung arbeiten
https://medium.com/swlh/how-data-binding-helps-you-when-working-with-edittext-inside-recyclerview-543a1eb5f2cc
quelle
implementiert View.OnFocusChangeListener
@Override public void onBindViewHolder(Viewholder holder, int position) { editText.setText(model.getValue()); editText.setOnFocusChangeListener(this); editText.setTag(position); } @Override public void onFocusChange(View v, boolean hasFocus) { if (v.getTag() == null) return; int position = (int) v.getTag(); if (!hasFocus && v instanceof EditText) mValues.get(position).setValue(((EditText) v).getText().toString()); }
quelle