Сохранение содержимого EditText в RecyclerView
У меня есть элемент списка с EditText
в нем, я не знаю, сколько предметов там будет.
У меня проблема при вводе некоторого текста в EditText
, а затем прокрутка вниз по RecyclerView
, после того, как я снова прокручиваю текст, в моем первом EditText
тексте нет текста.
Мне интересно, что и где писать код, чтобы во время ввода или завершения ввода текста (я думал сделать это с помощью TextWatcher
) в EditText
текст сохраняется в файл (Я сохраню его в .txt файле во внешнем хранилище)
Я должен сделать это в методе onCreate
активности или в классе адаптера или в другом месте?
Вот какой код
Основной код операции
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);
}
Мой код адаптера
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;
}
без этого "addtextChangedListener" мой код работает отлично obviusly, не сохраняя содержимое текста редактирования при прокрутке. Если я добавлю этот код, когда я просматриваю все виды editText, которые выходят из экрана, а затем возвращаются, перепутали содержимое.
Ответы
Ответ 1
Основная проблема с вашим решением заключается в распределении и назначении TextWatcher в onBindViewHolder, который является дорогостоящей операцией, которая будет вводить лага во время быстрых прокруток, а также мешает определить, какую позицию обновлять в mAdapter.
Сделать все операции в onCreateViewHolder предпочтительнее. Вот полное тестовое рабочее решение:
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
}
}
}
Ответ 2
У меня была та же проблема, я добавил следующую строку и, похоже, исправил эту проблему на моей стороне.
mRecyclerview.setItemViewCacheSize(mDataset.size());
Надеюсь, это разобрат проблему на вашей стороне.
Ответ 3
Создайте массив String с размером ваших данных адаптера.
Например: String[] texts = new String[dataSize];
в методе onBindViewHolder внутри вашего адаптера, добавьте TextChangedListener в Textview.
Например: -
@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
}
});
}
Ответ 4
Привет @mikwee убедитесь, что вы добавляете текстовый замеченный слушатель ниже, а не добавляете его в onBindViewHolder().
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) {
}
});
}
Ответ 5
Я бы создал интерфейс и передал текущую позицию адаптера для обработки события изменения текста
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);
}
});
}
}
}
Ответ 6
Я реализовал решение @dkarmazi, но оно мне не помогло. Итак, я пошел дальше и нашел действительно рабочее решение.
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
}
}
}
Основная проблема заключалась в том, что применяемый TextWatcher продолжал работать во время утилизации предметов.
Я пытался отключить его перед утилизацией, но нет никаких методов "beforeRecycle". Поэтому я использовал метод onViewDetachedFromWindow
, и он работал хорошо.
Есть только одна проблема, после добавления в EditText пользовательского Editable.Factory список снова тормозит. Я не знаю почему, может быть, я тоже найду решение этой проблемы
Ответ 7
Переопределите метод onViewRecycled в адаптере RecyclerView следующим образом:
@Override
public void onViewRecycled(@NonNull ViewHolder holder) {
mDataSet[holder.getAdapterPosition()] = holder.mEditText.getText().toString();
}
Ответ 8
По моему мнению, это больше оптимизирует ответ @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);
}
}
}
Ответ 9
Я не знаком с объектами RecyclerView, но у меня была такая же проблема с ListView. Для них я обычно создаю ad-hoc-класс, представляющий значения, вставленные в мои представления (он работает с EditTexts, Checkboxes, RadioButtons...) и обновляет данные через них. Затем я создаю настраиваемый ArrayAdapter, состоящий из указанных объектов контейнера, извлекающий значения для ввода в edittexts при каждом обратном вызове getView() и реализации текстового элемента, чтобы обновлять эти объекты. Опять же, я не совсем помню, как работают RecyclerViews, но если они связаны с адаптерами, это может быть хорошим взломом для вас.
Ответ 10
Для меня вышеуказанные решения не сработали. Для некоторых из них слушатель не вызывал, и когда слушатель был вызван в методе onBindViewHolder, кажется, что даже при прокрутке вызываются события слушателя. "Текст" меняется, поэтому я попробовал ключевой приемник, и он работал нормально. Я думаю, во время прокрутки не нажимаются клавиши.
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;
}
});
Код, который работал для меня.
Ответ 11
Просто отмените метод ниже, чтобы решить проблему:
@Override
public int getItemViewType(final int position) {
return position;
}