J'ai un élément de liste contenant EditText
, je ne sais pas combien d'éléments il y aura . J'ai un problème lorsque je saisis du texte dans EditText
, puis je fais défiler un RecyclerView
, après avoir remonté à la hausse il n'y a pas de texte dans mon premier EditText
.
Je me demande quoi, et où devrais-je écrire le code de sorte que lorsque l'utilisateur tape ou a fini de taper (je pensais le faire avec un TextWatcher
) dans EditText
, le texte est enregistré dans un fichier (je le sauvegarde dans un fichier .txt dans le stockage externe)
Suis-je censé le faire dans la méthode onCreate
de l'activité ou dans la classe adaptateur ou ailleurs?
Voici du code
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;
}
sans ce "addtextChangedListener", mon code fonctionne très bien sans enregistrer le contenu du texte d'édition lors du défilement. Si j'ajoute ce code, lorsque je fais défiler tous les affichages editText qui sortent de l'écran et qui reviennent ont tout foiré.
Le problème majeur de votre solution est d’affecter et d’attribuer TextWatcher à onBindViewHolder, une opération coûteuse qui introduit des retards lors des défilements rapides et semble également gêner la détermination de la position à mettre à jour dans mAdapter.
Effectuer toutes les opérations dans onCreateViewHolder est une option préférable. Voici la solution de travail testée complète:
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
}
}
}
J'ai eu le même problème, j'ai ajouté la ligne suivante et il semble avoir résolu le problème de mon côté.
mRecyclerview.setItemViewCacheSize(mDataset.size());
Espérons que cela règle la question de votre côté.
Créez un tableau String avec la taille de vos données d'adaptateur.
Exemple: String[] texts = new String[dataSize];
sur la méthode onBindViewHolder de votre adaptateur, ajoutez un TextChangedListener à la vue TextView.
Par exemple : -
@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
}
});
}
Je voudrais créer une interface et transmettre la position actuelle de l'adaptateur pour gérer l'événement de modification de texte
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);
}
});
}
}
}
Bonjour @mikwee, assurez-vous que vous ajoutez du texte à l'écoute dans la méthode ci-dessous plutôt que de l'ajouter à 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) {
}
});
}
J'ai implémenté la solution @dkarmazi, mais cela ne m'a pas aidé. Donc, je suis venu plus loin et il y a vraiment une solution de travail.
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
}
}
}
Le principal problème était que TextWatcher appliqué continuait à fonctionner pendant le recyclage des articles.
J'ai essayé de le désactiver avant le recyclage, mais il n'y a pas de méthode d'événement "beforeRecycle". J'ai donc utilisé la méthode onViewDetachedFromWindow
, et cela a bien fonctionné.
Il n’ya qu’un seul problème, après avoir ajouté à EditText custom Editable.Factory, la liste est de nouveau bridée. Je ne sais pas pourquoi, peut-être que je trouverai aussi la solution à ce problème
Selon moi, c'est plus d'optimiser la réponse de @ 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);
}
}
}
Pour moi, les solutions ci-dessus ne fonctionnaient pas. Pour certains d'entre eux, le programme d'écoute n'appelait pas et lorsque le programme a été appelé dans la méthode onBindViewHolder, il semble que même lorsque le défilement des événements du programme d'écoute est appelé. 'Texte' est en train de changer, j'ai donc essayé le programme d'écoute clé et tout a bien fonctionné. Aucune touche n'est pressée pendant le défilement, je suppose.
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;
}
});
Le code qui a fonctionné pour moi.
Je ne connais pas très bien les objets RecyclerView, mais j'avais le même problème avec ListView. Pour ceux-là, je crée généralement une classe ad-hoc représentant les valeurs insérées dans mes vues (cela fonctionne avec EditTexts, Checkboxes, RadioButtons ...) et je reçois des données mises à jour par leur intermédiaire. Je crée ensuite un ArrayAdapter personnalisé constitué desdits objets conteneur, en récupérant les valeurs à mettre dans les edittexts à chaque rappel de getView () et en implémentant un textwatcher pour maintenir ces objets à jour. Encore une fois, je ne me souviens pas exactement du fonctionnement de RecyclerViews, mais s’ils impliquaient des adaptateurs, cela pourrait être un bon bidouillage à essayer.
Remplacez la méthode onViewRecycled dans l’adaptateur RecyclerView comme ceci:
@Override
public void onViewRecycled(@NonNull ViewHolder holder) {
mDataSet[holder.getAdapterPosition()] = holder.mEditText.getText().toString();
}