在RecyclerView中保存EditText内容


77

我有清单项目,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视图时,会弄乱内容。


2
请为托管recyclerView的适配器,布局,活动/片段共享您的代码。
dkarmazi 2015年

使用TextWatcher将输入的文本保存到列表的内存中表示形式,然后将其保存到Activity或Fragment onStop()方法中的SharedPreferences中。
BladeCoder 2015年

您可以在活动中获取该editText。您只需查找该位置的editText的视图即可。如果您想要这种类型的代码,我可以为您提供帮助。
Harvi Sirja

发表您的onBindViewHolder
Apurva

@dkarmazi,我添加了代码
gatteo,2015年

Answers:


168

解决方案的主要问题是在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
        }
    }
}

3
竭诚欢迎您!关于您的问题,将数据保存到外部存储(无论是写入磁盘还是通过网络)是一项非常昂贵的操作,必须在UI线程之外执行。因此,您必须异步执行此操作,并将这些调用的数量减少到最少。由于您有20个EditTextViews,因此对每个对象进行单独的异步调用会更加昂贵。您可以做的就是像现在一样在本地保存到String []。退出此屏幕时,将本地String []同步到外部存储。回到该屏幕时,从外部存储中提取String []
dkarmazi

4
在这种情况下,Varun必须将第二个editText添加到布局中,将其添加到viewHolder中,然后创建第二个MyCustomEditTextListener来侦听第二个editText对象中的更改。最重要的是,您将需要创建一个与mDataset相同的新数据结构,以记住第二个editTexts的值。
dkarmazi 2015年

1
@ quangson91我们不必调用notifyItemChanged,因为它是我们当前在屏幕上看到的EditText视图。该视图将在可见时显示上一次用户输入的内容。现在,当视图离开屏幕然后返回时,mDataset变得很方便。如果我们有常规TextView之类的东西,则必须调用notifyItemChanged来更新视图的当前显示值。
dkarmazi'3

3
更新:应使用holder.getAdapterPosition()而不是position:holder.myCustomEditTextListener.updatePosition(position);
忍者编码

2
此解决方案有一个错误。一个人永远不应该存储传递到的位置信息,onBindViewHolder因为在不进行另一次onBindViewHolder调用的情况下,商品的位置可能会发生变化。相反,MyCustomEditTextListener应该引用ViewHolderViewHolder.getAdapterPosition()以获得正确的位置。
安德烈·马卡罗夫

13

我遇到了同样的问题,我添加了以下内容,似乎已经解决了这一问题。

mRecyclerview.setItemViewCacheSize(mDataset.size());

希望这能解决您的问题。


26
这将起作用,但会破坏RecyclerView中“回收视图”的整个目的。
SMBiggs '16

8
非常适合少量数据
Mohsin

真的很有帮助!!经过这么多的努力,终于有了这个答案。(y)
Stuti Kasliwal '18

4

创建一个具有适配器数据大小的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
            }
        });


}

1
@JITHINRAJ,您好,没有这个“ addtextChangedListener”,我的代码在滚动时不保存编辑文本的内容就可以正常工作。如果添加此代码,则当我滚动离开屏幕并返回的所有editText视图时,会弄乱内容。
gatteo,2015年

4

我实现了@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中之后,只有一个问题,列表再次制动。我不知道为什么,也许我也会找到这个问题的解决方案


到目前为止最好的答案,这应该是公认的答案。最受欢迎的答案有性能缺陷,除此之外
Aklesh Singh

3

我将创建一个接口并传递当前适配器的位置以处理文本更改事件

    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);
                }
            });
        }
    }

}

所有答案中最好最干净的。那肯定是一个要走的路
纳斯·恩约卡(Nasska Sr.)

2

嗨,@ 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) {

        }
    });


}

2

像这样在RecyclerView适配器中覆盖onViewRecycled方法:

@Override
public void onViewRecycled(@NonNull ViewHolder holder) {
    mDataSet[holder.getAdapterPosition()] = holder.mEditText.getText().toString();
}

holder.getAdapterPosition()在TextWatcher内部使用是最好的解决方案-则不需要跟踪位置,因为回收站正在为您完成此工作。只要确保在视图持有者构造函数中仅添加一个TextWatcher。
zkon

@zkon我的解决方案不需要TextWatcher。它只是重写RecyclerView.Adapter类的onViewRecycled方法。
Pourqavam

@Pourqavam的问题,那就是你可以编辑文本,然后按保存按钮(在appBar)和onViewRecycled()不会被调用,所以你不会皮卡的变化。使用,TextWatcher您将始终收到有关更改的通知。
zkon

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);
            }
        }

    }

1

对我来说,上述解决方案没有用。对于其中一些而言,侦听器未在调用,并且在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;
                }
            });

对我有用的代码。


谢谢你,帮我
MAQ

0

我对RecyclerView对象不是很熟悉,但是ListView也有同样的问题。对于这些,我通常创建一个临时类,表示插入到视图中的值(它与EditTexts,Checkboxes,RadioButtons ...一起使用)并通过它们获取更新的数据。然后,我创建一个由所述容器对象组成的自定义ArrayAdapter,从它们的每个getView()回调中检索要放入edittext中的值,并实现一个textwatcher来使这些对象保持最新。同样,我不完全记得RecyclerViews是如何工作的,但是如果它们涉及适配器,那么这可能是一个不错的技巧供您尝试。


0

我重写了getItemViewType为我解决的方法

override fun getItemViewType(position: Int): Int {
    return position
}

1
不幸的是,这将为每个元素创建许多视图持有者,并且它们将永远不会被重用。实际上,性能与在内部添加新元素完全相同LinearLayout
ruX


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());
}
By using our site, you acknowledge that you have read and understand our Cookie Policy and Privacy Policy.
Licensed under cc by-sa 3.0 with attribution required.