在 RecyclerView 中使用 EditText 嵌套开发,是开发中经常遇到的,由于 RecyclerView 的回收复用机制导致 EditText 出现数据混乱 及 EditText TextWatcher 监听混乱等问题。
1、当需要以列表样式管理某些数据时,可能需要列表项的某个字段可编辑
2、编辑 Item 上的某个字段后可能还要更新相关字段的值
1、列表滑动导致输入框中的数据错位(或者焦点错位)
2、无法更新 Item 上相关的字段项的值
3、监听输入框文本(TextWatcher)更改时陷入死循环
1、使用 RecyclerView+TextWatcher 而不是 ListView 的原因?
因为 RecyclerView 在滑动的时候会使 EditText 失去焦点,这样可以触发 OnFocusChangeListener,可以更准确的绑定和解绑 TxtWatcher;而 ListView 在滑动的时候不会使 EditText 失去焦点,导致滑动时输入框焦点错位。
2、为什么要解绑 TxtWatcher?
因为在 RecyclerView 刷新的时候会重复触发 TextWatcher 导致很多次无用的回调(甚至死循环)。
这里附上 RecyclerView.Adapter 中的代码。
RecyclerView.Adapter 基类(通用):
package com.example.risen.adapter;
import android.support.annotation.NonNull;
import android.support.v7.widget.RecyclerView;
import android.view.View;
import java.util.List;
public abstract class BaseQuickAdapter extends RecyclerView.Adapter {
public OnItemClickListener mOnItemClickListener;
public OnItemLongClickListener mOnItemLongClickListener;
protected List mData;
@Override
public void onBindViewHolder(@NonNull final K k, int position) {
// 如果设置了回调,则设置点击事件
if (mOnItemClickListener != null) {
k.itemView.setOnClickListener(new View.OnClickListener() {
@Override
public void onClick(View v) {
int pos = k.getLayoutPosition();
mOnItemClickListener.onItemClick(k.itemView, pos);
}
});
}
if (mOnItemLongClickListener != null) {
k.itemView.setOnLongClickListener(new View.OnLongClickListener() {
@Override
public boolean onLongClick(View v) {
int pos = k.getLayoutPosition();
mOnItemLongClickListener.onItemLongClick(k.itemView, pos);
return false;
}
});
}
}
@Override
public int getItemCount() {
return mData == null ? 0 : mData.size();
}
/**
* item点击事件
* @param mOnItemClickListener 点击监听
*/
public void setOnItemClickLitener(OnItemClickListener mOnItemClickListener) {
this.mOnItemClickListener = mOnItemClickListener;
}
/**
* item长按事件
* @param mOnItemLongClickListener 长按监听
*/
public void setOnItemLongClickLitener(OnItemLongClickListener mOnItemLongClickListener) {
this.mOnItemLongClickListener = mOnItemLongClickListener;
}
public static interface OnItemClickListener {
void onItemClick(View view, int position);
}
public static interface OnItemLongClickListener {
void onItemLongClick(View view, int position);
}
}
Adapter 类
package com.example.risen.adapter;
import android.content.Context;
import android.support.annotation.NonNull;
import android.support.v4.content.ContextCompat;
import android.support.v7.widget.RecyclerView;
import android.text.Editable;
import android.text.TextUtils;
import android.text.TextWatcher;
import android.util.Log;
import android.view.LayoutInflater;
import android.view.View;
import android.view.ViewGroup;
import android.widget.CheckBox;
import android.widget.EditText;
import android.widget.TextView;
import com.example.risen.bean2.FormDetailBean2;
import com.example.risen.readsignsystem.R;
import com.example.risen.readsignsystem.activity.ChooseDialogActivity;
import java.util.List;
public class ChooseOpinion1Adapter extends BaseQuickAdapter {
private Context context;
public ChooseOpinion1Adapter(Context context, List dataList){
this.context = context;
mData = dataList;
}
public List getData(){
return mData;
}
private void setSelected(){
if (mData != null){
for (int i = 0; i < mData.size(); i++){
mData.get(i).setSelected(false);
mData.get(i).setSaveOpinion(false);
mData.get(i).setEditContent(null);
}
}
}
@NonNull
@Override
public ChooseOpinion1Adapter.ViewHolder onCreateViewHolder(@NonNull ViewGroup viewGroup, int position) {
View view = LayoutInflater.from(context).inflate(R.layout.item_choose_opinion_1, viewGroup, false);
return new ViewHolder(view);
}
@Override
public void onBindViewHolder(@NonNull final ViewHolder viewHolder, final int position) {
super.onBindViewHolder(viewHolder, position);
final FormDetailBean2.DataBean.OaCommListBean bean = mData.get(position);
if (!TextUtils.isEmpty(bean.getEditContent())){
viewHolder.ed_content.setText(bean.getEditContent());
} else {
viewHolder.ed_content.setText(bean.getOaopinionsDisplay());
}
viewHolder.tv_content.setText(bean.getOaopinionsDisplay());
viewHolder.cb_retain.setChecked(bean.isSaveOpinion());
setDataToView(viewHolder, position, bean);
viewHolder.itemView.setTag(bean);
viewHolder.itemView.setOnClickListener(new View.OnClickListener() {
@Override
public void onClick(View v) {
Log.e("ChooseOpinionAdapter===", position + "");
if (!bean.isSelected()){
setSelected();
bean.setSelected(true);
setDataToView(viewHolder, position, bean);
if (context instanceof ChooseDialogActivity) {
((ChooseDialogActivity) context).setTextToTvInfo();
}
notifyDataSetChanged();
}
}
});
//CheckBox :使用 setOnClickListener 代替 setOnCheckedChangeListener,原因看下面的代码
viewHolder.cb_retain.setOnClickListener(new View.OnClickListener() {
@Override
public void onClick(View v) {
bean.setSaveOpinion(!bean.isSaveOpinion());
}
});
//滚动监听重复
//在 RecyclerView 中 CheckBox 不要使用 setOnCheckedChangeListener 进行监听,因为 RecyclerView 的重复使用机制 致使 setOnCheckedChangeListener 滚动时监听重复
// viewHolder.cb_retain.setOnCheckedChangeListener(new CompoundButton.OnCheckedChangeListener() {
// @Override
// public void onCheckedChanged(CompoundButton buttonView, boolean isChecked) {
// if (bean.isSelected()){
// bean.setSaveOpinion(isChecked);
// Log.e("viewHolder.cb_retain", isChecked + "");
// }
// }
// });
if (bean.isSelected()){
viewHolder.mTxtWatcher.buildWatcher(position, bean);
}
/**
* RecyclerView 在滑动的时候会使EditText失去焦点,这样可以触发OnFocusChangeListener,
* 这样可以更准确的绑定和解绑TxtWatcher
*/
viewHolder.ed_content.setOnFocusChangeListener(new View.OnFocusChangeListener() {
@Override
public void onFocusChange(View v, boolean hasFocus) {
if(hasFocus){
viewHolder.ed_content.addTextChangedListener(viewHolder.mTxtWatcher);
}else{
viewHolder.ed_content.removeTextChangedListener(viewHolder.mTxtWatcher);
}
}
});
}
// @Override
// public void onViewRecycled(@NonNull ViewHolder holder) {
// super.onViewRecycled(holder);
// //当前holder被销毁时,把holder的TextChangedListener删除
// ViewHolder viewHolder = holder;
// viewHolder.ed_content.removeTextChangedListener(textWatcher);
// }
private void setDataToView(@NonNull ViewHolder viewHolder, int position, FormDetailBean2.DataBean.OaCommListBean bean) {
Log.d("ChooseDialogActivity==", "position");
if (bean.isSelected()){
viewHolder.ed_content.setEnabled(true);
viewHolder.ed_content.setVisibility(View.VISIBLE);
viewHolder.tv_content.setVisibility(View.GONE);
viewHolder.ed_content.setBackground(ContextCompat.getDrawable(context, R.drawable.choose_bac_edit));
viewHolder.cb_retain.setVisibility(View.VISIBLE);
viewHolder.itemView.setBackground(ContextCompat.getDrawable(context, R.drawable.choose_bac_selected));
}else{
viewHolder.ed_content.setEnabled(false);
viewHolder.ed_content.setVisibility(View.GONE);
viewHolder.tv_content.setVisibility(View.VISIBLE);
viewHolder.ed_content.setBackgroundColor(ContextCompat.getColor(context, R.color.transparent));
viewHolder.cb_retain.setVisibility(View.GONE);
viewHolder.itemView.setBackground(ContextCompat.getDrawable(context, R.drawable.choose_bac_normal));
}
}
/**
* RecyclerView 在滑动的时候会使EditText失去焦点,这样可以触发OnFocusChangeListener,
* 这样可以更准确的绑定和解绑TxtWatcher。为什么要解绑TxtWatcher?
* 因为在RecyclerView刷新的时候会重复触发TextWatcher导致很多次无用的回调(甚至死循环)。
*/
public class TxtWatcher implements TextWatcher{
private int mPosition;
private FormDetailBean2.DataBean.OaCommListBean bean;
public void buildWatcher(int position, FormDetailBean2.DataBean.OaCommListBean bean){
this.mPosition = position;
this.bean = bean;
}
@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) {
if(s.length() > 0){
bean.setEditContent(s.toString());
if (context instanceof ChooseDialogActivity) {
((ChooseDialogActivity) context).setTextToTvInfo();
}
}
}
}
public class ViewHolder extends RecyclerView.ViewHolder {
private EditText ed_content;
private CheckBox cb_retain;
private TextView tv_content;
private TxtWatcher mTxtWatcher;
public ViewHolder(@NonNull View itemView) {
super(itemView);
ed_content = itemView.findViewById(R.id.ed_content);
cb_retain = itemView.findViewById(R.id.cb_retain);
tv_content = itemView.findViewById(R.id.tv_content);
mTxtWatcher = new TxtWatcher();
}
}
}
参考资料
Android RecyclerView嵌套EditView实时更新Item数据