Android — RecyclerView中嵌套EditView

  • Post author:
  • Post category:其他




一、前言

在 RecyclerView 中使用 EditText 嵌套开发,是开发中经常遇到的,由于 RecyclerView 的回收复用机制导致 EditText 出现数据混乱 及 EditText TextWatcher 监听混乱等问题。



二、 RecyclerView 中嵌套 EditText



2.1 场景(例如:购物车)

1、当需要以列表样式管理某些数据时,可能需要列表项的某个字段可编辑

2、编辑 Item 上的某个字段后可能还要更新相关字段的值



2.2 可能遇到的问题

1、列表滑动导致输入框中的数据错位(或者焦点错位)

2、无法更新 Item 上相关的字段项的值

3、监听输入框文本(TextWatcher)更改时陷入死循环



2.3 可行方案(RecyclerView+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<T,K extends RecyclerView.ViewHolder> extends RecyclerView.Adapter<K> {

    public OnItemClickListener mOnItemClickListener;
    public OnItemLongClickListener mOnItemLongClickListener;
    protected List<T> 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<FormDetailBean2.DataBean.OaCommListBean, ChooseOpinion1Adapter.ViewHolder> {
    private Context context;

    public ChooseOpinion1Adapter(Context context, List<FormDetailBean2.DataBean.OaCommListBean> dataList){
        this.context = context;
        mData = dataList;
    }

    public List<FormDetailBean2.DataBean.OaCommListBean> 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数据



版权声明:本文为qq_24382363原创文章,遵循 CC 4.0 BY-SA 版权协议,转载请附上原文出处链接和本声明。