滚动出屏幕后如何从回收视图中的视图中获取值

How to get values from views in a recyclerview after they are scrolled out of screen

我有一个包含 RecyclerView 的片段。这个 RecyclerView 将包含几种不同类型的视图(EditText、Seekbar、CheckBox 等),它将作为一种表单工作。我还有一个执行保存的按钮,它应该转到 RecyclerView 的每一行并获取用户输入的值。

我还有一个适配器,它根据我想在 RecyclerView 上添加的视图类型来扩展不同的布局。 在这个适配器中,我有一个 "getValuesFromViews" 方法,它根据视图类型读取用户输入。

public String getValuesFromViews(int position,ViewHolder vHolder){
 //...
}

在我的片段中,在 For 循环期间,在使用 findViewHolderForAdapterPosition

获取 ViewHolder 后,我调用了此方法

我的问题是,当我滚动我的 RecyclerView 时,我丢失了屏幕上未显示的位置的 ViewHolder,因此 findViewHolderForAdapterPosition returns 为空。

如何从屏幕上未显示的视图中读取值?

编辑 这是我目前的适配器(仍在处理中)。

public class ParamsAdapter extends RecyclerView.Adapter<ParamsAdapter.ViewHolder> {
    private final android.support.v4.app.FragmentManager fragmentManager;
    private final Fragment fragment;
    private Context context;
    private View view1;


    ParamsAdapter.ViewHolder viewHolder1;
    List<UserConfigurableParametersResponseMainItem1> listParams;

    public ParamsAdapter(Context context1, List<UserConfigurableParametersResponseMainItem1> list, android.support.v4.app.FragmentManager fmanager, Fragment frag){
        context = context1;
        listParams = list;
        fragmentManager = fmanager;
        fragment = frag;
    }

    public List<UserConfigurableParametersResponseMainItem1> getListParams() {
        return listParams;
    }

    public class ViewHolder extends RecyclerView.ViewHolder{

        @BindView(R.id.tv_param_title)
        TextViewMPOS tvParamTitle;
        @BindView(R.id.iv_question)
        ImageView ivQuestion;
        @Nullable @BindView(R.id.et_param)
        View param;
        @BindView(R.id.param_loader)
        AVLoadingIndicatorView loader;
        @BindView(R.id.iv_status)
        ImageView iv_status;
        @BindView(R.id.iv_error)
        ImageView iv_error;
        @BindView(R.id.tv_param_error)
        TextViewMPOS tvError;
        @BindView(R.id.iv_reset)
        ImageView ivReset;

        public ViewHolder(View v){
            super(v);
            ButterKnife.bind(this,v);

        }
    }

    @Override
    public int getItemViewType(int position) {
        return position;
    }

    @Override
    public ParamsAdapter.ViewHolder onCreateViewHolder(ViewGroup parent, int viewType){

        view1 = createViews(parent,viewType);
        viewHolder1 = new ParamsAdapter.ViewHolder(view1);

        return viewHolder1;
    }

    private View createViews(ViewGroup parent, int viewType) {
        ConfigurableParameterWebControlCode op = listParams.get(viewType).getParamWbCntrlCd();
        switch (op){
            case NUMERO:
                return LayoutInflater.from(context).inflate(R.layout.adapter_param_num,parent,false);
            case DOUBLE:
                return LayoutInflater.from(context).inflate(R.layout.adapter_param_double,parent,false);
            case EMAIL:
                return LayoutInflater.from(context).inflate(R.layout.adapter_param_email,parent,false);
            case TEXTBOX:
                return LayoutInflater.from(context).inflate(R.layout.adapter_param_text,parent,false);
            case TEXAREA:
                return LayoutInflater.from(context).inflate(R.layout.adapter_param_textarea,parent,false);
            case SLIDER:
                return LayoutInflater.from(context).inflate(R.layout.adapter_param_slider,parent,false);
            case CHECKBOX:
                return LayoutInflater.from(context).inflate(R.layout.adapter_param_num,parent,false);
            case DROPDOWN:
                return LayoutInflater.from(context).inflate(R.layout.adapter_param_drop,parent,false);
            case RADIOBUTTON:
                return LayoutInflater.from(context).inflate(R.layout.adapter_param_num,parent,false);
            case DATA:
                return LayoutInflater.from(context).inflate(R.layout.adapter_param_data,parent,false);
            case DATAHORA:
                return LayoutInflater.from(context).inflate(R.layout.adapter_param_datahora,parent,false);
            case TEMPO:
                return LayoutInflater.from(context).inflate(R.layout.adapter_param_tempo,parent,false);
/*            case PHONE:
                return LayoutInflater.from(context).inflate(R.layout.adapter_param_phone,parent,false);*/
            case CHECKGROUP:
                return LayoutInflater.from(context).inflate(R.layout.adapter_param_checkgroup,parent,false);
        }

        return null;
    }

    @Override
    public void onBindViewHolder(final ParamsAdapter.ViewHolder holder, final int position){

        final int index = getLanguageIndex(position);
        holder.tvParamTitle.setText(listParams.get(position).getUsrCnfgrblPrmtrsRspnItms().get(index).getLablTxt());
        holder.ivQuestion.setOnClickListener(new View.OnClickListener() {
            @Override
            public void onClick(View view) {
                if(context != null)
                    MyDialogBuilder.createSingleButtonDialog(context,R.drawable.ic_warningcor,listParams.get(position).getUsrCnfgrblPrmtrsRspnItms().get(index).getHlprTxt(),R.string.ok,null,-1).show();
            }
        });
        holder.ivReset.setOnClickListener(new View.OnClickListener() {
            @Override
            public void onClick(View view) {
                clearView(holder,position);
            }
        });

        holder.iv_error.setOnClickListener(new View.OnClickListener() {
            @Override
            public void onClick(View view) {
                if(context != null)
                    MyDialogBuilder.createSingleButtonDialog(context,R.drawable.ic_errorcor,listParams.get(position).getUsrCnfgrblPrmtrsRspnItms().get(index).getErrTxt(),R.string.ok,null,-1).show();
            }
        });


        switch (listParams.get(position).getParamWbCntrlCd()){

            case NUMERO:
            case DOUBLE:
            case EMAIL:
            case TEXTBOX:
            case TEXAREA:
            case TELEFONE:
                EditTextMPOS editTextAux = (EditTextMPOS)holder.param;
                if (editTextAux != null) {
                    editTextAux.setText(listParams.get(position).getParamVl());
                }
                break;
            case SLIDER:
                AppCompatSeekBar seekbarAux = (AppCompatSeekBar)holder.param;
                seekbarAux.setMax(Integer.parseInt(listParams.get(position).getParamVldtnMax()));
                seekbarAux.setOnSeekBarChangeListener(new SeekBar.OnSeekBarChangeListener() {
                    @Override
                    public void onProgressChanged(SeekBar seekBar, int i, boolean b) {
                        holder.tvParamTitle.setText(listParams.get(position).getUsrCnfgrblPrmtrsRspnItms().get(index).getLablTxt() +"   "+i);
                    }

                    @Override
                    public void onStartTrackingTouch(SeekBar seekBar) {

                    }

                    @Override
                    public void onStopTrackingTouch(SeekBar seekBar) {

                    }
                });
                break;
            case CHECKBOX:
                break;
            case DROPDOWN:
                break;
            case RADIOBUTTON:
                break;

            case DATA:
                if (holder.param != null) {
                    holder.param.setOnClickListener(new View.OnClickListener() {
                        @Override
                        public void onClick(View view) {
                            MyDialogBuilder.DialogDateNoHoursPicker(context,fragmentManager,fragment, DatePickerFragment.DATE_END,"# ",0);
                        }
                    });
                }
                break;
            case DATAHORA:
                if (holder.param != null) {
                    holder.param.setOnClickListener(new View.OnClickListener() {
                        @Override
                        public void onClick(View view) {
                            MyDialogBuilder.DialogDatePicker(context,fragmentManager,fragment, DatePickerFragment.DATE_END,"##",0);
                        }
                    });
                }
                break;
            case TEMPO:
                if (holder.param != null) {
                    holder.param.setOnClickListener(new View.OnClickListener() {
                        @Override
                        public void onClick(View view) {
                            MyDialogBuilder.DialogTimePicker(context,fragmentManager,fragment, DatePickerFragment.DATE_END,"###",0);
                        }
                    });
                }
                break;
            case CHECKGROUP:
                LinearLayout linearLayout = (LinearLayout)holder.param;
                List<String> checkList = new ArrayList<String>(Arrays.asList(listParams.get(position).getParamVldtnRgExp().split(";")));
                for(int i=0;i<checkList.size();i++){
                    CheckBox checkBox = new CheckBox(context);
                    checkBox.setText(checkList.get(i));
                    LinearLayout.LayoutParams newParams = new LinearLayout.LayoutParams(
                            RelativeLayout.LayoutParams.WRAP_CONTENT,
                            RelativeLayout.LayoutParams.WRAP_CONTENT);
                        newParams.weight = 1;
                        checkBox.setLayoutParams(newParams);

                    linearLayout.addView(checkBox,i);
                }
                break;
        }
    }

    private void clearView(ViewHolder holder, int position) {
        switch (listParams.get(position).getParamWbCntrlCd()){
            case NUMERO:
            case DOUBLE:
            case EMAIL:
            case TEXTBOX:
            case TEXAREA:
            case TELEFONE:
                EditTextMPOS auxView = (EditTextMPOS)holder.param;
                if (auxView != null) {
                    auxView.setText("");
                    auxView.setHint(listParams.get(position).getDfltVl());
                }
                listParams.get(position).setUseDefault(true);
                break;
            case CHECKBOX:
                break;
            case DROPDOWN:
                break;
            case RADIOBUTTON:
                break;
            case DATA:
                if (holder.param != null) {
                    holder.param.setOnClickListener(new View.OnClickListener() {
                        @Override
                        public void onClick(View view) {
                            MyDialogBuilder.DialogDateNoHoursPicker(context,fragmentManager,fragment, DatePickerFragment.DATE_END,"# ",0);
                        }
                    });
                }
                break;
            case DATAHORA:
                if (holder.param != null) {
                    holder.param.setOnClickListener(new View.OnClickListener() {
                        @Override
                        public void onClick(View view) {
                            MyDialogBuilder.DialogDatePicker(context,fragmentManager,fragment, DatePickerFragment.DATE_END,"##",0);
                        }
                    });
                }
                break;
            case TEMPO:
                if (holder.param != null) {
                    holder.param.setOnClickListener(new View.OnClickListener() {
                        @Override
                        public void onClick(View view) {
                            MyDialogBuilder.DialogTimePicker(context,fragmentManager,fragment, DatePickerFragment.DATE_END,"###",0);
                        }
                    });
                }
                break;
            case CHECKGROUP:
                break;
        }
    }

    private int getLanguageIndex(int position) {
        int index = 0;
        String lang = HelperSharedPreferences.getSharedPreferencesString(context,HelperSharedPreferences.LANG,"pt-PT");
        for (int i=0; i<listParams.get(position).getUsrCnfgrblPrmtrsRspnItms().size();i++) {
            if(listParams.get(position).getUsrCnfgrblPrmtrsRspnItms().get(i).getLangCd().equals(lang)) {
                index = i;
                break;
            }
        }
        return index;
    }

    public String getValuesFromViews(int position,ViewHolder vHolder){
        String result = new String();
        if(listParams.get(position).isUseDefault()) {
            result = null;
        }
        else {
            switch (listParams.get(position).getParamWbCntrlCd()) {
                case NUMERO:
                case DOUBLE:
                case EMAIL:
                case TEXTBOX:
                case TEXAREA:
                case TELEFONE:
                    EditTextMPOS auxView = (EditTextMPOS) vHolder.param;
                    if (auxView != null) {
                        result = auxView.getText().toString();
                    }
                    break;
                case CHECKBOX:

                    break;
                case CHECKGROUP:
                    LinearLayout linearLayout = (LinearLayout)vHolder.param;
                    for(int i = 0;i<linearLayout.getChildCount();i++){
                        if(((CheckBox)linearLayout.getChildAt(i)).isChecked()){
                            result += ((CheckBox)linearLayout.getChildAt(i)).getText() + ";";
                        }
                    }
                    break;

            }
        }
        Logging.log("RESULT ",result);
        return result;
    }


    public void setStatus(final int position, ViewHolder vHolder,int status){
        if (status == 1) {
            vHolder.iv_status.setVisibility(View.VISIBLE);
            vHolder.iv_error.setVisibility(View.INVISIBLE);
            vHolder.param.setBackgroundResource(R.drawable.edit_text_background);
        } else if (status == 2) {
            vHolder.iv_status.setVisibility(View.INVISIBLE);
            vHolder.iv_error.setVisibility(View.VISIBLE);
            vHolder.param.setBackgroundResource(R.drawable.edit_text_error_background);
        } else {
            vHolder.iv_status.setVisibility(View.INVISIBLE);
            vHolder.iv_error.setVisibility(View.INVISIBLE);
            if (vHolder.param != null) {
                vHolder.param.setBackgroundResource(R.drawable.edit_text_background);
            }
        }
    }


    @Override
    public int getItemCount(){
        if(listParams != null)
            return listParams.size();
        else
            return 0;
    }

    public void update (List<UserConfigurableParametersResponseMainItem1> list){
        this.listParams.clear();
        this.listParams = list;
        setDataChanged();
    }

    public void setDataChanged(){
        new Handler(Looper.getMainLooper()).post(new Runnable() {
            @Override
            public void run() {
                notifyDataSetChanged();
            }
        });
    }
}

我要创建它的数据是这样的数组

    {

    "ParamCd":"xxxxxxx",
    "ParamVl":"xxxxxxx",
    "DfltVl":" ",
    "ParamVldtnRgExp":".*",
    "ParamWbCntrlCd":"EMAIL",   
    "ParamOrdr":54,
    "UsrCnfgrblPrmtrsRspnItms":[
        {
            "LangCd":"pt-PT",
            "LablTxt":"xxxxxxx",
            "HlprTxt":"xxxxxxxxxxxxxx",
            "ErrTxt":"xxxxxxxxxxxxxxxxxxxxx"
        },
        {
            "LangCd":"en-UK",
            "LablTxt":"xxxxxxx",
            "HlprTxt":"xxxxxxxxxxxxxx",
            "ErrTxt":"xxxxxxxxxxxxxxxxxxxxx"
        }
    ]
}

字段 "ParamWbCntrlCd" 定义出现在 RecyclerView 行中的视图类型

在 Android 中,适配器用于创建表示特定数据集的视图。您可以将适配器视为 datadisplay.

之间的一种桥梁

因此,ViewHolder 是您的数据在给定位置的表示。由于视图通常比纯数据占用更多内存,因此 RecyclerView 在滚动时重复使用它们。这就是为什么您无法检索与滚出屏幕的视图关联的数据的原因:那些 ViewHolder 已被回收,并且其视图的先前状态已丢失。

为了解决这个问题,我建议您执行以下步骤:

  1. 创建一个 SparseArray<UserConfigurableParametersResponseMainItem1> 作为适配器的字段。此 SparseArray 将包含已被用户修改的模型对象的副本。
  2. 为其内容可以修改的 ViewHolder 中的每个视图配置侦听器。

对于每个有趣的更改,调用 holder.getAdapterPosition() 以检索此 ViewHolder 在数据集中的当前位置。使用 position 检查 SparseArray 中的此位置是否存在对象。如果不存在,则在 listParams 的相同位置创建原始数据的副本。然后设置此对象的属性以反映用户所做的更改。

这个想法是维护一个 "cache" 用户在适配器中所做的修改。这样,用户所做的修改就不会再丢失了。

  1. onBindViewHolder 中,从 SparseArray 中给定的 position 检索数据。如果是null,则没有修改发出,可以绑定listParams的数据。否则,用户已经进行了修改,您必须绑定那些而不是原来的。

  2. 创建方法UserConfigurableParametersResponseMainItem1 getModificationsForPosition(int position)。此方法直接映射到 SparseArray 中包含的数据。这样,如果返回值不是 null.

  3. ,调用 Fragment 会直接知道发生修改
  4. 当点击 "Save" 按钮时,使用 for-loop 为适配器的每个有效位置调用 getModificationsForPosition(i),检索所有修改的对象,然后将它们保存在任何位置你想要(SQLite 或远程服务器)。

  5. 根据保存的更改刷新适配器的数据集。例如,您可以将 listParams 中的数据替换为稀疏数组中的数据。单击 "Save" 按钮时不要忘记清除稀疏数组。

这种构造具有以下优点:

  • 即使在回收 ViewHolder 后,对视图的修改也会保留。
  • 可以为每个位置单独丢弃修改。
  • 只保存修改过的数据,效率更高

希望对您有所帮助!