我的列表项目中包含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”,我的代码可以正常工作,滚动时不保存编辑文本的内容。如果我添加此代码,当我滚动所有关闭屏幕的editText视图而不是回来时会搞乱内容。
答案 0 :(得分:130)
你的解决方案的主要问题是在onBindViewHolder中分配和分配TextWatcher,这是一个昂贵的操作,会在快速滚动期间引入滞后,它似乎也会干扰确定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
}
}
}
答案 1 :(得分:9)
我有同样的问题,我添加了以下一行,它似乎解决了我的问题。
mRecyclerview.setItemViewCacheSize(mDataset.size());
希望这可以帮助您解决问题。
答案 2 :(得分: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
}
});
}
答案 3 :(得分:2)
我会创建一个接口并传递当前的适配器位置来处理文本更改事件
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);
}
});
}
}
}
答案 4 :(得分:1)
您好@mikwee确保您在下面的方法中添加了文本更改的侦听器,而不是将其添加到onBindViewHolder()。
<html>
<title>Test jQuery</title>
<head>
<link rel="stylesheet" type="text/css" href="style.css" />
<meta http-equiv="Content-Type" content="text/html" charset="UTF-8" />
</head>
<body>
<div class="container">
<div class="header">
<span>Expand</span>
</div>
<div class="content">
<ul>
<li>This is just some random content.</li>
<li>This is just some random content.</li>
<li>This is just some random content.</li>
<li>This is just some random content.</li>
</ul>
</div>
</div>
<script src="https://ajax.googleapis.com/ajax/libs/jquery/2.1.1/jquery.min.js"></script>
<script type="text/javascript">
$(document).ready(function() {
$(".header").click(function() {
$header = $(this);
//getting the next element
$content = $header.next();
//open up the content needed - toggle the slide- if visible, slide up, if not slidedown.
$content.slideToggle(500, function() {
//execute this after slideToggle is done
//change text of header based on visibility of content div
$header.text(function() {
//change text based on condition
return $content.is(":visible") ? "Collapse" : "Expand";
});
});
});
});
</script>
</body>
</html>
答案 5 :(得分:1)
根据我的说法,@ 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);
}
}
}
答案 6 :(得分:1)
我实现了@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 :(得分:1)
在RecyclerView适配器中重写onViewRecycled方法,如下所示:
@Override
public void onViewRecycled(@NonNull ViewHolder holder) {
mDataSet[holder.getAdapterPosition()] = holder.mEditText.getText().toString();
}
答案 8 :(得分:1)
问题是你【添加】一个监听器,而不是【设置】一个监听器。所以当 view 被回收时,一些 EditText 的监听器会超过 1。
要解决此问题,应在视图回收时清除侦听器。
第一步
使用自定义 EditText 替换。
public class RecyclerViewEditText extends AppCompatEditText {
private ArrayList<TextWatcher> mListeners = null;
public RecyclerViewEditText(@NonNull Context context) {
super(context);
}
public RecyclerViewEditText(@NonNull Context context, @Nullable AttributeSet attrs) {
super(context, attrs);
}
public RecyclerViewEditText(@NonNull Context context, @Nullable AttributeSet attrs, int defStyleAttr) {
super(context, attrs, defStyleAttr);
}
@Override
public void addTextChangedListener(TextWatcher watcher)
{
if (mListeners == null)
{
mListeners = new ArrayList<>();
}
mListeners.add(watcher);
super.addTextChangedListener(watcher);
}
@Override
public void removeTextChangedListener(TextWatcher watcher)
{
if (mListeners != null)
{
int i = mListeners.indexOf(watcher);
if (i >= 0)
{
mListeners.remove(i);
}
}
super.removeTextChangedListener(watcher);
}
public void clearTextChangedListeners()
{
if(mListeners != null)
{
for(TextWatcher watcher : mListeners)
{
super.removeTextChangedListener(watcher);
}
mListeners.clear();
mListeners = null;
}
}
}
步骤 2
将 xml 中的 EditText 替换为上面的内容。
<com.xxxxxxxx.widget.RecyclerViewEditText
android:id="@+id/et_remark"
android:layout_width="100dp"
android:layout_height="wrap_content"
android:background="@null"
android:gravity="end"
android:hint="input hint"
android:inputType="textPersonName" />
步骤 3
当视图被回收时,清除适配器中的所有侦听器。
@Override
public void onViewRecycled(@NonNull ViewHolder holder) {
holder.et_remark.clearTextChangedListeners();
super.onViewRecycled(holder);
}
答案 9 :(得分:0)
我对RecyclerView对象并不熟悉,但我对ListView也有同样的问题。对于那些,我通常创建一个ad-hoc类,表示插入到我的视图中的值(它与EditTexts,Checkboxes,RadioButtons ...一起使用)并通过它们获取更新的数据。然后,我创建一个由所述容器对象组成的自定义ArrayAdapter,检索在每个getView()回调时放入edittexts的值,并实现textwatcher以使这些对象保持最新。同样,我并不完全记得RecyclerViews是如何工作的,但如果它们涉及适配器,那么这对你来说可能是个不错的选择。
答案 10 :(得分:0)
对我来说,上述解决方案没有用。对于其中一些而言,侦听器未在调用,并且在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 :(得分:0)
我重写了方法getItemViewType
为我解决了
override fun getItemViewType(position: Int): Int {
return position
}
答案 12 :(得分:0)
如果使用数据绑定,请使用双向绑定
答案 13 :(得分:0)
实现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());
}
答案 14 :(得分:-1)
只需覆盖以下方法即可解决问题:
@Override
public int getItemViewType(final int position) {
return position;
}