最近在做毕业设计,使用了RecyclerView来做了以下个人资料的编辑界面。RecyclerView中包含了多种不同的Item,例如图片里面的 姓名,公司,部门,职位,邮箱的Item 里面都包含一个EditText。
RecyclerView中EditText的监听处理
这个界面的主要逻辑是监听每个EditText的输入,然后保存到个人对象里,最后保存到服务器上。RecyclerView中Item里EditText的监听可以按如下代码实现:
首先定义个回调接口:
public interface onTextChangeListener {
void onTextChanged(int pos,String str);
}
在RecyclerView的自定义Adapter中添加一个如上自定义的接口成员变量mTextListener,然后在onBindViewHolder里面来添加Item中EditText的输入监听,即EditText.addTextChangedListener(),然后在监听事件回调里的afterTextChanged 方法中 调用 mTextListener.onTextChanged 来达到接口回调的目的。
public class EditListAdapter extends RecyclerView.Adapter<RecyclerView.ViewHolder>{
private Context mContext;
private LayoutInflater mInflater;
private ArrayList<EditListBean> mList;
private onTextChangeListener mTextListener;
public EditListAdapter(Context context, ArrayList<EditListBean> list){
this.mContext=context;
mInflater=LayoutInflater.from(mContext);
this.mList=list;
}
//设置自定义接口成员变量
public void setOnTextChangeListener(onTextChangeListener onTextChangeListener){
this.mTextListener=onTextChangeListener;
}
//创建ViewHolder
@Override
public RecyclerView.ViewHolder onCreateViewHolder(ViewGroup parent, int viewType) {
RecyclerView.ViewHolder holder=new
itemHolder2(mInflater.inflate(R.layout.edit_recyclerview_item2,parent,false));
return holder;
}
//绑定ViewHolder
@Override
public void onBindViewHolder(final RecyclerView.ViewHolder holder, final int
position) {
final itemHolder2 holder2= (itemHolder2) holder;
holder2.title.setText(mList.get(position).getKey());
holder2.input.setText((String) mList.get(position).getValue());
holder2.input.setHint(mList.get(position).getHint());
//添加EditText的监听事件
holder2.input.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) {
}
@Override
public void afterTextChanged(Editable s) {
//通过接口回调将数据传递到Activity中
mTextListener.onTextChanged(position,holder2.input.getText().toString());
}
});
//内部类 ViewHolder
class itemHolder2 extends RecyclerView.ViewHolder{
TextView title;
EditText input;
public itemHolder2(View itemView) {
super(itemView);
title= (TextView) itemView.findViewById(R.id.EditActivity_item2_title);
input= (EditText) itemView.findViewById(R.id.EditActivity_item2_text);
}
}
@Override
public int getItemCount() {
return mList.size();
}
@Override
public int getItemViewType(int position){
return mList.get(position).getType();
}
}
Activity中的代码实现,在mAdapter中添加匿名内部类来监听EditText中的接口回调数据:
mAdapter.setOnTextChangeListener(new onTextChangeListener() {
@Override
public void onTextChanged(int pos, String str) {
//在这里拿到RecyclerView中Item的position和EditText中的变化
mPedit.setList(pos,str);
Log.e(String.format("%d is ---->",pos),str);
}
});
OK 上面的监听事件处理完了 我们来试试效果怎么样。首先点击姓名里面的EditText,在马化腾后面输入哈哈哈哈 然后收起键盘, 可以发现竟然下面几个Item的EditText也触发了回调事件了,也就是我编辑了Item2的EditText,然后Item7 , 9 , 11的EditText也触发了onTextChanged的监听事件了。如图,出现了监听事件错乱的重大的Bug。
Recycler中 多Item中EditText焦点混乱的网上方法
2017年12月11号更新下,现在很多网上的解决方法是使用EditText的TAG来解决问题,EditText中有setTag和getTag可以储存Object对象作为标签,然后把textWatcher作为标签设置TAG来作为标记表明这个item的editText是否已经设置textChange监听了。在Adapter的onBindViewHolder中每次都先判断editText的Tag是否有textWatcher对象,有的话就调用removeTextChangedListener来移除由于视图复用之前绑定的textWatcher,然后就设置editText的内容setText,最后再给editText设置监听器addTextChangedListener,并把这个textWatcher加入到editText的TAG标签中。总结来说就是在适配器里先移除被复用事件,再添加新事件。
例如这种方式:http://m.blog.csdn.net/jhear/article/details/75191997
//避免复用
EditText editText = helper.getView(R.id.et_comment);
if (editText.getTag() instanceof TextWatcher) {
editText.removeTextChangedListener((TextWatcher) editText.getTag());
}
helper.setText(R.id.et_comment, item.content);
TextWatcher watcher = 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) {
}
@Override
public void afterTextChanged(Editable s) {
item.content = s.length() > 0 ? s.toString() : "";
}
};
editText.addTextChangedListener(watcher);
editText.setTag(watcher);
这种方式并不优雅,而且还要进行类型判断和强制类型转型,对性能有一定的开销,下面来介绍真正的优雅解决EditText焦点混乱的解决方法:
## Recycler中 多Item中EditText焦点混乱,数据错乱的正确优雅的解决方法 !!##
首先要知道EditText焦点错乱的问题就是RecyclerView的视图回收与复用问题,例如在位置0有item1视图中的editText已经绑定了textWatcher1监听器,但是又没有移除监听器,在位置6时又复用了item1视图,editText又再次绑定了textWatcher2监听器,此时item1视图中的editText已经绑定了2个textWatcher监听器了,当在位置6进行输入数据时,必定会导致位置0的数据变化了,令位置0的数据跟位置6的数据一模一样,也就是数据错乱了。原因是editText中的addTextChangedListener是可以绑定多个监听器的,来看看它的实现:
public void addTextChangedListener(TextWatcher watcher) {
if (mListeners == null) {
mListeners = new ArrayList<TextWatcher>();
}
mListeners.add(watcher);
}
可以看到mListeners 是一个ArrayList数组,当多次调用addTextChangedListener时,一个editText可以绑定多个textWatcher监听器,所以要记住在每次调用addTextChangedListener完之后必须调用removeTextChangedListener来接触绑定。
那调用这两个方法的时间节点是在哪里的呢?首先我们要知道如果一个editText要获取输入值那它就首先必须要获取到焦点了,没错,答案就是设置setOnFocusChangeListener监听器来判断焦点的变化从而设置addTextChangedListener和removeTextChangedListener。
代码如下:
final itemHolder2 holder2= (itemHolder2) holder;
//holder2.input为EditTextView
holder2.input.setText((String) mList.get(position).getValue());
holder2.input.setHint(mList.get(position).getHint());
final TextWatcher textWatcher=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) {
}
@Override
public void afterTextChanged(Editable s) {
Log.e("textWatcher",holder.getAdapterPosition()+"");
if(holder2.input.hasFocus()){//判断当前EditText是否有焦点在
//通过接口回调将数据传递到Activity中
mTextListener.onTextChanged(position,holder2.input.getText().toString());
}
}
};
//设置EditText的焦点监听器判断焦点变化,当有焦点时addTextChangedListener,失去焦点时removeTextChangedListener
holder2.input.setOnFocusChangeListener(new View.OnFocusChangeListener() {
@Override
public void onFocusChange(View v, boolean hasFocus) {
if(hasFocus){
holder2.input.addTextChangedListener(textWatcher);
Log.e("addTextChanged",position+"");
}else{
holder2.input.removeTextChangedListener(textWatcher);
Log.e("removeTextChanged",position+"");
}
}
});
通过设置setOnFocusChangeListener来监听焦点就能完美的解决了RecyclerView中EditText数据监听textWatcher导致的数据错乱问题了。目前来说是最优雅的一种方式了,也不需要用到Tag了。